PageSourceSearch

https://sunnypork.com/

html sunnypork.com collected 2026-09-30 13:39:10 UTC 866,146 bytes, 25,096 lines download raw bytes

1
2
3
4<!DOCTYPE html>
5<html lang="id">
6<head>
7  <meta charset="UTF-8">
8  <title>NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif</title>
9  <meta name="description" content="Perubahan Teknologi Digital pada NAGARP X Sunny Pork membentuk pengalaman platform interaktif yang lebih responsif. Pengguna dapat merasakan evolusi layanan digital yang dinamis dan sesuai kebutuhan modern.">
10  <meta name="robots" content="index,follow">
11  <link rel="canonical" href="https://sunnypork.com/">
12  <link rel="amphtml" href="https://sunny-401.sunnypork.com/">
13  <link rel="alternate" href="https://sunny-401.sunnypork.com/" hreflang="id">
14  <link rel="alternate" href="https://sunny-401.sunnypork.com/" hreflang="en">
15  <link rel="alternate" href="https://sunny-401.sunnypork.com/" hreflang="x-default">
16  <link rel="alternate" hreflang="id-ID" href="https://sunny-401.sunnypork.com/" />
17  <link rel="alternate" hreflang="en-US" href="https://sunny-401.sunnypork.com/" />
18  <link rel="alternate" hreflang="en-SG" href="https://sunny-401.sunnypork.com/" />
19    <link rel="preconnect" href="https://www.googletagmanager.com" crossorigin>
vendor: 67 bytes, lines 19-20
19
20  <script async src="https://www.googletagmanager.com/gtag/js?id=
20G-0LSM1YJSPZ
vendor: 48 bytes, lines 20-21
20" nonce="se3iyzEHMc9FQEB4wSyd0w=="></script>
21  
21<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>
21
22  <meta property="og:locale" content="id_ID">
23  <meta property="og:site_name" content="NAGARP">
24  <meta property="og:type" content="product">
25  <meta property="og:title" content="NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif">
26  <meta property="og:description" content="Perubahan Teknologi Digital pada NAGARP X Sunny Pork membentuk pengalaman platform interaktif yang lebih responsif. Pengguna dapat merasakan evolusi layanan digital yang dinamis dan sesuai kebutuhan modern.">
27  <meta property="og:url" content="https://sunnypork.com/">
28  <meta property="og:image" content="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg">
29  <meta property="og:image:alt" content="NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif">
30<meta property="product:price:amount" content="5000">
31<meta property="product:price:currency" content="IDR">
32<meta property="product:availability" content="out of stock">
33<meta name="twitter:card" content="summary_large_image">
34<meta name="twitter:title" content="NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif">
35<meta name="twitter:description" content="Perubahan Teknologi Digital pada NAGARP X Sunny Pork membentuk pengalaman platform interaktif yang lebih responsif. Pengguna dapat merasakan evolusi layanan digital yang dinamis dan sesuai kebutuhan modern.">
36<meta name="twitter:image" content="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg">
37
38<script type="application/ld+json">
39      {
40        "@context": "http:\/\/schema.org",
41        "@type": "BreadcrumbList",
42        "itemListElement": [
43          {
44            "@type": "ListItem",
45            "item": {
46              "@id": "https://sunnypork.com/",
47              "name": "NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif"
48            },
49            "position": 1
50          }
51        ]
52      }
53    </script>
53
54    
54<script type="application/ld+json">
55      {
56        "@context": "http:\/\/schema.org",
57        "@type": "Product",
58        "name": "NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif",
59        "description": "Perubahan Teknologi Digital pada NAGARP X Sunny Pork membentuk pengalaman platform interaktif yang lebih responsif. Pengguna dapat merasakan evolusi layanan digital yang dinamis dan sesuai kebutuhan modern.",
60        "image": "https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg",
61        "offers": {
62          "@type": "http:\/\/schema.org\/Offer",
63          "price": 9999,
64          "url": "https://sunnypork.com/",
65          "priceCurrency": "IDR",
66          "availability": "http:\/\/schema.org\/InStock"
67        }
68      }
69    </script>
69
70    
70<script type="application/ld+json">
71      {
72        "@context": "http:\/\/schema.org\/",
73        "@type": "WebPage",
74        "speakable": {
75          "@type": "SpeakableSpecification",
76          "cssSelector": [".description"],
77          "xpath": ["\/html\/head\/title"]
78        }
79      }
80    </script>
80
81    <!-- ZS -->
82    <!-- Schema JSON untuk SBO -->
83<script type="application/ld+json">
84{
85"@context": "https://schema.org/",
86"@type": "Product",
87"name": "NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif",
88"url": "https://sunnypork.com/",
89"sku": "NAGARP SITUS",
90"image": [
91  "https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg"
92],
93"description": "Perubahan Teknologi Digital pada NAGARP X Sunny Pork membentuk pengalaman platform interaktif yang lebih responsif. Pengguna dapat merasakan evolusi layanan digital yang dinamis dan sesuai kebutuhan modern.",
94"brand": {
95  "@type": "Organization",
96  "name": "NAGARP ONLINE"
97},
98"offers": {
99  "@type": "Offer",
100  "name": "NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif",
101  "sku": "NAGARP ONLINE",
102  "price": "9999",
103  "priceCurrency": "IDR",
104  "availability": "https://schema.org/InStock"
105}
106}
107</script>
107
108
109<script type="application/ld+json">
110{
111"@context": "https://schema.org/",
112"@type": "Product",
113"name": "NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif",
114"image": "https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg",
115"description": "Perubahan Teknologi Digital pada NAGARP X Sunny Pork membentuk pengalaman platform interaktif yang lebih responsif. Pengguna dapat merasakan evolusi layanan digital yang dinamis dan sesuai kebutuhan modern.",
116"sku": "NAGARP ONLINE",
117"offers": {
118  "@type": "Offer",
119  "priceCurrency": "IDR",
120  "url": "https://sunnypork.com/",
121  "availability": "https://schema.org/InStock",
122  "price": "9999.00"
123}
124}
125</script>
125
126
127<script type="application/ld+json" id="bv-jsonld-bvloader-summary">
128{
129"@context": "https://schema.org/",
130"@id": "https://sunnypork.com/",
131"aggregateRating": {
132  "@type": "AggregateRating",
133  "itemReviewed": {
134    "@type": "IndividualProduct",
135    "name": "NAGARP SITUS"
136  },
137  "ratingValue": "5.0",
138  "reviewCount": 954638,
139  "bestRating": 5
140}
141}
142</script>
142
143
144<script type="application/ld+json">
145{
146"@context": "https://schema.org",
147"@type": "BreadcrumbList",
148"itemListElement": [
149  {
150    "@type": "ListItem",
151    "position": 1,
152    "item": {
153      "@id": "https://sunnypork.com/",
154      "name": "NAGARP SITUS"
155    }
156  },
157  {
158    "@type": "ListItem",
159    "position": 2,
160    "item": {
161      "@id": "https://sunnypork.com/",
162      "name": "NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif"
163    }
164  },
165  {
166    "@type": "ListItem",
167    "position": 3,
168    "item": {
169      "@id": "https://sunnypork.com/",
170      "name": "NAGARP ONLINE"
171    }
172  },
173  {
174    "@type": "ListItem",
175    "position": 4,
176    "item": {
177      "@id": "https://sunnypork.com/",
178      "name": "NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif"
179    }
180  }
181]
182}
183</script>
183
184
185<script type="application/ld+json" id="bv-jsonld-reviews-data">
186{
187  "@context": "https://schema.org",
188  "@type": "Organization",
189  "@id": "https://sunnypork.com/",
190  "name": "NAGARP SITUS ONLINE",
191  "alternateName": "NAGARP SITUS ONLINE",
192  "url": "https://sunnypork.com/",
193  "review": [
194    {
195      "@type": "Review",
196      "dateCreated": "2026-03-31T12:28:35+00:00",
197      "datePublished": "2026-03-31T12:28:35+00:00",
198      "headline": "NAGARP ONLINE",
199      "reviewBody": "NAGARP X Sunny Pork berhasil menghadirkan nuansa baru lewat integrasi teknologi digital yang membuat platform interaktif terasa lebih hidup dan mudah digunakan.",
200      "reviewRating": {
201        "@type": "Rating",
202        "ratingValue": 5,
203        "bestRating": 5,
204        "worstRating": 1
205      },
206      "author": {
207        "@type": "Person",
208        "name": "Perubahan Teknologi Digital – Surabaya"
209      }
210    },
211    {
212      "@type": "Review",
213      "dateCreated": "2026-04--02T18:28:21+00:00",
214      "datePublished": "2026-04-03T18:28:21+00:00",
215      "headline": "NAGARP ONLINE",
216      "reviewBody": "Saya cukup impressed dengan cara NAGARP X Sunny Pork membentuk pengalaman platform interaktif menggunakan teknologi digital terkini tanpa ribet.",
217      "reviewRating": {
218        "@type": "Rating",
219        "ratingValue": 5,
220        "bestRating": 5,
221        "worstRating": 1
222      },
223      "author": {
224        "@type": "Person",
225        "name": "Pengalaman Platform Interaktif – Semarang"
226      }
227    },
228    {
229      "@type": "Review",
230      "dateCreated": "2026-04-05T19:28:18+00:00",
231      "datePublished": "2026-04-05T19:28:18+00:00",
232      "headline": "NAGARP ONLINE",
233      "reviewBody": "Platform NAGARP X Sunny Pork terasa modern berkat perubahan teknologi digital yang diterapkan secara konsisten dan user-friendly.",
234      "reviewRating": {
235        "@type": "Rating",
236        "ratingValue": 5,
237        "bestRating": 5,
238        "worstRating": 1
239      },
240      "author": {
241        "@type": "Person",
242        "name": "Teknologi Digital – Makassar"
243      }
244    }
245  ]
246}
247</script>
vendor: 2 bytes, line 247
247
248<script type="application/ld+json">
249{
250  "@context": "https://schema.org",
251  "@type": "FAQPage",
252  "mainEntity": [
253    {
254  "@type": "Question",
255  "name": "Apa dampak perubahan teknologi digital pada platform interaktif NAGARP X Sunny Pork?",
256  "acceptedAnswer": {
257    "@type": "Answer",
258    "text": "Perubahan teknologi digital membantu NAGARP X Sunny Pork menciptakan pengalaman platform interaktif yang lebih responsif dan engaging bagi pengguna."
259  }
260},
261{
262  "@type": "Question",
263  "name": "Bagaimana NAGARP X Sunny Pork memanfaatkan teknologi digital untuk platformnya?",
264  "acceptedAnswer": {
265    "@type": "Answer",
266    "text": "NAGARP X Sunny Pork mengintegrasikan teknologi digital terkini untuk membentuk pengalaman platform interaktif yang dinamis dan mudah diakses."
267  }
268},
269{
270  "@type": "Question",
271  "name": "Apakah NAGARP X Sunny Pork menawarkan fitur interaktif berbasis teknologi digital?",
272  "acceptedAnswer": {
273    "@type": "Answer",
274    "text": "Ya, NAGARP X Sunny Pork fokus pada perubahan teknologi digital yang membentuk pengalaman platform interaktif dengan elemen visual dan navigasi canggih."
275  }
276},
277{
278  "@type": "Question",
279  "name": "Mengapa pengalaman platform interaktif penting di NAGARP X Sunny Pork?",
280  "acceptedAnswer": {
281    "@type": "Answer",
282    "text": "Pengalaman platform interaktif di NAGARP X Sunny Pork menjadi prioritas karena perubahan teknologi digital memungkinkan interaksi yang lebih personal dan efisien."
283  }
284},
285{
286  "@type": "Question",
287  "name": "Apa keunggulan NAGARP X Sunny Pork dalam hal teknologi digital?",
288  "acceptedAnswer": {
289    "@type": "Answer",
290    "text": "Keunggulan NAGARP X Sunny Pork terletak pada kemampuannya memanfaatkan perubahan teknologi digital untuk menghadirkan pengalaman platform interaktif yang seamless."
291  }
292}
293  ]
294}
295</script>
295
296  <!-- User Session Data -->
297    <meta name="csrf-token" content="9ce6e4a17f7cc189893df9e47789b13e98829450d516a0cae5ca7b5f43e5dadc">
298  <meta name="user-logged-in" content="false">
299  <meta name="user-id" content="0">
300  
301  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
302
303  <link rel="shortcut icon" href="https://sunnypork.com/assets/images/icon-nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.png" type="image/x-icon">
304  <style data-inlined="style.css">
305@font-face {
306    font-family: 'Segoe UI';
307    src: url('/assets/fonts/segoeui.woff2') format('woff2'),
308         url('/assets/fonts/segoeui.woff') format('woff');
309    font-weight: 400;
310    font-style: normal;
311}
312
313:root {
314  --brand-50:#A61016;
315  --brand-100:#335DD9;
316  --brand-500:#C10DFF;
317  --brand-600:#C10DFF;
318  --gray-900:#A61016;
319  --gray-700:#335DD9;
320  --gray-500:#6F737B;
321  --gray-300:#C5C9CF;
322  --gray-100:#335DD9;
323  --border:#E4E7EB;
324  --radius:8px;
325  --transition: .18s ease;
326  /* font-family: "Inter", system-ui, sans-serif; */
327  font-family: 'Segoe UI', sans-serif;
328}
329
330* { box-sizing: border-box; }
331body { 
332  margin:0; 
333  padding-bottom: 0; /* Reset for desktop */
334  color:var(--gray-900); 
335  background:#fff; 
336}
337img { max-width:100%; display:block; }
338
339.container {
340  width:100%;
341  max-width:1280px;
342  margin:0 auto;
343  padding:10px 0;
344}
345
346.topbar {
347  background:#111;
348  color:#fff;
349  font-size:13px;
350}
351.topbar-inner {
352  display:flex;
353  justify-content:space-between;
354  align-items:center;
355  min-height:36px;
356}
357.topbar a { color:#fff; text-decoration:none; margin-left:12px; }
358
359/* ========== Desktop base (Glassmorphism Modern with Warm Gradient) ========== */
360.navbar {
361  background:linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(255,245,230,0.85) 100%);
362  backdrop-filter:blur(12px);
363  -webkit-backdrop-filter:blur(12px);
364  border-bottom:1px solid rgba(228,231,235,0.6);
365  box-shadow:0 2px 16px -4px rgba(0,0,0,0.08);
366  position:sticky;
367  top:0;
368  z-index:500;
369  transition:all .3s ease;
370}
371
372/* Accent line gradient #d9a837 */
373.navbar::after {
374  content:'';
375  position:absolute;
376  bottom:0;
377  left:0;
378  right:0;
379  height:2px;
380  background:linear-gradient(90deg,transparent 0%, var(--brand-500) 20%, var(--brand-600) 50%, var(--brand-500) 80%, transparent 100%);
381  opacity:0.5;
382}
383
384.nav-inner {
385  display:flex;
386  align-items:center;
387  gap:24px;
388  min-height:70px;
389}
390.nav-inner .search .m-search-ico { display: none; }
391
392/* Secara default (desktop) ikon login disembunyikan; teks muncul */
393.login-btn .login-ico { display:none; }
394.navbar-logo {
395  display: none;
396  background:linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(255,245,230,0.85) 100%);
397  backdrop-filter:blur(12px);
398  -webkit-backdrop-filter:blur(12px);
399  border-bottom:1px solid rgba(228,231,235,0.6);
400  box-shadow:0 2px 16px -4px rgba(0,0,0,0.08);
401  position:sticky;
402  top:0;
403  z-index:500;
404}
405
406/* Hide bottom navigation on desktop - only show on mobile */
407.bottom-nav {
408  display: none;
409}
410
411/* ==============================================================
412   MOBILE (<=640px)
413   ============================================================== */
414@media (max-width:640px){
415  .container { padding:10px 16px; }
416  
417  .topbar { display:none !important; }
418
419  /* Add padding bottom for sticky bottom nav */
420  body {
421    padding-bottom: 68px; /* Height of bottom nav + safe area */
422  }
423
424  /* Mobile Top Navbar - Clean & Minimal */
425  .navbar {
426    background:linear-gradient(135deg, rgba(255,255,255,0.97) 0%, rgba(255,245,230,0.9) 100%);
427    backdrop-filter:blur(16px);
428    -webkit-backdrop-filter:blur(16px);
429    box-shadow:0 2px 12px -3px rgba(0,0,0,0.08);
430    border: 0;
431  }
432
433  /* Simple grid layout - Logo, Search, Category */
434  .nav-inner {
435    display:grid;
436    grid-template-columns:1fr;
437    gap:10px;
438    padding:12px 16px 0 12px;
439    min-height:auto;
440  }
441
442  /* Logo centered at top */
443  .nav-inner .logo {
444    justify-self:center;
445    margin:0;
446    display:flex;
447  }
448  .nav-inner .logo img { height:36px; }
449
450  /* Search bar - full width */
451  .nav-inner .search {
452    position:relative;
453    display:flex;
454    align-items:center;
455    gap:0;
456    width:100%;
457    height:44px;
458    background:#fff;
459    border:1.5px solid #e5e7eb;
460    border-radius:12px;
461    padding:0 12px;
462    box-shadow:0 1px 3px rgba(0,0,0,0.04);
463    transition:all .25s;
464  }
465  .nav-inner .search:focus-within {
466    border-color:#C10DFF;
467    box-shadow:0 0 0 3px rgba(245,202,86,0.1);
468  }
469
470  .nav-inner .search-icon-btn {
471    color:#9ca3af;
472    padding:4px;
473    margin-right:4px;
474  }
475  
476  .nav-inner .search-icon-btn svg {
477    width:18px;
478    height:18px;
479  }
480
481  .nav-inner .search input {
482    flex:1;
483    min-width:0;
484    border:none;
485    outline:none;
486    background:transparent;
487    font-size:14px;
488    padding:0 8px;
489    height:100%;
490  }
491  
492  .nav-inner .ai-bot-toggle {
493    padding:6px;
494    margin-left:0;
495  }
496  
497  .nav-inner .ai-bot-toggle .bot-icon {
498    width:20px;
499    height:20px;
500  }
501  
502  .nav-inner .search-suggest {
503    left:0;
504    right:0;
505    width:calc(100vw - 32px);
506    margin-top:6px;
507    border-radius:12px;
508    max-height:70vh;
509  }
510
511  /* Hide desktop nav actions but keep structure for userMenu */
512  .nav-inner .nav-actions {
513    display:flex !important; /* Keep flex to allow userMenu positioning */
514    visibility:hidden; /* Hide visually but keep in DOM */
515    pointer-events:none; /* Disable interactions with hidden elements */
516    position:absolute; /* Remove from flow */
517    right:0;
518    top:0;
519  }
520  
521  /* Re-enable userMenu for logged-in users */
522  .nav-inner .nav-actions #userMenu {
523    visibility:visible !important;
524    pointer-events:auto !important;
525  }
526  
527  .nav-inner .logo { display: none; }
528  .navbar-logo {
529    display: block;
530    /* background:linear-gradient(135deg, rgba(255,255,255,0.97) 0%, rgba(255,245,230,0.9) 100%); */
531    backdrop-filter:blur(16px);
532    -webkit-backdrop-filter:blur(16px);
533    position: static; /* Remove sticky on mobile */
534    box-shadow: none;
535    border-bottom: 0;
536    padding-top: .6rem;
537  }
538  .nav-inner .logo-mobile { display: block; }
539  
540  /* Category menu wrapper will handle visibility */
541
542  /* ==============================================================
543     BOTTOM NAVIGATION - Styles moved to bottom-nav.css
544     ============================================================== */
545  /* All bottom-nav styles now in /assets/css/bottom-nav.css */
546
547  /* Hide desktop elements that now moved to bottom nav */
548  #wishlistBtn,
549  #notifBtn,
550  #loginBtn { display:none !important; }
551  
552  /* Hide desktop user menu on mobile - use bottom sheet instead */
553  .navbar #userMenu { 
554    display:none !important;
555  }
556  
557  .nav-actions .cart-indicator,
558  .nav-actions .compare-indicator {
559    display:none !important;
560  }
561
562  /* Hide category button from bottom nav */
563  #bottomCategoryBtn {
564    display: none !important;
565  }
566  
567  /* User menu button styling for mobile - scoped to navbar only */
568  .navbar #userMenu .login-btn.logged-in {
569    width:40px;
570    height:40px;
571    padding:0;
572    border-radius:50%;
573    display:flex;
574    align-items:center;
575    justify-content:center;
576    background:rgba(255,255,255,0.95);
577    backdrop-filter:blur(12px);
578    -webkit-backdrop-filter:blur(12px);
579    border:1px solid rgba(228,231,235,0.6);
580    box-shadow:0 2px 12px -3px rgba(0,0,0,0.12);
581  }
582  
583  .navbar #userMenu .login-btn.logged-in .login-text { 
584    display:none; 
585  }
586  
587  .navbar #userMenu .login-btn.logged-in .login-ico {
588    display:inline;
589    font-size:20px;
590    line-height:1;
591  }
592  
593  /* User dropdown positioning for mobile */
594  .navbar #userMenu .user-dropdown {
595    position:fixed !important;
596    top:60px !important;
597    right:16px !important;
598    left:auto !important;
599    min-width:200px;
600    background:rgba(255,255,255,0.98);
601    backdrop-filter:blur(20px);
602    -webkit-backdrop-filter:blur(20px);
603    border:1px solid rgba(228,231,235,0.6);
604    box-shadow:0 8px 24px -8px rgba(0,0,0,0.2);
605    z-index:1002 !important;
606    border-radius:12px;
607    padding:8px 0;
608  }
609  
610  /* Force visibility when not hidden attribute */
611  .navbar #userMenu .user-dropdown:not([hidden]) {
612    display:block !important;
613    visibility:visible !important;
614    opacity:1 !important;
615  }
616
617  /* Container untuk kategori dan tombol kategori sejajar */
618  .navbar .container:last-child {
619    display:flex !important;
620    align-items:center !important;
621    padding:4px 16px 6px !important;
622    gap:5px;
623    overflow:visible;
624  }
625  
626  /* Wrapper untuk mega-cats dengan fade gradient (mobile only) */
627  .mega-cats-wrapper {
628    position:relative;
629    flex:1;
630    overflow:hidden;
631    min-height:34px; /* Ensure content is visible */
632    display:flex; /* Ensure wrapper is flex container */
633  }
634  
635  /* Fade gradient indicator di ujung kanan (mobile only) */
636  .mega-cats-fade {
637    position:absolute;
638    top:0;
639    right:0;
640    bottom:0;
641    width:80px; /* Increased from 60px for better visibility */
642    background:linear-gradient(to left, 
643      rgba(255,245,230,1) 0%, 
644      rgba(255,245,230,0.98) 15%,
645      rgba(255,245,230,0.85) 35%, 
646      transparent 100%);
647    pointer-events:none;
648    z-index:5; /* Increased z-index */
649    transition:opacity 0.3s ease;
650  }
651  
652  /* Kategori menu di mobile */
653  .mega-cats.container {
654    display:flex !important;
655    gap:12px;
656    overflow-x:auto;
657    overflow-y:visible;
658    -webkit-overflow-scrolling:touch;
659    scrollbar-width:none;
660    flex:1;
661    padding:0 !important;
662    margin:0 !important;
663    position:relative; /* For z-index stacking */
664    z-index:1; /* Below fade gradient */
665  }
666  .mega-cats.container::-webkit-scrollbar { display:none; }
667
668  /* Styling untuk menu items di mobile */
669  .mega-cats a {
670    font-size:13px;
671    padding:6px 10px;
672    white-space:nowrap;
673    flex-shrink:0;
674    position:relative; /* Ensure proper stacking */
675  }
676  .mega-cats a.active {
677    background:var(--brand-600);
678    color:#fff;
679    font-weight:600;
680    border-radius:6px;
681  }
682  
683  /* Tombol kategori di mobile */
684  .mm-wp {
685    flex-shrink:0;
686  }
687  .mm2-trigger {
688    display:flex;
689    align-items:center;
690    gap:6px;
691    padding:6px 12px;
692    background:var(--gray-100);
693    color:var(--gray-700);
694    text-decoration:none;
695    border-radius:6px;
696    font-size:13px;
697    white-space:nowrap;
698  }
699  
700  .mm2-trigger .label-cat {display: none; }
701  /* Tombol more kategori */
702  .mega-more-btn {
703    border:1px solid var(--border);
704    background:#fff;
705    border-radius:10px;
706    padding:6px 14px;
707    font-size:18px;
708    cursor:pointer;
709  }
710  .mega-cats-more.active > .mega-more-btn {
711    background:var(--brand-600);
712    color:#fff;
713    border-color:var(--brand-600);
714  }
715  .mega-more-dropdown {
716    top:calc(100% + 10px);
717    right:0;
718    min-width:85vw;
719    max-width:90vw;
720    border-radius:16px;
721  }
722  .mega-more-grid {
723    grid-template-columns:repeat(auto-fill,minmax(120px,1fr));
724  }
725  
726  /* ==============================================================
727     MOBILE USER BOTTOM SHEET
728     ============================================================== */
729  .mobile-user-sheet {
730    position:fixed;
731    top:0;
732    left:0;
733    right:0;
734    bottom:0;
735    z-index:10000;
736    pointer-events:none;
737  }
738  
739  .mobile-user-sheet.active {
740    pointer-events:auto;
741  }
742  
743  .mobile-user-sheet-backdrop {
744    position:absolute;
745    top:0;
746    left:0;
747    right:0;
748    bottom:0;
749    background:rgba(0,0,0,0.5);
750    opacity:0;
751    transition:opacity .3s ease;
752  }
753  
754  .mobile-user-sheet.active .mobile-user-sheet-backdrop {
755    opacity:1;
756  }
757  
758  .mobile-user-sheet-content {
759    position:absolute;
760    bottom:0;
761    left:0;
762    right:0;
763    background:#fff;
764    border-radius:20px 20px 0 0;
765    box-shadow:0 -4px 24px rgba(0,0,0,0.15);
766    transform:translateY(100%);
767    transition:transform .3s cubic-bezier(0.4, 0.0, 0.2, 1);
768    padding-bottom:calc(68px + env(safe-area-inset-bottom));
769  }
770  
771  .mobile-user-sheet.active .mobile-user-sheet-content {
772    transform:translateY(0);
773  }
774  
775  .mobile-user-sheet-header {
776    padding:16px 20px 12px;
777    border-bottom:1px solid #e5e7eb;
778  }
779  
780  .mobile-user-sheet-handle {
781    width:40px;
782    height:4px;
783    background:#d1d5db;
784    border-radius:2px;
785    margin:0 auto 16px;
786  }
787  
788  .mobile-user-info {
789    display:flex;
790    align-items:center;
791    gap:12px;
792  }
793  
794  .mobile-user-avatar {
795    width:48px;
796    height:48px;
797    border-radius:50%;
798    background:linear-gradient(135deg, #335DD9, #fff1cc);
799    display:flex;
800    align-items:center;
801    justify-content:center;
802    font-size:24px;
803    flex-shrink:0;
804  }
805  
806  .mobile-user-details {
807    flex:1;
808    min-width:0;
809  }
810  
811  .mobile-user-name {
812    font-size:16px;
813    font-weight:600;
814    color:#111827;
815    margin-bottom:2px;
816    white-space:nowrap;
817    overflow:hidden;
818    text-overflow:ellipsis;
819  }
820  
821  .mobile-user-email {
822    font-size:13px;
823    color:#6b7280;
824    white-space:nowrap;
825    overflow:hidden;
826    text-overflow:ellipsis;
827  }
828  
829  .mobile-user-sheet-body {
830    padding:8px 0;
831  }
832  
833  .mobile-user-sheet-item {
834    display:flex;
835    align-items:center;
836    gap:16px;
837    padding:14px 20px;
838    text-decoration:none;
839    color:#374151;
840    font-size:15px;
841    transition:background .2s;
842    border:none;
843    background:none;
844    width:100%;
845    text-align:left;
846    cursor:pointer;
847  }
848  
849  .mobile-user-sheet-item:active {
850    background:#f3f4f6;
851  }
852  
853  .mobile-user-sheet-icon {
854    font-size:20px;
855    width:24px;
856    text-align:center;
857    flex-shrink:0;
858  }
859  
860  .mobile-user-sheet-label {
861    flex:1;
862    font-weight:500;
863  }
864  
865  .mobile-user-sheet-divider {
866    height:1px;
867    background:#e5e7eb;
868    margin:8px 0;
869  }
870}
871
872/* END NAVBAR */
873
874.logo img { display:block; }
875.search {
876  flex:1;
877  position:relative;
878  display:flex;
879  align-items:center;
880  gap:0;
881  background:#fff;
882  border:1.5px solid #e5e7eb;
883  border-radius:12px;
884  padding:0 16px;
885  height:38px;
886  transition:all 0.3s ease;
887}
888.search:focus-within {
889  border-color:#C10DFF;
890  box-shadow:0 0 0 3px rgba(245,202,86,0.1);
891}
892.search input {
893  flex:1;
894  border:none;
895  outline:none;
896  background:transparent;
897  font-size:15px;
898  padding:0 12px;
899  height:100%;
900  color:#1f2937;
901}
902.search input::placeholder {
903  color:#9ca3af;
904}
905.search-icon-btn {
906  background:transparent;
907  border:none;
908  padding:0;
909  display:flex;
910  align-items:center;
911  justify-content:center;
912  cursor:pointer;
913  color:#6b7280;
914  transition:color 0.2s ease;
915  flex-shrink:0;
916}
917.search-icon-btn:hover {
918  color:#C10DFF;
919}
920.search-suggest {
921  position:absolute;
922  left:0; right:0; top:100%;
923  background:#fff;
924  border:1px solid var(--border);
925  border-radius:var(--radius);
926  padding:8px 0;
927  box-shadow:0 8px 20px rgba(0,0,0,.08);
928  margin-top:4px;
929  font-size:14px;
930  max-height:260px;
931  overflow:auto;
932}
933.search-suggest div {
934  padding:6px 12px;
935  cursor:pointer;
936}
937.search-suggest div:hover { background:var(--gray-100); }
938
939.nav-actions { display:flex; gap:10px; align-items:center; }
940.icon-btn, .login-btn {
941  background:linear-gradient(135deg, rgba(255,255,255,0.95), rgba(249,250,251,0.9));
942  border:1px solid rgba(228,231,235,0.6);
943  padding:8px 12px;
944  border-radius:var(--radius);
945  cursor:pointer;
946  font-size:14px;
947  transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
948  height: 38px;
949  position:relative;
950  box-shadow:
951    0 1px 3px rgba(0,0,0,0.06),
952    0 0 0 1px rgba(255,255,255,0.5) inset;
953  backdrop-filter:blur(8px);
954  -webkit-backdrop-filter:blur(8px);
955}
956.icon-btn:hover, .login-btn:hover { 
957  background:linear-gradient(135deg, rgba(249,250,251,1), rgba(243,244,246,0.95));
958  border-color:rgba(209,213,219,0.8);
959  box-shadow:
960    0 2px 8px rgba(0,0,0,0.08),
961    0 0 0 1px rgba(255,255,255,0.6) inset;
962  transform:translateY(-1px);
963}
964.cart-indicator, .compare-indicator { position:relative; }
965.badge {
966  background:linear-gradient(135deg, #e2a642, #e2a642);
967  color:#fff;
968  font-size:10.5px;
969  font-weight:600;
970  padding:2px 6px;
971  border-radius:999px;
972  position:absolute;
973  top:-6px; 
974  right:-6px;
975  min-width:18px;
976  height:18px;
977  display:flex;
978  align-items:center;
979  justify-content:center;
980  box-shadow:
981    0 2px 8px rgba(226,166,66,0.4),
982    0 0 0 2px #fff,
983    0 0 12px -2px rgba(226,166,66,0.6);
984  transition:all 0.3s ease;
985}
986.icon-btn:hover .badge {
987  transform:scale(1.08);
988  box-shadow:
989    0 4px 12px rgba(226,166,66,0.5),
990    0 0 0 2px #fff,
991    0 0 16px -2px rgba(226,166,66,0.7);
992}
993.badge.is-empty { display:none; }
994
995/* User Menu Dropdown */
996.user-menu {
997  position:relative;
998}
999.user-dropdown {
1000  position:absolute;
1001  top:calc(100% + 8px);
1002  right:0;
1003  background:#fff;
1004  border:1px solid var(--border);
1005  border-radius:12px;
1006  box-shadow:0 10px 40px rgba(0,0,0,0.12);
1007  min-width:200px;
1008  padding:8px 0;
1009  z-index:9999;
1010}
1011
1012/* Ensure dropdown is visible when not hidden */
1013.user-dropdown:not([hidden]) {
1014  display:block !important;
1015  visibility:visible !important;
1016  opacity:1 !important;
1017}
1018
1019/* Mobile: Force user dropdown to be visible above everything */
1020@media (max-width:640px) {
1021  .user-dropdown:not([hidden]) {
1022    position:fixed !important;
1023    z-index:9999 !important;
1024    top:60px !important;
1025    right:16px !important;
1026  }
1027}
1028
1029.dropdown-item {
1030  display:flex;
1031  align-items:center;
1032  gap:12px;
1033  padding:10px 16px;
1034  text-decoration:none;
1035  color:var(--gray-700);
1036  font-size:14px;
1037  transition:var(--transition);
1038  border:none;
1039  background:none;
1040  width:100%;
1041  text-align:left;
1042  cursor:pointer;
1043}
1044.dropdown-item:hover {
1045  background:var(--gray-100);
1046  color:var(--gray-900);
1047}
1048.dropdown-icon {
1049  font-size:16px;
1050  opacity:0.7;
1051}
1052.dropdown-separator {
1053  height:1px;
1054  background:var(--border);
1055  margin:8px 0;
1056}
1057.login-btn.logged-in {
1058  background:linear-gradient(135deg, rgba(255,245,230,0.8), rgba(255,231,204,0.6));
1059  border:1px solid rgba(245,202,86,0.2);
1060  color:var(--brand-700);
1061  max-width:200px; /* Prevent button from getting too wide */
1062  display:inline-flex;
1063  align-items:center;
1064  gap:10px;
1065  padding:4px 12px 4px 4px;
1066  transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1067  position:relative;
1068  overflow:hidden;
1069  box-shadow:
1070    0 1px 3px rgba(245,202,86,0.08),
1071    0 0 0 1px rgba(255,255,255,0.5) inset,
1072    0 0 12px -6px rgba(245,202,86,0.1);
1073  backdrop-filter:blur(8px);
1074  -webkit-backdrop-filter:blur(8px);
1075}
1076/* Subtle background glow effect */
1077.login-btn.logged-in::before {
1078  content:'';
1079  position:absolute;
1080  top:50%;
1081  left:0;
1082  width:32px;
1083  height:32px;
1084  background:radial-gradient(circle, rgba(245,202,86,0.15), transparent 70%);
1085  transform:translateY(-50%);
1086  opacity:0;
1087  transition:opacity 0.3s ease;
1088}
1089.login-btn.logged-in:hover::before {
1090  opacity:1;
1091}
1092.login-btn.logged-in .user-avatar {
1093  width:32px;
1094  height:32px;
1095  border-radius:50%;
1096  background:linear-gradient(135deg, #335DD9 0%, #fff1cc 50%, #C10DFF 100%);
1097  color:#C10DFF;
1098  font-size:11.5px;
1099  font-weight:700;
1100  display:flex;
1101  align-items:center;
1102  justify-content:center;
1103  flex-shrink:0;
1104  letter-spacing:0.3px;
1105  position:relative;
1106  box-shadow:
1107    0 2px 8px rgba(245,202,86,0.25),
1108    0 1px 3px rgba(0,0,0,0.12),
1109    inset 0 1px 0 rgba(255,255,255,0.5);
1110  transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1111}
1112/* Ring gradient border */
1113.login-btn.logged-in .user-avatar::before {
1114  content:'';
1115  position:absolute;
1116  inset:-2px;
1117  border-radius:50%;
1118  padding:2px;
1119  background:linear-gradient(135deg, #C10DFF, #fff1cc, #C10DFF);
1120  -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
1121  -webkit-mask-composite:xor;
1122  mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
1123  mask-composite:exclude;
1124  opacity:0;
1125  transition:opacity 0.3s ease;
1126}
1127.login-btn.logged-in:hover .user-avatar::before {
1128  opacity:1;
1129}
1130.login-btn.logged-in:hover .user-avatar {
1131  transform:scale(1.08) rotate(-3deg);
1132  box-shadow:
1133    0 4px 12px rgba(245,202,86,0.35),
1134    0 2px 6px rgba(0,0,0,0.15),
1135    inset 0 1px 0 rgba(255,255,255,0.6);
1136}
1137.login-btn.logged-in .login-text {
1138  overflow:hidden;
1139  text-overflow:ellipsis;
1140  white-space:nowrap;
1141  flex:1;
1142  min-width:0;
1143  font-weight:500;
1144  font-size:14px;
1145  transition:color 0.2s ease;
1146}
1147.login-btn.logged-in:hover .login-text {
1148  color:var(--brand-600);
1149}
1150.login-btn.logged-in:hover {
1151  background:linear-gradient(135deg, rgba(255,245,230,0.95), rgba(255,231,204,0.85));
1152  border-color:rgba(245,202,86,0.3);
1153  box-shadow:
1154    0 4px 16px -4px rgba(245,202,86,0.25),
1155    0 0 0 1px rgba(245,202,86,0.1) inset,
1156    0 1px 2px rgba(0,0,0,0.05);
1157  transform:translateY(-1px);
1158}
1159
1160/* Desktop: hide wrapper elements, show menu normally */
1161@media (min-width: 761px) {
1162  .mega-cats-wrapper {
1163    display:contents; /* Remove wrapper from layout */
1164  }
1165  .mega-cats-fade {
1166    display:none !important; /* Hide fade on desktop */
1167  }
1168}
1169
1170.mega-cats {
1171  display:flex;
1172  gap:5px;
1173  /* padding:6px 16px 10px; */
1174  scrollbar-width:none;
1175  position: relative;
1176  z-index: 510;
1177  overflow-x:auto;
1178  -webkit-overflow-scrolling:touch;
1179  flex:1;
1180}
1181.mega-cats::-webkit-scrollbar { 
1182  display:none; 
1183}
1184.mega-cats a {
1185  text-decoration:none;
1186  font-size:14px;
1187  color:var(--gray-700);
1188  padding:6px 10px;
1189  border-radius:6px;
1190  white-space:nowrap;
1191  transition:var(--transition);
1192}
1193.mega-cats a:hover { background:var(--brand-100); color:var(--brand-600); }
1194/*.mega-cats::after {
1195  content:'';
1196  position:absolute;
1197  right:0;
1198  top:0;
1199  width:40px;
1200  height:100%;
1201  background:linear-gradient(to right,rgba(255,255,255,0),#fff);
1202  pointer-events:none;
1203}*/
1204
1205/* Tambahan untuk tombol More & dropdown.
1206   Style dasar .mega-cats dan .mega-cats a tetap pakai definisi Anda. */
1207
1208.mega-cats-more {
1209  position:relative;
1210  display:flex;
1211  align-items:center;
1212}
1213
1214.mega-cats-more.active > .mega-more-btn {
1215  background:var(--brand-600);
1216  color:#fff;
1217}
1218
1219.mega-more-btn {
1220  cursor:pointer;
1221  border:none;
1222  background:var(--gray-100,#f5f5f5);
1223  color:var(--gray-700,#333);
1224  padding:6px 12px;
1225  font-size:16px;
1226  line-height:1;
1227  border-radius:6px;
1228  display:flex;
1229  align-items:center;
1230  justify-content:center;
1231  transition:var(--transition);
1232}
1233.mega-more-btn:hover {
1234  background:var(--brand-100);
1235  color:var(--brand-600);
1236}
1237
1238.mega-more-dropdown {
1239  position:absolute;
1240  top:100%;
1241  right:0;
1242  margin-top:8px;
1243  background:#fff;
1244  border:1px solid var(--gray-200,#e3e6e9);
1245  border-radius:10px;
1246  padding:12px 14px 14px;
1247  min-width:400px;
1248  max-width:520px;
1249  max-height:420px;
1250  box-shadow:0 12px 32px -8px rgba(25,40,65,.25);
1251  z-index: 3000; 
1252}
1253
1254.mega-more-grid {
1255  display:grid;
1256  grid-template-columns:repeat(auto-fill,minmax(120px,1fr));
1257  gap:8px 10px;
1258  overflow-x: hidden;
1259  max-height:380px;
1260  overscroll-behavior:contain;
1261  scrollbar-width:thin;
1262}
1263.mega-more-grid::-webkit-scrollbar {
1264  width:6px;
1265}
1266.mega-more-grid::-webkit-scrollbar-thumb {
1267  background:var(--gray-300,#c7ccd1);
1268  border-radius:3px;
1269}
1270
1271/* Link di dropdown mewarisi style .mega-cats a (warna, padding, radius).
1272   Kita tambahkan state active agar konsisten. */
1273.mega-more-grid a.active {
1274  background:var(--brand-600);
1275  color:#fff;
1276  font-weight:600;
1277}
1278.hero {
1279  background:linear-gradient(135deg,var(--brand-50), #fff);
1280}
1281.hero-inner {
1282  display:grid;
1283  grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
1284  gap:48px;
1285  align-items:center;
1286}
1287.hero-text h1 {
1288  font-size:48px;
1289  line-height:1.1;
1290  margin:0;
1291  font-weight:600;
1292  font-family:"Poppins",sans-serif;
1293}
1294.hero-text .text-brand { color:var(--brand-500); }
1295.lead { font-size:18px; color:var(--gray-700); margin-top:16px; }
1296.hero-cta { margin-top:28px; display:flex; gap:16px; }
1297.hero-bullets { margin-top:28px; display:flex; gap:28px; font-size:14px; color:var(--gray-600); }
1298.hero-box {
1299  background:#fff;
1300  padding:10px;
1301  border-radius:16px;
1302  box-shadow:0 4px 18px rgba(0,0,0,.08);
1303  /* min-height:320px; */
1304  display:flex;
1305  align-items:center;
1306  justify-content:center;
1307}
1308.hero-box img {border-radius: 10px; }
1309
1310@media (max-width:680px) {
1311    .hero-inner { gap: 25px; }    
1312}
1313
1314.usp-section {
1315  position:relative;
1316  background:
1317    linear-gradient(180deg,#FFFDFB 0%, #FFFFFF 60%) ;
1318  border-top:1px solid var(--border);
1319  border-bottom:1px solid var(--border);
1320  padding:10px 0 10px;
1321}
1322
1323.usp-list {
1324  list-style:none;
1325  margin:0;
1326  padding:0;
1327  display:grid;
1328  gap:26px;
1329}
1330
1331.usp-list.variant-soft {
1332  grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
1333}
1334
1335.usp-item {
1336  display:flex;
1337  gap:16px;
1338  align-items:flex-start;
1339  background:#fff;
1340  padding:18px 18px 18px 16px;
1341  border:1px solid rgba(226,226,226,0.7);
1342  border-radius:18px;
1343  position:relative;
1344  overflow:hidden;
1345  isolation:isolate;
1346  box-shadow:0 2px 4px -1px rgba(0,0,0,.04), 0 6px 18px -6px rgba(0,0,0,.08);
1347  transition: .35s cubic-bezier(.16,.8,.32,1);
1348}
1349
1350.usp-item::before {
1351  content:"";
1352  position:absolute;
1353  inset:0;
1354  background:radial-gradient(circle at 18% 22%, rgba(245,202,86,.18), transparent 60%);
1355  opacity:.65;
1356  transition:.5s;
1357  z-index:-1;
1358}
1359
1360.usp-item:hover {
1361  transform:translateY(-6px);
1362  box-shadow:0 10px 26px -8px rgba(0,0,0,.15), 0 3px 8px -2px rgba(0,0,0,.08);
1363  border-color:rgba(245,202,86,.35);
1364}
1365
1366.usp-item:hover::before {
1367  background:radial-gradient(circle at 70% 40%, rgba(245,202,86,.22), transparent 65%);
1368}
1369
1370.usp-icon {
1371  flex:0 0 56px;
1372  width:56px;
1373  height:56px;
1374  border-radius:16px;
1375  display:flex;
1376  align-items:center;
1377  justify-content:center;
1378  background:
1379    linear-gradient(135deg,#335DD9,#fff1cc);
1380  position:relative;
1381  box-shadow:inset 0 0 0 1px rgba(255,255,255,.6), 0 4px 10px -3px rgba(0,0,0,.15);
1382  transition:.45s;
1383}
1384
1385.usp-item:hover .usp-icon {
1386  transform:rotate(-4deg) scale(1.06);
1387  box-shadow:inset 0 0 0 1px rgba(255,255,255,.8), 0 6px 14px -4px rgba(0,0,0,.20);
1388}
1389
1390.usp-icon svg {
1391  width:28px;
1392  height:28px;
1393  display:block;
1394  filter:drop-shadow(0 2px 2px rgba(0,0,0,.18));
1395}
1396
1397.usp-text h3 {
1398  font-size:15px;
1399  font-weight:600;
1400  margin:2px 0 6px;
1401  letter-spacing:.2px;
1402}
1403
1404.usp-text p {
1405  font-size:13px;
1406  line-height:1.3;
1407  margin:0;
1408  color:var(--gray-600);
1409}
1410
1411@media (max-width:680px) {
1412  .usp-section { padding: 5px 0 5px; margin-bottom: 0.75rem;}
1413  .usp-item { padding:16px 14px; }
1414  .usp-icon { width:50px;height:50px;border-radius:14px; }
1415  .usp-text h3 { font-size:14px; margin-bottom:4px; }
1416  .usp-text p { font-size:12.5px; }
1417  .usp-list { gap: 10px; }
1418}
1419
1420.flash { padding:50px 0 30px; background:#fff; }
1421.flash-head {
1422  display:flex; align-items:center;
1423  justify-content:space-between;
1424  margin-bottom:20px;
1425}
1426.flash-head h2 { margin:0; font-size:24px; font-family:"Poppins"; }
1427.flash .countdown {
1428  font-family:monospace;
1429  background:#111;
1430  color:#fff;
1431  padding:8px 14px;
1432  border-radius:8px;
1433  font-size:16px;
1434  letter-spacing:1px;
1435}
1436.flash-scroll {
1437  display:flex;
1438  gap:16px;
1439  overflow-x:auto;
1440  padding-bottom:6px;
1441  scrollbar-width:none;
1442}
1443.flash-scroll::-webkit-scrollbar { display:none; }
1444
1445.products { padding:60px 0; background:#fff; }
1446.products-head {
1447  display:flex; justify-content:space-between; align-items:center;
1448  margin-bottom:24px;
1449  flex-wrap:wrap;
1450  gap:16px;
1451}
1452.products-head h2 { margin:0; font-size:24px; font-family:"Poppins"; }
1453.product-filters input {
1454  padding:8px 12px;
1455  border:1px solid var(--border);
1456  border-radius:8px;
1457  font-size:14px;
1458  width:220px;
1459}
1460
1461.product-grid {
1462  display:grid;
1463  gap:24px;
1464  grid-template-columns:repeat(auto-fill,minmax(180px,1fr));
1465}
1466
1467.prod-card {
1468  background:#fff;
1469  border:1px solid var(--border);
1470  padding:12px;
1471  border-radius:12px;
1472  position:relative;
1473  display:flex;
1474  flex-direction:column;
1475  transition:var(--transition);
1476}
1477.prod-card.small { min-width:160px; }
1478.prod-card:hover { box-shadow:0 8px 20px rgba(0,0,0,.12); transform:translateY(-3px); }
1479.prod-card .img-box {
1480  background:var(--gray-100);
1481  border-radius:10px;
1482  aspect-ratio:1/1;
1483  display:flex;
1484  align-items:center;
1485  justify-content:center;
1486  overflow:hidden;
1487}
1488.prod-card .img-box img { width:100%; height:100%; object-fit:contain; transition:var(--transition); }
1489.prod-card:hover .img-box img { transform:scale(1.04); }
1490.prod-card .name {
1491  font-size:13.5px;
1492  font-weight:500;
1493  margin:10px 0 6px;
1494  min-height:36px;
1495  line-height:1.3;
1496}
1497.price {
1498  display:flex;
1499  gap:6px;
1500  align-items:center;
1501  flex-wrap:wrap;
1502}
1503.price strong { color:var(--brand-600); font-size:14px; }
1504.price .old {
1505  font-size:11px;
1506  color:var(--gray-500);
1507  text-decoration:line-through;
1508}
1509.tag.discount {
1510  position:absolute;
1511  top:8px; left:8px;
1512  background:#e2a642;
1513  color:#fff;
1514  font-size:11px;
1515  padding:4px 6px;
1516  border-radius:6px;
1517  font-weight:600;
1518}
1519.btn {
1520  border:none;
1521  cursor:pointer;
1522  font-weight:500;
1523  font-size:14px;
1524  padding:12px 22px;
1525  border-radius:10px;
1526  background:var(--gray-900);
1527  color:#fff;
1528  transition:var(--transition);
1529  text-decoration:none;
1530  text-align:center;
1531  display:inline-flex;
1532  align-items:center;
1533  justify-content:center;
1534  gap:6px;
1535}
1536.btn:hover { background:#000; }
1537.btn-primary { background:var(--brand-500); }
1538.btn-primary:hover { background:var(--brand-600); }
1539.btn-outline {
1540  background:#fff;
1541  color:var(--gray-900);
1542  border:1px solid var(--border);
1543}
1544.btn-outline:hover { background:var(--gray-100); }
1545.btn-sm { padding:8px 14px; font-size:13px; }
1546.btn-mini { padding:6px 10px; font-size:12px; }
1547.add-cart { margin-top:auto; background:var(--brand-500); color:#fff; }
1548.add-cart:hover { background:var(--brand-600); }
1549
1550/* ==== FOOTER COMPACT FIX PATCH (final) ==== */
1551
1552/* 1. Pastikan grid aktif */
1553
1554.site-footer.footer-compact .ft-grid {
1555  display:grid;
1556  grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
1557  gap:30px 40px;
1558  align-items:start;
1559}
1560
1561/* 2. Spacing atas bawah dipadatkan */
1562.site-footer.footer-compact .ft-top {
1563  padding:42px 0 26px;
1564}
1565
1566/* 3. Judul & teks */
1567.site-footer.footer-compact .ft-title {
1568  margin:0 0 14px;
1569  font-size:14px;
1570  letter-spacing:.8px;
1571  text-transform:uppercase;
1572  font-weight:600;
1573  color:#82b1c9;
1574}
1575
1576.site-footer.footer-compact .ft-desc,
1577.site-footer.footer-compact .ft-text {
1578  margin:0 0 16px;
1579  font-size:13.5px;
1580  line-height:1.45;
1581  color:#c6d6e0;
1582  max-width:330px;
1583}
1584.site-footer.footer-compact .ft-desc.sm-gap { margin-top:18px; }
1585
1586/* 4. Reset ikon SVG agar tidak membesar */
1587.site-footer.footer-compact svg {
1588  width:20px;
1589  height:20px;
1590  flex:0 0 20px;
1591  display:inline-block;
1592}
1593.site-footer.footer-compact .ft-social svg { width:18px; height:18px; }
1594.site-footer.footer-compact .ft-wa-btn svg { width:18px; height:18px; }
1595
1596/* Jika ada aturan global svg { width:100%; } — paksa override */
1597svg { max-width:100%; }
1598.site-footer.footer-compact svg {
1599  width:20px !important;
1600  height:20px !important;
1601}
1602
1603/* 5. Link list */
1604.site-footer.footer-compact .ft-links {
1605  list-style:none;
1606  margin:0 0 12px;
1607  padding:0;
1608  display:flex;
1609  flex-direction:column;
1610  gap:4px;
1611}
1612.site-footer.footer-compact .ft-links li { margin:0; }
1613.site-footer.footer-compact .ft-links a {
1614  display:flex;
1615  align-items:center;
1616  gap:8px;
1617  font-size:13.5px;
1618  padding:5px 2px;
1619  text-decoration:none;
1620  color:#c6d6e0;
1621  border-radius:8px;
1622  transition:.25s;
1623}
1624.site-footer.footer-compact .ft-links a:hover {
1625  background:rgba(255,255,255,.06);
1626  padding-left:6px;
1627  color:#fff;
1628}
1629
1630.site-footer.footer-compact .ico-s {
1631  width:24px;
1632  height:24px;
1633  background:#103346;
1634  color:#89b8cf;
1635  border-radius:6px;
1636  display:flex;
1637  align-items:center;
1638  justify-content:center;
1639  flex:0 0 24px;
1640  font-size:12px;
1641  transition:.3s;
1642}
1643.site-footer.footer-compact .ft-links a:hover .ico-s {
1644  background:#C10DFF;
1645  color:#fff;
1646  transform:rotate(-4deg);
1647}
1648
1649/* 6. WhatsApp button */
1650.site-footer.footer-compact .ft-wa-btn {
1651  display:inline-flex;
1652  align-items:center;
1653  gap:8px;
1654  background:#fff;
1655  color:#062437;
1656  padding:8px 18px;
1657  font-size:13.5px;
1658  border-radius:22px;
1659  font-weight:500;
1660  text-decoration:none;
1661  box-shadow:0 3px 8px -3px rgba(0,0,0,.45);
1662  transition:.25s;
1663}
1664.site-footer.footer-compact .ft-wa-btn:hover {
1665  transform:translateY(-2px);
1666  box-shadow:0 6px 14px -6px rgba(0,0,0,.55);
1667}
1668
1669/* 7. Sosial */
1670.site-footer.footer-compact .ft-social {
1671  display:flex;
1672  gap:12px;
1673  margin-top:4px;
1674}
1675.site-footer.footer-compact .ft-social a {
1676  width:34px;
1677  height:34px;
1678  background:rgba(255,255,255,.08);
1679  display:flex;
1680  align-items:center;
1681  justify-content:center;
1682  border-radius:10px;
1683  color:#c6d6e0;
1684  transition:.25s;
1685}
1686.site-footer.footer-compact .ft-social a:hover {
1687  background:#C10DFF;
1688  color:#fff;
1689}
1690
1691/* 8. Alamat */
1692.site-footer.footer-compact .ft-address {
1693  font-size:12.5px;
1694  line-height:1.44;
1695  color:#8fa4b1;
1696  max-width:330px;
1697  margin-top:12px;
1698}
1699.site-footer.footer-compact .ft-address .company {
1700  margin:0 0 4px;
1701  font-weight:600;
1702  color:#e5f2f7;
1703  font-size:12.8px;
1704}
1705
1706/* 9. Payments kecil & rapi */
1707.site-footer.footer-compact .ft-payments {
1708  margin-top:14px;
1709  display:flex;
1710  flex-direction:column;
1711  gap:8px;
1712  max-width:350px;
1713}
1714.site-footer.footer-compact .ft-payments .pay-row {
1715  display:flex;
1716  gap:8px;
1717  flex-wrap:wrap;
1718}
1719.site-footer.footer-compact .ft-payments img {
1720  width:46px;
1721  height:32px;
1722  padding:3px 5px;
1723  background:#fff;
1724  border-radius:7px;
1725  object-fit:contain;
1726  box-shadow:0 2px 6px -3px rgba(0,0,0,.45);
1727  transition:.25s;
1728}
1729.site-footer.footer-compact .ft-payments img:hover {
1730  transform:translateY(-3px);
1731  box-shadow:0 5px 12px -6px rgba(0,0,0,.55);
1732}
1733
1734/* 10. Bottom bar */
1735.site-footer.footer-compact .ft-bottom {
1736  border-top:1px solid rgba(255,255,255,.08);
1737  background:#041f31;
1738  padding:12px 0 16px;
1739  margin-top:4px;
1740}
1741.site-footer.footer-compact .ft-copy {
1742  font-size:11.5px;
1743  color:#8fa7b5;
1744  display:flex;
1745  flex-wrap:wrap;
1746  gap:12px;
1747  justify-content:center;
1748  line-height:1.3;
1749  margin:0;
1750}
1751.site-footer.footer-compact .ft-copy span {
1752  position:relative;
1753  padding:0 10px;
1754}
1755.site-footer.footer-compact .ft-copy span+span:before {
1756  content:"";
1757  position:absolute;
1758  left:0;
1759  top:50%;
1760  width:1px;
1761  height:12px;
1762  background:rgba(255,255,255,.14);
1763  transform:translateY(-50%);
1764}
1765@media (max-width:580px){
1766  .site-footer.footer-compact .ft-top { padding:34px 0 18px; }
1767  .site-footer.footer-compact .ft-grid { gap:26px 20px; }
1768  .site-footer.footer-compact .ft-copy span { padding:0; }
1769  .site-footer.footer-compact .ft-copy span+span:before { display:none; }
1770}
1771.copy {
1772  margin-top:40px;
1773  border-top:1px solid #222;
1774  text-align:center;
1775  padding:18px 0;
1776  font-size:12px;
1777  color:#666;
1778}
1779
1780@media (max-width:760px) {
1781  .hero-text h1 { font-size:38px; }
1782  .hero { padding:0px 0 30px; }
1783  .topbar-inner { flex-direction:column; gap:4px; padding:6px 0; }
1784  .nav-inner { flex-wrap:wrap; }
1785  .mega-cats { gap:10px; }
1786  .product-grid { gap:16px; }
1787  .prod-card { padding:10px; }
1788}
1789
1790/* === PLAZA IT DARK #d9a837 FOOTER THEME === */
1791.site-footer.footer-compact {
1792  --ft-bg-0:#051722;
1793  --ft-bg-1:#072534;
1794  --ft-bg-2:#0B2F40;
1795  --ft-bg-3:#103A4E;
1796  --ft-orange:#C10DFF;
1797  --ft-orange-dark:#C10DFF;
1798  --ft-text:#E9F2F6;
1799  --ft-text-sec:#B8CAD3;
1800  --ft-text-muted:#8FA4B1;
1801  --ft-divider:rgba(255,255,255,0.08);
1802  --ft-glow:rgba(245,202,86,0.18);
1803  --ft-glow-fade:rgba(245,202,86,0);
1804  color:var(--ft-text);
1805}
1806
1807/* BASE TEXT / LINKS (override sebelumnya jika perlu) */
1808.site-footer.footer-compact .ft-title { color:#9CC5D7; }
1809.site-footer.footer-compact .ft-links a { color:var(--ft-text-sec); }
1810.site-footer.footer-compact .ft-links a:hover { color:#fff; }
1811.site-footer.footer-compact .ft-address { color:var(--ft-text-muted); }
1812.site-footer.footer-compact .ft-copy { color:#9AB2BD; }
1813
1814/* ICON BACKGROUND (kubu gelap) */
1815.site-footer.footer-compact .ico-s {
1816  background:#0F3647;
1817  color:#9FC7DA;
1818}
1819.site-footer.footer-compact .ft-links a:hover .ico-s {
1820  background:var(--ft-orange);
1821  color:#fff;
1822}
1823
1824/* WhatsApp button biarkan putih agar kontras */
1825.site-footer.footer-compact .ft-wa-btn {
1826  box-shadow:0 4px 12px -5px rgba(0,0,0,.6), 0 0 0 3px rgba(255,255,255,0.05);
1827}
1828.site-footer.footer-compact .ft-wa-btn:hover {
1829  box-shadow:0 8px 20px -8px rgba(0,0,0,.65), 0 0 0 3px rgba(245,202,86,0.25);
1830}
1831
1832/* Payment cards border halus */
1833.site-footer.footer-compact .ft-payments img {
1834  box-shadow:0 2px 6px -3px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,0.05);
1835}
1836.site-footer.footer-compact .ft-payments img:hover {
1837  box-shadow:0 6px 14px -6px rgba(0,0,0,.65), 0 0 0 1px rgba(245,202,86,0.4);
1838}
1839
1840/* ========== VARIANT A: DIAGONAL EMBER (AKTIF) ========== */
1841.site-footer.footer-compact.variant-ember {
1842  background:
1843    radial-gradient(circle at 78% 82%, var(--ft-glow) 0%, var(--ft-glow-fade) 55%),
1844    linear-gradient(140deg,var(--ft-bg-1) 0%, var(--ft-bg-2) 55%, var(--ft-bg-0) 100%);
1845}
1846.site-footer.footer-compact.variant-ember .ft-bottom {
1847  background:rgba(0,0,0,0.25);
1848  backdrop-filter:blur(2px);
1849}
1850
1851/* ========== VARIANT B: TOP BAR #d9a837 ========== */
1852.site-footer.footer-compact.variant-bar {
1853  background:var(--ft-bg-1);
1854  position:relative;
1855}
1856.site-footer.footer-compact.variant-bar::before {
1857  content:"";
1858  position:absolute;
1859  left:0;top:0;
1860  width:100%;height:3px;
1861  background:linear-gradient(90deg,var(--ft-orange-dark),var(--ft-orange));
1862  box-shadow:0 0 12px -2px var(--ft-orange);
1863}
1864.site-footer.footer-compact.variant-bar .ft-bottom {
1865  background:#041c28;
1866}
1867
1868/* ========== VARIANT C: RADIAL GLOW BAWAH ========== */
1869.site-footer.footer-compact.variant-radial {
1870  background:
1871    radial-gradient(circle at 70% 90%, rgba(245,202,86,0.22) 0%, rgba(245,202,86,0.04) 38%, rgba(245,202,86,0) 65%),
1872    linear-gradient(150deg,#052131 0%, #062d3f 50%, #041924 100%);
1873}
1874.site-footer.footer-compact.variant-radial .ft-bottom {
1875  background:#041c29;
1876}
1877
1878/* ========== VARIANT D: LAYERED STRIPES ========== */
1879.site-footer.footer-compact.variant-stripes {
1880  background:
1881    repeating-linear-gradient(180deg, rgba(255,255,255,0.015) 0 38px, rgba(255,255,255,0) 38px 76px),
1882    linear-gradient(130deg,#052636 0%, #062a3c 60%, #041d28 100%);
1883  position:relative;
1884}
1885.site-footer.footer-compact.variant-stripes::after {
1886  content:"";
1887  position:absolute;
1888  left:0;bottom:0;
1889  width:100%;height:110px;
1890  background:linear-gradient(0deg,rgba(0,0,0,.35),rgba(0,0,0,0));
1891  pointer-events:none;
1892}
1893.site-footer.footer-compact.variant-stripes .ft-bottom {
1894  background:#031821;
1895}
1896
1897/* Garis aksen oranye tipis di atas bottom */
1898.site-footer.footer-compact.variant-stripes .ft-bottom {
1899  position:relative;
1900}
1901.site-footer.footer-compact.variant-stripes .ft-bottom::before {
1902  content:"";
1903  position:absolute;
1904  left:0;top:0;
1905  width:100%;height:2px;
1906  background:linear-gradient(90deg,rgba(245,202,86,0),rgba(245,202,86,.65),rgba(245,202,86,0));
1907}
1908
1909/* ========== VARIANT E: GLASS PANEL ========== */
1910.site-footer.footer-compact.variant-glass {
1911  background:linear-gradient(145deg,#051c29 0%, #041620 65%, #03121b 100%);
1912}
1913.site-footer.footer-compact.variant-glass .ft-grid {
1914  position:relative;
1915  padding:32px clamp(10px,2vw,24px);
1916  border-radius:22px;
1917  background:linear-gradient(140deg,rgba(255,255,255,0.06),rgba(255,255,255,0.02));
1918  border:1px solid rgba(255,255,255,0.08);
1919  box-shadow:0 12px 28px -12px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.04) inset;
1920  backdrop-filter:blur(8px);
1921  -webkit-backdrop-filter:blur(8px);
1922}
1923.site-footer.footer-compact.variant-glass .ft-bottom {
1924  background:#02141d;
1925}
1926
1927/* HOVER ACCENTS */
1928.site-footer.footer-compact a {
1929  transition:color .25s, background .25s, box-shadow .25s;
1930}
1931.site-footer.footer-compact .ft-links a:hover {
1932  box-shadow:0 2px 6px -3px rgba(0,0,0,.5),
1933             0 0 0 1px rgba(245,202,86,0.55);
1934}
1935.site-footer.footer-compact .ft-social a {
1936  background:#0E3445;
1937  color:#bcd4dd;
1938}
1939.site-footer.footer-compact .ft-social a:hover {
1940  background:var(--ft-orange);
1941  color:#fff;
1942  box-shadow:0 0 0 3px rgba(245,202,86,0.28);
1943}
1944
1945/* Divider di atas footer (opsional) */
1946.footer-divider-top {
1947  height:1px;
1948  background:linear-gradient(90deg,transparent,rgba(255,255,255,0.15),transparent);
1949  width:100%;
1950  margin:0 0 4px;
1951}
1952
1953/* ==== MOBILE FOOTER COLLAPSE (Non-Homepage Only) ==== */
1954
1955/* Desktop: wrapper tidak mengganggu grid layout */
1956.ft-collapsible-content {
1957  display: contents;
1958}
1959
1960@media (max-width: 768px) {
1961  /* Wrapper untuk konten yang bisa collapse */
1962  .ft-top.ft-collapsible .ft-collapsible-content {
1963    display: block;
1964    max-height: 80px;
1965    overflow: hidden;
1966    position: relative;
1967    transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1);
1968  }
1969  
1970  /* Fade gradient overlay */
1971  .ft-top.ft-collapsible .ft-collapsible-content::after {
1972    content: '';
1973    position: absolute;
1974    bottom: 0;
1975    left: 0;
1976    right: 0;
1977    height: 70px;
1978    background: linear-gradient(to bottom, transparent 0%, rgba(6, 43, 69, 0.6) 40%, #062b45 100%);
1979    pointer-events: none;
1980    opacity: 1;
1981    transition: opacity 0.3s ease;
1982    z-index: 1;
1983  }
1984  
1985  /* State: Expanded */
1986  .ft-top.ft-collapsible.expanded .ft-collapsible-content {
1987    max-height: 1500px;
1988  }
1989  
1990  .ft-top.ft-collapsible.expanded .ft-collapsible-content::after {
1991    opacity: 0;
1992  }
1993  
1994  /* Expand Button - Di luar collapsible, selalu visible */
1995  .ft-expand-btn {
1996    display: flex;
1997    align-items: center;
1998    justify-content: center;
1999    gap: 8px;
2000    width: 100%;
2001    margin: -8px 0 0;
2002    padding: 12px 20px;
2003    background: rgba(255,255,255,0.12);
2004    border: 1px solid rgba(255,255,255,0.25);
2005    border-radius: 10px;
2006    color: #e5f2f7;
2007    font-size: 13.5px;
2008    font-weight: 500;
2009    cursor: pointer;
2010    transition: all 0.3s ease;
2011    outline: none;
2012    position: relative;
2013    z-index: 10;
2014  }
2015  
2016  .ft-expand-btn:hover {
2017    background: rgba(255,255,255,0.18);
2018    border-color: rgba(255,255,255,0.35);
2019    color: #fff;
2020    transform: translateY(-2px);
2021  }
2022  
2023  .ft-expand-btn:active {
2024    transform: scale(0.98);
2025  }
2026  
2027  .ft-expand-btn svg {
2028    width: 18px;
2029    height: 18px;
2030    transition: transform 0.3s ease;
2031    fill: currentColor;
2032  }
2033  
2034  .ft-top.ft-collapsible.expanded .ft-expand-btn {
2035    margin-top: 15px;
2036  }
2037  
2038  .ft-top.ft-collapsible.expanded .ft-expand-btn svg {
2039    transform: rotate(180deg);
2040  }
2041}
2042
2043/* Desktop: hide expand button */
2044@media (min-width: 769px) {
2045  .ft-expand-btn {
2046    display: none !important;
2047  }
2048}
2049
2050/* RESPONSIVE (jika background panel butuh penyesuaian) */
2051@media (max-width:640px){
2052  .site-footer.footer-compact.variant-glass .ft-grid {
2053    padding:26px 18px;
2054  }
2055}
2056
2057</style>
2058  <style data-inlined="bottom-nav.css">
2059/* Mobile Bottom Navigation */
2060.bottom-nav {
2061  position: fixed;
2062  bottom: 0;
2063  left: 0;
2064  right: 0;
2065  background: #ffffff;
2066  border-top: 1px solid #e5e7eb;
2067  z-index: 9998; /* Below chat widget */
2068  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
2069  display: none; /* Hidden on desktop */
2070}
2071
2072.bottom-nav-inner {
2073  display: flex;
2074  justify-content: space-around;
2075  align-items: center;
2076  padding: 2px 0;
2077  max-width: 640px;
2078  margin: 0 auto;
2079}
2080
2081.bottom-nav-item {
2082  flex: 1;
2083  display: flex;
2084  flex-direction: column;
2085  align-items: center;
2086  justify-content: center;
2087  gap: 4px;
2088  padding: 8px 8px;
2089  background: none;
2090  border: none;
2091  cursor: pointer;
2092  text-decoration: none;
2093  color: #6b7280;
2094  transition: all 0.3s ease;
2095  position: relative;
2096}
2097
2098.bottom-nav-item:hover {
2099  color: #C10DFF;
2100}
2101
2102.bottom-nav-item.active {
2103  flex-direction: row;
2104  gap: 8px;
2105  background: linear-gradient(135deg, #C10DFF 0%, #C10DFF 100%);
2106  color: white;
2107  border-radius: 20px;
2108  padding: 8px 16px;
2109  box-shadow: 0 2px 8px rgba(245,202,86,0.3);
2110}
2111
2112.bottom-nav-item-icon {
2113  width: 24px;
2114  height: 24px;
2115  position: relative;
2116}
2117
2118.bottom-nav-item-icon svg {
2119  width: 100%;
2120  height: 100%;
2121}
2122
2123/* Icon variant switching */
2124.bottom-nav-item .icon-outlined {
2125  display: block;
2126}
2127
2128.bottom-nav-item .icon-filled {
2129  display: none;
2130}
2131
2132.bottom-nav-item.active .icon-outlined {
2133  display: none;
2134}
2135
2136.bottom-nav-item.active .icon-filled {
2137  display: block;
2138}
2139
2140.bottom-nav-item-label {
2141  font-size: 12px;
2142  font-weight: 600;
2143  line-height: 1;
2144  display: none;
2145  white-space: nowrap;
2146}
2147
2148.bottom-nav-item.active .bottom-nav-item-label {
2149  display: block;
2150}
2151
2152/* Avatar styles for bottom-nav */
2153.bottom-nav-avatar-img,
2154.bottom-nav-avatar-initials {
2155  width: 24px;
2156  height: 24px;
2157  border-radius: 50%;
2158  display: block;
2159}
2160
2161.bottom-nav-avatar-img {
2162  object-fit: cover;
2163}
2164
2165.bottom-nav-avatar-initials {
2166  background: linear-gradient(135deg, #C10DFF 0%, #C10DFF 100%);
2167  color: white;
2168  font-size: 10px;
2169  font-weight: 700;
2170  display: flex;
2171  align-items: center;
2172  justify-content: center;
2173  line-height: 1;
2174  letter-spacing: 0.5px;
2175}
2176
2177/* Hide SVG when avatar is shown */
2178.bottom-nav-item-icon:has(.bottom-nav-avatar-img) .bottom-nav-svg-icon,
2179.bottom-nav-item-icon:has(.bottom-nav-avatar-initials) .bottom-nav-svg-icon {
2180  display: none;
2181}
2182
2183/* Active state adjustments for avatar */
2184.bottom-nav-item.active .bottom-nav-avatar-initials {
2185  background: white;
2186  color: #C10DFF;
2187  border: 2px solid rgba(255, 255, 255, 0.8);
2188  box-sizing: border-box;
2189}
2190
2191.bottom-nav-item.active .bottom-nav-avatar-img {
2192  border: 2px solid rgba(255, 255, 255, 0.9);
2193  box-sizing: border-box;
2194}
2195
2196.bottom-nav-item-badge {
2197  position: absolute;
2198  top: -4px;
2199  right: -4px;
2200  background: #e2a642;
2201  color: white;
2202  border-radius: 10px;
2203  padding: 2px 5px;
2204  font-size: 10px;
2205  font-weight: 600;
2206  min-width: 18px;
2207  height: 18px;
2208  display: flex;
2209  align-items: center;
2210  justify-content: center;
2211  line-height: 1;
2212}
2213
2214/* Show on mobile only */
2215@media (max-width: 640px) {
2216  .bottom-nav {
2217    display: block;
2218  }
2219  
2220  /* Add bottom padding to body to prevent content being hidden */
2221  body {
2222    padding-bottom: 60px;
2223  }
2224}
2225
2226/* Mobile User Sheet - Bottom Sheet for Profile Menu */
2227.mobile-user-sheet {
2228  position: fixed;
2229  bottom: 0;
2230  left: 0;
2231  right: 0;
2232  z-index: 10000;
2233  pointer-events: none;
2234}
2235
2236.mobile-user-sheet:not([hidden]) {
2237  pointer-events: auto;
2238}
2239
2240.mobile-user-sheet-backdrop {
2241  position: fixed;
2242  inset: 0;
2243  background: rgba(0, 0, 0, 0.5);
2244  opacity: 0;
2245  transition: opacity 0.2s ease-out;
2246}
2247
2248.mobile-user-sheet.active .mobile-user-sheet-backdrop {
2249  opacity: 1;
2250}
2251
2252.mobile-user-sheet-content {
2253  position: absolute;
2254  bottom: 0;
2255  left: 0;
2256  right: 0;
2257  background: white;
2258  border-radius: 16px 16px 0 0;
2259  max-height: 80vh;
2260  overflow-y: auto;
2261  transform: translateY(100%);
2262  transition: transform 0.3s ease-out;
2263}
2264
2265.mobile-user-sheet.active .mobile-user-sheet-content {
2266  transform: translateY(0);
2267}
2268
2269.mobile-user-sheet-handle {
2270  width: 40px;
2271  height: 4px;
2272  background: #d1d5db;
2273  border-radius: 2px;
2274  margin: 12px auto 16px;
2275}
2276
2277.mobile-user-sheet-header {
2278  padding: 0 20px 16px;
2279  border-bottom: 1px solid #e5e7eb;
2280}
2281
2282.mobile-user-info {
2283  display: flex;
2284  align-items: center;
2285  gap: 12px;
2286}
2287
2288.mobile-user-avatar {
2289  width: 48px;
2290  height: 48px;
2291  background: linear-gradient(135deg, #335DD9 0%, #fff1cc 50%, #C10DFF 100%);
2292  border-radius: 50%;
2293  display: flex;
2294  align-items: center;
2295  justify-content: center;
2296  font-size: 24px;
2297  color: #C10DFF;
2298  font-weight: 600;
2299}
2300
2301.mobile-user-details {
2302  flex: 1;
2303}
2304
2305.mobile-user-name {
2306  font-weight: 600;
2307  font-size: 16px;
2308  color: #1f2937;
2309  margin-bottom: 4px;
2310}
2311
2312.mobile-user-email {
2313  font-size: 13px;
2314  color: #6b7280;
2315}
2316
2317.mobile-user-sheet-body {
2318  padding: 8px 0;
2319}
2320
2321.mobile-user-sheet-item {
2322  display: flex;
2323  align-items: center;
2324  gap: 12px;
2325  padding: 14px 20px;
2326  background: none;
2327  border: none;
2328  width: 100%;
2329  text-align: left;
2330  cursor: pointer;
2331  text-decoration: none;
2332  color: #374151;
2333  transition: background 0.2s;
2334}
2335
2336.mobile-user-sheet-item:hover {
2337  background: #f9fafb;
2338}
2339
2340.mobile-user-sheet-icon {
2341  font-size: 20px;
2342  width: 24px;
2343  text-align: center;
2344}
2345
2346.mobile-user-sheet-label {
2347  font-size: 15px;
2348  font-weight: 500;
2349}
2350
2351.mobile-user-sheet-divider {
2352  height: 1px;
2353  background: #e5e7eb;
2354  margin: 8px 0;
2355}
2356
2357@keyframes fadeIn {
2358  from {
2359    opacity: 0;
2360  }
2361  to {
2362    opacity: 1;
2363  }
2364}
2365
2366@keyframes slideUp {
2367  from {
2368    transform: translateY(100%);
2369  }
2370  to {
2371    transform: translateY(0);
2372  }
2373}
2374
2375/* Ensure chat widget works well with bottom nav */
2376@media (max-width: 640px) {
2377  .chat-widget {
2378    bottom: 0 !important; /* Chat opens from bottom */
2379  }
2380  
2381  .chat-popup {
2382    /* Chat popup should be above bottom nav */
2383    z-index: 9999;
2384  }
2385}
2386
2387</style>
2388  <style data-inlined="search-suggest.css">
2389.search { 
2390  position: relative; 
2391}
2392
2393/* AI Bot Toggle Button */
2394.ai-bot-toggle {
2395  display: flex;
2396  align-items: center;
2397  justify-content: center;
2398  background: transparent;
2399  border: none;
2400  padding: 8px;
2401  cursor: pointer;
2402  border-radius: 8px;
2403  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
2404  margin-left: 4px;
2405  flex-shrink: 0;
2406  display: none;
2407}
2408
2409.ai-bot-toggle .bot-icon {
2410  color: #9ca3af;
2411  transition: all 0.3s ease;
2412}
2413
2414.ai-bot-toggle:hover .bot-icon {
2415  color: #6b7280;
2416  transform: scale(1.1);
2417}
2418
2419.ai-bot-toggle.active .bot-icon {
2420  color: #C10DFF;
2421  animation: botPulse 2s ease-in-out infinite;
2422}
2423
2424.ai-bot-toggle.active {
2425  background: rgba(245,202,86,0.1);
2426}
2427
2428.ai-bot-toggle.active:hover .bot-icon {
2429  color: #C10DFF;
2430}
2431
2432@keyframes botPulse {
2433  0%, 100% { transform: scale(1); }
2434  50% { transform: scale(1.12); }
2435}
2436
2437/* Search Suggest Box */
2438.search-suggest {
2439  position: absolute;
2440  top: 100%; left: 0; right: 0;
2441  background: #fff;
2442  border: 1px solid #e1e4e8;
2443  border-top: none;
2444  box-shadow: 0 4px 12px rgba(0,0,0,0.08);
2445  max-height: 400px;
2446  overflow-y: auto;
2447  z-index: 100;
2448  padding: 0;
2449  font-size: 14px;
2450  border-radius: 0 0 8px 8px;
2451}
2452
2453.suggest-item {
2454  display: flex;
2455  align-items: center;
2456  gap: 8px;
2457  padding: 10px 16px;
2458  cursor: pointer;
2459  transition: all 0.2s ease;
2460  border-bottom: 1px solid #f1f5f9;
2461}
2462
2463.suggest-item:last-child {
2464  border-bottom: none;
2465}
2466
2467.suggest-item:hover,
2468.suggest-item.active {
2469  background: linear-gradient(90deg, #f8fafc 0%, #f1f5f9 100%);
2470  padding-left: 20px;
2471}
2472
2473.suggest-thumb {
2474  width: 42px;
2475  height: 42px;
2476  object-fit: cover;
2477  border: 1px solid #ddd;
2478  border-radius: 4px;
2479  background: #fafafa;
2480}
2481
2482.suggest-meta { flex: 1; min-width: 0; }
2483
2484.suggest-name {
2485  font-weight: 500;
2486  color: #222;
2487  line-height: 1.2;
2488  white-space: nowrap;
2489  overflow: hidden;
2490  text-overflow: ellipsis;
2491}
2492
2493.suggest-price {
2494  color: #e2a642;
2495  font-size: 12px;
2496  margin-top: 2px;
2497}
2498
2499/* AI Insights */
2500.suggest-ai-insight {
2501  display: flex;
2502  align-items: center;
2503  gap: 8px;
2504  padding: 8px 12px;
2505  background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
2506  border-bottom: 1px solid #bae6fd;
2507  color: #0369a1;
2508  font-size: 12px;
2509  font-weight: 500;
2510  line-height: 1.4;
2511}
2512
2513.suggest-ai-insight svg {
2514  flex-shrink: 0;
2515  opacity: 0.8;
2516}
2517
2518.suggest-ai-insight span {
2519  flex: 1;
2520}
2521
2522/* AI Progress States */
2523.ai-progress-container {
2524  padding: 20px 16px;
2525  background: linear-gradient(135deg, #fafbff 0%, #f5f7ff 100%);
2526}
2527
2528.ai-progress-header {
2529  display: flex;
2530  align-items: center;
2531  gap: 10px;
2532  margin-bottom: 16px;
2533}
2534
2535.ai-progress-icon {
2536  width: 36px;
2537  height: 36px;
2538  display: flex;
2539  align-items: center;
2540  justify-content: center;
2541  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
2542  border-radius: 50%;
2543  color: #fff;
2544  font-size: 18px;
2545  animation: aiIconSpin 3s linear infinite;
2546}
2547
2548@keyframes aiIconSpin {
2549  0% { transform: rotate(0deg); }
2550  100% { transform: rotate(360deg); }
2551}
2552
2553.ai-progress-text {
2554  flex: 1;
2555}
2556
2557.ai-progress-title {
2558  font-weight: 600;
2559  color: #1e293b;
2560  font-size: 14px;
2561  margin-bottom: 2px;
2562}
2563
2564.ai-progress-subtitle {
2565  font-size: 12px;
2566  color: #64748b;
2567}
2568
2569/* Progress Steps */
2570.ai-progress-steps {
2571  display: flex;
2572  flex-direction: column;
2573  gap: 10px;
2574  margin-top: 16px;
2575}
2576
2577.ai-progress-step {
2578  display: flex;
2579  align-items: center;
2580  gap: 10px;
2581  padding: 8px 12px;
2582  background: #fff;
2583  border-radius: 8px;
2584  border: 1px solid #e2e8f0;
2585  transition: all 0.3s ease;
2586}
2587
2588.ai-progress-step.active {
2589  border-color: #667eea;
2590  background: #f8f9ff;
2591  box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1);
2592}
2593
2594.ai-progress-step.completed {
2595  border-color: #10b981;
2596  background: #f0fdf4;
2597}
2598
2599.ai-step-icon {
2600  width: 20px;
2601  height: 20px;
2602  display: flex;
2603  align-items: center;
2604  justify-content: center;
2605  border-radius: 50%;
2606  font-size: 11px;
2607  flex-shrink: 0;
2608}
2609
2610.ai-progress-step.active .ai-step-icon {
2611  background: #667eea;
2612  color: #fff;
2613  animation: pulse 1.5s ease-in-out infinite;
2614}
2615
2616.ai-progress-step.completed .ai-step-icon {
2617  background: #10b981;
2618  color: #fff;
2619}
2620
2621.ai-progress-step.pending .ai-step-icon {
2622  background: #e2e8f0;
2623  color: #94a3b8;
2624}
2625
2626.ai-step-text {
2627  flex: 1;
2628  font-size: 13px;
2629  color: #475569;
2630  font-weight: 500;
2631}
2632
2633.ai-progress-step.completed .ai-step-text {
2634  color: #059669;
2635}
2636
2637/* AI Analysis Result Banner */
2638.ai-result-banner {
2639  padding: 12px 16px;
2640  background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
2641  border-bottom: 1px solid #86efac;
2642  display: flex;
2643  align-items: center;
2644  gap: 10px;
2645}
2646
2647.ai-result-icon {
2648  font-size: 20px;
2649  animation: bounceIn 0.5s ease;
2650}
2651
2652@keyframes bounceIn {
2653  0% { transform: scale(0); opacity: 0; }
2654  50% { transform: scale(1.1); }
2655  100% { transform: scale(1); opacity: 1; }
2656}
2657
2658.ai-result-text {
2659  flex: 1;
2660}
2661
2662.ai-result-title {
2663  font-weight: 600;
2664  color: #065f46;
2665  font-size: 13px;
2666  margin-bottom: 2px;
2667}
2668
2669.ai-result-description {
2670  font-size: 12px;
2671  color: #047857;
2672  opacity: 0.9;
2673}
2674
2675/* Product Items Section Header */
2676.suggest-section-header {
2677  padding: 10px 16px;
2678  background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
2679  border-bottom: 1px solid #e2e8f0;
2680}
2681
2682.suggest-header-main {
2683  display: flex;
2684  align-items: center;
2685  gap: 6px;
2686  margin-bottom: 4px;
2687}
2688
2689.suggest-header-icon {
2690  font-size: 14px;
2691}
2692
2693.suggest-header-text {
2694  font-size: 12px;
2695  font-weight: 600;
2696  color: #64748b;
2697  text-transform: uppercase;
2698  letter-spacing: 0.5px;
2699}
2700
2701.suggest-header-hint {
2702  font-size: 11px;
2703  color: #94a3b8;
2704  font-weight: 400;
2705  text-transform: none;
2706  letter-spacing: 0;
2707  display: flex;
2708  align-items: center;
2709  gap: 4px;
2710}
2711
2712.suggest-header-hint kbd {
2713  display: inline-block;
2714  padding: 2px 6px;
2715  font-size: 10px;
2716  font-weight: 600;
2717  line-height: 1;
2718  color: #475569;
2719  background: #fff;
2720  border: 1px solid #cbd5e1;
2721  border-radius: 4px;
2722  box-shadow: 0 1px 2px rgba(0,0,0,0.05);
2723  font-family: 'Segoe UI', monospace;
2724}
2725
2726/* Footer Hint */
2727.suggest-footer-hint {
2728  display: flex;
2729  align-items: center;
2730  justify-content: center;
2731  padding: 12px 16px;
2732  background: linear-gradient(135deg, #fef3c7 0%, #C10DFF 100%);
2733  border-top: 1px solid #C10DFF;
2734  color: #ae7e21;
2735  font-size: 12px;
2736  font-weight: 500;
2737  text-align: center;
2738  cursor: default;
2739}
2740
2741.suggest-footer-hint svg {
2742  color: #C10DFF;
2743  flex-shrink: 0;
2744}
2745
2746.suggest-footer-hint kbd {
2747  display: inline-block;
2748  padding: 3px 7px;
2749  margin: 0 3px;
2750  font-size: 11px;
2751  font-weight: 600;
2752  line-height: 1;
2753  color: #765212;
2754  background: #fff;
2755  border: 1px solid #C10DFF;
2756  border-radius: 4px;
2757  box-shadow: 0 1px 3px rgba(245,202,86,0.2);
2758  font-family: 'Segoe UI', monospace;
2759}
2760
2761.suggest-footer-link {
2762  color: #C10DFF;
2763  font-weight: 600;
2764  text-decoration: underline;
2765  text-underline-offset: 2px;
2766  text-decoration-thickness: 1px;
2767  cursor: pointer;
2768  transition: all 0.2s ease;
2769}
2770
2771.suggest-footer-link:hover {
2772  color: #c9832a;
2773  text-decoration-thickness: 2px;
2774  text-shadow: 0 0 8px rgba(245,202,86,0.3);
2775}
2776
2777/* Loading state */
2778.suggest-loading {
2779  display: flex;
2780  align-items: center;
2781  justify-content: center;
2782  gap: 8px;
2783  padding: 16px 12px;
2784  color: #64748b;
2785  font-size: 13px;
2786}
2787
2788@keyframes pulse {
2789  0%, 100% { opacity: 1; }
2790  50% { opacity: 0.5; }
2791}
2792
2793.suggest-loading {
2794  animation: pulse 1.5s ease-in-out infinite;
2795}
2796
2797/* Responsive Mobile Styles */
2798@media (max-width: 768px) {
2799  .ai-bot-toggle {
2800    padding: 6px;
2801    margin-left: 0;
2802  }
2803  
2804  .ai-bot-toggle .bot-icon {
2805    width: 20px;
2806    height: 20px;
2807  }
2808  
2809  .search-icon-btn {
2810    margin-right: 4px;
2811  }
2812  
2813  .search-icon-btn svg {
2814    width: 18px;
2815    height: 18px;
2816  }
2817  
2818  .search-suggest {
2819    max-height: 350px;
2820  }
2821  
2822  /* Mobile hint adjustments */
2823  .suggest-header-hint {
2824    font-size: 10px;
2825  }
2826  
2827  .suggest-header-hint kbd {
2828    padding: 1px 4px;
2829    font-size: 9px;
2830  }
2831  
2832  .suggest-footer-hint {
2833    padding: 10px 12px;
2834    font-size: 11px;
2835  }
2836  
2837  .suggest-footer-hint svg {
2838    width: 14px;
2839    height: 14px;
2840  }
2841  
2842  .suggest-footer-hint kbd {
2843    padding: 2px 5px;
2844    font-size: 10px;
2845  }
2846  
2847  .ai-progress-container {
2848    padding: 16px 12px;
2849  }
2850  
2851  .ai-progress-icon {
2852    width: 32px;
2853    height: 32px;
2854    font-size: 16px;
2855  }
2856  
2857  .ai-progress-title {
2858    font-size: 13px;
2859  }
2860  
2861  .ai-progress-subtitle {
2862    font-size: 11px;
2863  }
2864  
2865  .ai-progress-step {
2866    padding: 6px 10px;
2867  }
2868  
2869  .ai-step-text {
2870    font-size: 12px;
2871  }
2872  
2873  .suggest-item {
2874    padding: 8px 12px;
2875  }
2876  
2877  .suggest-thumb {
2878    width: 36px;
2879    height: 36px;
2880  }
2881  
2882  .suggest-name {
2883    font-size: 13px;
2884  }
2885  
2886  .suggest-price {
2887    font-size: 11px;
2888  }
2889}
2890
2891@media (max-width: 480px) {
2892  .ai-bot-toggle {
2893    padding: 4px;
2894  }
2895  
2896  .ai-bot-toggle .bot-icon {
2897    width: 18px;
2898    height: 18px;
2899  }
2900  
2901  .search-icon-btn svg {
2902    width: 16px;
2903    height: 16px;
2904  }
2905  
2906  /* Ultra-compact hints for small screens */
2907  .suggest-header-hint {
2908    display: none; /* Hide header hint on very small screens */
2909  }
2910  
2911  .suggest-footer-hint {
2912    padding: 8px 10px;
2913    font-size: 10px;
2914    flex-direction: column;
2915    gap: 4px;
2916  }
2917  
2918  .suggest-footer-hint svg {
2919    display: none; /* Hide icon to save space */
2920  }
2921  
2922  .suggest-footer-hint kbd {
2923    padding: 2px 4px;
2924    font-size: 9px;
2925  }
2926  
2927  .ai-progress-steps {
2928    gap: 8px;
2929  }
2930  
2931  .ai-step-text {
2932    font-size: 11px;
2933  }
2934}
2935
2936/* AI Search Prompt Box */
2937.ai-search-prompt {
2938  padding: 24px;
2939  background: linear-gradient(135deg, #fff5f0 0%, #ffffff 100%);
2940}
2941
2942.ai-prompt-header {
2943  display: flex;
2944  align-items: center;
2945  gap: 12px;
2946  margin-bottom: 16px;
2947}
2948
2949.ai-prompt-icon {
2950  width: 48px;
2951  height: 48px;
2952  background: linear-gradient(135deg, #C10DFF 0%, #C10DFF 100%);
2953  border-radius: 12px;
2954  display: flex;
2955  align-items: center;
2956  justify-content: center;
2957  font-size: 24px;
2958  box-shadow: 0 4px 12px rgba(245,202,86,0.3);
2959}
2960
2961.ai-prompt-text {
2962  flex: 1;
2963}
2964
2965.ai-prompt-title {
2966  font-size: 16px;
2967  font-weight: 700;
2968  color: #1f2937;
2969  margin-bottom: 4px;
2970}
2971
2972.ai-prompt-subtitle {
2973  font-size: 13px;
2974  color: #6b7280;
2975}
2976
2977.ai-prompt-query {
2978  background: white;
2979  border: 2px solid #C10DFF;
2980  border-radius: 8px;
2981  padding: 12px 16px;
2982  margin-bottom: 16px;
2983}
2984
2985.ai-query-label {
2986  font-size: 11px;
2987  font-weight: 600;
2988  color: #C10DFF;
2989  text-transform: uppercase;
2990  letter-spacing: 0.5px;
2991  margin-bottom: 4px;
2992}
2993
2994.ai-query-text {
2995  font-size: 15px;
2996  font-weight: 600;
2997  color: #1f2937;
2998}
2999
3000.ai-search-trigger-btn {
3001  width: 100%;
3002  padding: 14px 20px;
3003  background: linear-gradient(135deg, #C10DFF 0%, #C10DFF 100%);
3004  color: white;
3005  border: none;
3006  border-radius: 10px;
3007  font-size: 15px;
3008  font-weight: 700;
3009  cursor: pointer;
3010  display: flex;
3011  align-items: center;
3012  justify-content: center;
3013  gap: 8px;
3014  transition: all 0.3s ease;
3015  box-shadow: 0 4px 12px rgba(245,202,86,0.4);
3016}
3017
3018.ai-search-trigger-btn:hover {
3019  transform: translateY(-2px);
3020  box-shadow: 0 6px 20px rgba(245,202,86,0.5);
3021}
3022
3023.ai-search-trigger-btn:active {
3024  transform: translateY(0);
3025}
3026
3027.ai-btn-icon {
3028  font-size: 18px;
3029  animation: sparkle 2s ease-in-out infinite;
3030}
3031
3032@keyframes sparkle {
3033  0%, 100% { opacity: 1; transform: scale(1); }
3034  50% { opacity: 0.7; transform: scale(1.2); }
3035}
3036
3037.ai-btn-text {
3038  font-size: 15px;
3039}
3040
3041.ai-prompt-footer {
3042  margin-top: 12px;
3043  text-align: center;
3044  font-size: 12px;
3045  color: #9ca3af;
3046  font-style: italic;
3047}
3048
3049/* ==================== TRENDING SEARCH STYLES ==================== */
3050
3051.search-suggest.trending-mode {
3052  border-top: 3px solid #C10DFF;
3053  max-width: 900px;
3054  min-width: 600px;
3055}
3056
3057/* Two-column container */
3058.trending-container {
3059  display: flex;
3060  gap: 0;
3061  padding: 0 !important;
3062}
3063
3064.trending-column {
3065  flex: 1;
3066  min-width: 0;
3067  padding: 0 !important;
3068}
3069
3070.trending-column.trending-searches {
3071  border-right: 2px solid #e2e8f0; padding: 0 !important;
3072}
3073
3074/* Mobile: stack columns vertically */
3075@media (max-width: 768px) {
3076  .search-suggest.trending-mode {
3077    min-width: auto;
3078    max-width: 100%;
3079  }
3080  
3081  .trending-container {
3082    flex-direction: column;
3083  }
3084  
3085  .trending-column.trending-searches {
3086    border-right: none;
3087    border-bottom: 2px solid #e2e8f0;
3088  }
3089}
3090
3091.trending-header {
3092  display: flex;
3093  align-items: center;
3094  gap: 8px;
3095  padding: 14px 16px;
3096  border-bottom: 1px solid #e2e8f0;
3097  background: linear-gradient(135deg, #C10DFF 0%, #C10DFF 100%);
3098  color: white;
3099  position: sticky;
3100  top: 0;
3101  z-index: 10;
3102}
3103
3104.trending-icon {
3105  font-size: 20px;
3106  animation: trendingPulse 2s infinite;
3107}
3108
3109@keyframes trendingPulse {
3110  0%, 100% { 
3111    transform: scale(1); 
3112    filter: drop-shadow(0 0 0 rgba(245,202,86,0));
3113  }
3114  50% { 
3115    transform: scale(1.15); 
3116    filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.8));
3117  }
3118}
3119
3120.trending-title {
3121  font-weight: 700;
3122  font-size: 14px;
3123  letter-spacing: 0.3px;
3124}
3125
3126.trending-subtitle {
3127  font-size: 11px;
3128  opacity: 0.9;
3129  margin-left: auto;
3130  font-style: italic;
3131}
3132
3133/* Trending searches item */
3134.trending-item {
3135  display: flex;
3136  align-items: center;
3137  gap: 12px;
3138  padding: 12px 16px;
3139  cursor: pointer;
3140  transition: all 0.2s ease;
3141  border-bottom: 1px solid #f1f5f9;
3142  position: relative;
3143}
3144
3145.trending-item:hover {
3146  background: linear-gradient(90deg, #fff7ed 0%, #ffedd5 100%);
3147  padding-left: 20px;
3148}
3149
3150.trending-item:hover::before {
3151  content: '';
3152  position: absolute;
3153  left: 0;
3154  top: 0;
3155  bottom: 0;
3156  width: 4px;
3157  background: linear-gradient(180deg, #C10DFF 0%, #C10DFF 100%);
3158}
3159
3160.trending-item:last-child {
3161  border-bottom: none;
3162}
3163
3164.trending-rank {
3165  font-size: 20px;
3166  flex-shrink: 0;
3167  width: 32px;
3168  text-align: center;
3169}
3170
3171.trending-query {
3172  flex: 1;
3173  font-size: 14px;
3174  color: #1f2937;
3175  font-weight: 500;
3176  line-height: 1.4;
3177}
3178
3179.trending-category {
3180  font-size: 11px;
3181  color: #C10DFF;
3182  background: #fff7ed;
3183  padding: 3px 10px;
3184  border-radius: 12px;
3185  white-space: nowrap;
3186  font-weight: 500;
3187  border: 1px solid #fff1cc;
3188}
3189
3190/* Trending products item */
3191.trending-product-item {
3192  display: flex;
3193  align-items: center;
3194  gap: 12px;
3195  padding: 12px 16px;
3196  cursor: pointer;
3197  transition: all 0.2s ease;
3198  border-bottom: 1px solid #f1f5f9;
3199  position: relative;
3200}
3201
3202.trending-product-item:hover {
3203  background: linear-gradient(90deg, #fef3c7 0%, #C10DFF 100%);
3204  padding-left: 20px;
3205}
3206
3207.trending-product-item:hover::before {
3208  content: '';
3209  position: absolute;
3210  left: 0;
3211  top: 0;
3212  bottom: 0;
3213  width: 4px;
3214  background: linear-gradient(180deg, #C10DFF 0%, #C10DFF 100%);
3215}
3216
3217.trending-product-item:last-child {
3218  border-bottom: none;
3219}
3220
3221.trending-product-thumb {
3222  width: 48px;
3223  height: 48px;
3224  object-fit: cover;
3225  border-radius: 8px;
3226  border: 1px solid #e5e7eb;
3227  flex-shrink: 0;
3228  background: white;
3229}
3230
3231.trending-product-info {
3232  flex: 1;
3233  min-width: 0;
3234}
3235
3236.trending-product-name {
3237  font-size: 13px;
3238  font-weight: 400;
3239  color: #1f2937;
3240  line-height: 1.4;
3241  margin-bottom: 0;
3242  padding: 0 !important;
3243  overflow: hidden;
3244  text-overflow: ellipsis;
3245  display: -webkit-box;
3246  -webkit-line-clamp: 2;
3247  -webkit-box-orient: vertical;
3248}
3249
3250.trending-product-price {
3251  font-size: 12px;
3252  font-weight: 700;
3253  color: #C10DFF;
3254  padding: 4px 0 !important;
3255}
3256.trending-product { padding: 0;}
3257/* ==================== END TRENDING STYLES ==================== */
3258
3259</style>
3260  <style data-inlined="product-detail.css">
3261/* === PRODUCT DETAIL v3 (Tokopedia-like) — Full stylesheet === */
3262
3263/* Root / Container */
3264.product-single {
3265  --pd-gap: 2rem;
3266  --pd-border: #e2e8f0;
3267  --pd-bg: #ffffff;
3268  --pd-muted: #64748b;
3269  --pd-dark: #0f172a;
3270  --pd-accent: #0d6efd; /* tema utama situs */
3271  --pd-radius: 12px;
3272  --pd-soft: #f8fafc;
3273  --pd-soft-border: #e5e7eb;
3274  --pd-danger: #e2a642;
3275  --pd-success-bg: #dcfce7;
3276  --pd-success-text: #166534;
3277  --pd-danger-bg: #fee2e2;
3278  --pd-danger-text: #c9832a;
3279  --pd-shadow: 0 6px 20px -10px rgba(0,0,0,.12);
3280  --pd-transition: .25s cubic-bezier(.4,.0,.2,1);
3281  max-width:1280px;
3282  margin-inline:auto;
3283}
3284
3285/* === Sticky Product Header Bar (muncul saat scroll) === */
3286.pd-sticky-header {
3287  position: fixed;
3288  top: 0;
3289  left: 0;
3290  right: 0;
3291  z-index: 998;
3292  background: rgba(255, 255, 255, 0.98);
3293  backdrop-filter: blur(12px);
3294  -webkit-backdrop-filter: blur(12px);
3295  border-bottom: 1px solid rgba(226, 232, 240, 0.8);
3296  box-shadow: 0 4px 16px -4px rgba(0, 0, 0, 0.08);
3297  transform: translateY(-100%);
3298  opacity: 0;
3299  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), 
3300              opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
3301  pointer-events: none;
3302}
3303
3304.pd-sticky-header.visible {
3305  transform: translateY(0);
3306  opacity: 1;
3307  pointer-events: auto;
3308}
3309
3310.pd-sticky-header-container {
3311  max-width: 1280px;
3312  margin: 0 auto;
3313  padding: 12px 20px;
3314  display: flex;
3315  align-items: center;
3316  gap: 16px;
3317}
3318
3319.pd-sticky-thumb {
3320  width: 48px;
3321  height: 48px;
3322  flex-shrink: 0;
3323  border-radius: 8px;
3324  border: 1px solid #e2e8f0;
3325  overflow: hidden;
3326  background: #f8fafc;
3327}
3328
3329.pd-sticky-thumb img {
3330  width: 100%;
3331  height: 100%;
3332  object-fit: cover;
3333  display: block;
3334}
3335
3336.pd-sticky-info {
3337  flex: 1;
3338  min-width: 0;
3339  display: flex;
3340  flex-direction: column;
3341  gap: 4px;
3342}
3343
3344.pd-sticky-title {
3345  font-size: 0.9rem;
3346  font-weight: 600;
3347  color: #0f172a;
3348  line-height: 1.3;
3349  white-space: nowrap;
3350  overflow: hidden;
3351  text-overflow: ellipsis;
3352  margin: 0;
3353}
3354
3355.pd-sticky-price-row {
3356  display: flex;
3357  align-items: center;
3358  gap: 8px;
3359  flex-wrap: wrap;
3360}
3361
3362.pd-sticky-price {
3363  font-size: 1.1rem;
3364  font-weight: 700;
3365  color: #0d6efd;
3366  line-height: 1;
3367}
3368
3369.pd-sticky-price-original {
3370  font-size: 0.75rem;
3371  color: #94a3b8;
3372  text-decoration: line-through;
3373  line-height: 1;
3374}
3375
3376.pd-sticky-discount {
3377  background: #e2a642;
3378  color: #fff;
3379  font-size: 0.65rem;
3380  font-weight: 600;
3381  padding: 2px 6px;
3382  border-radius: 4px;
3383  letter-spacing: 0.3px;
3384  line-height: 1;
3385}
3386
3387.pd-sticky-actions {
3388  display: flex;
3389  gap: 8px;
3390  flex-shrink: 0;
3391  align-items: center;
3392}
3393
3394.pd-sticky-share-btn {
3395  padding: 8px 16px;
3396  border-radius: 8px;
3397  border: 1px solid #e2e8f0;
3398  background: #fff;
3399  color: #64748b;
3400  cursor: pointer;
3401  transition: all 0.2s ease;
3402  display: inline-flex;
3403  align-items: center;
3404  justify-content: center;
3405  flex-shrink: 0;
3406}
3407
3408.pd-sticky-share-btn:hover {
3409  background: #f8fafc;
3410  border-color: #cbd5e1;
3411  color: #0f172a;
3412  transform: translateY(-1px);
3413}
3414
3415.pd-sticky-share-btn:active {
3416  transform: translateY(0);
3417}
3418
3419.pd-sticky-share-btn svg {
3420  display: block;
3421  width: 16px;
3422  height: 16px;
3423}
3424
3425.pd-sticky-btn-chat svg {
3426  width: 16px !important;
3427  height: 16px !important;
3428  max-width: none !important;
3429  margin-right: 4px;
3430  flex-shrink: 0;
3431}
3432
3433.pd-sticky-btn {
3434  padding: 8px 16px;
3435  border-radius: 8px;
3436  font-size: 0.85rem;
3437  font-weight: 600;
3438  cursor: pointer;
3439  transition: all 0.2s ease;
3440  border: 0;
3441  display: inline-flex;
3442  align-items: center;
3443  justify-content: center;
3444  white-space: nowrap;
3445}
3446
3447.pd-sticky-btn-cart {
3448  background: #fff;
3449  color: #0d6efd;
3450  border: 1px solid #0d6efd;
3451}
3452
3453.pd-sticky-btn-cart:hover {
3454  background: rgba(13, 110, 253, 0.06);
3455}
3456
3457.pd-sticky-btn-chat {
3458  background: #fff;
3459  color: #64748b;
3460  border: 1px solid #cbd5e1;
3461  flex: 0.8;
3462}
3463
3464.pd-sticky-btn-chat:hover {
3465  background: #f8fafc;
3466  border-color: #94a3b8;
3467  color: #475569;
3468}
3469
3470.pd-sticky-btn-buy {
3471  background: #0d6efd;
3472  color: #fff;
3473  border: 1px solid #0d6efd;
3474  box-shadow: 0 2px 8px -2px rgba(13, 110, 253, 0.4);
3475}
3476
3477.pd-sticky-btn-buy:hover {
3478  filter: brightness(0.95);
3479}
3480
3481.pd-sticky-btn:disabled {
3482  opacity: 0.5;
3483  cursor: not-allowed;
3484}
3485
3486/* Hide on mobile (use existing FAB instead) */
3487@media (max-width: 880px) {
3488  .pd-sticky-header {
3489    display: none;
3490  }
3491}
3492
3493/* === PRINT STYLES (for PDF Export) === */
3494@media print {
3495  /* Reset & Base */
3496  * {
3497    -webkit-print-color-adjust: exact !important;
3498    print-color-adjust: exact !important;
3499  }
3500  
3501  body {
3502    background: white !important;
3503    margin: 0;
3504    padding: 0;
3505  }
3506  
3507  /* Hide all navigation, buttons, and interactive elements */
3508  nav, header, footer,
3509  nav *, header *, footer *,
3510  .navbar, .navbar *,
3511  .nav-item, .nav-link,
3512  .pd-sticky-header,
3513  .pd-sticky-header.visible,
3514  .pd-breadcrumb,
3515  .pd-sidebar,
3516  .pd-mobile-fab,
3517  .pd-share-btn,
3518  .pd-sticky-share-btn,
3519  #pd-share-btn,
3520  .share-button,
3521  [data-share-btn],
3522  button,
3523  .btn-add-cart,
3524  .btn-buy,
3525  .pd-actions,
3526  .pd-compare-section,
3527  .pd-compare-btn,
3528  .pd-compare-info,
3529  #pd-compare-btn,
3530  .compare-note,
3531  .compare-container,
3532  .compare-dropdown,
3533  .compare-panel,
3534  .compare-wrapper,
3535  [data-compare],
3536  [id*="compare"],
3537  [class*="compare"],
3538  .pd-thumb-gallery,
3539  .pd-installment-card,
3540  .pd-installment-card2,
3541  .inst-alt-banner--compact,
3542  .qoala-banner-card,
3543  .tradein-compact-card,
3544  .pwp-deals-section,
3545  #pwp-deals-section,
3546  .pwp-trigger-badge,
3547  .pd-related-full,
3548  #pd-similar,
3549  .pd-similar-strip,
3550  .pd-scroll-top,
3551  .stock-info-btn,
3552  .stock-popover,
3553  .pd-countdown-modern,
3554  .pd-share-modal-overlay,
3555  [id*="promo-"],
3556  .bottom-nav,
3557  .pd-buy-form,
3558  .pd-buy-card,
3559  .pd-extra-meta,
3560  .pd-reviews-section,
3561  #reviews,
3562  hr,
3563  .divider,
3564  .separator {
3565    display: none !important;
3566    visibility: hidden !important;
3567    opacity: 0 !important;
3568    height: 0 !important;
3569    max-height: 0 !important;
3570    overflow: hidden !important;
3571    margin: 0 !important;
3572    padding: 0 !important;
3573    border: none !important;
3574  }
3575  
3576  /* Extra aggressive reset for any remaining borders */
3577  .pd-info-card *,
3578  .pd-meta-inline *,
3579  .pd-variants * {
3580    border: none !important;
3581    border-top: none !important;
3582    border-bottom: none !important;
3583  }
3584  
3585  /* Layout restructure */
3586  .product-single {
3587    max-width: 100% !important;
3588    margin: 0 !important;
3589    padding: 0 !important;
3590    box-shadow: none !important;
3591  }
3592  
3593  /* Reset all shadows and borders for print */
3594  * {
3595    box-shadow: none !important;
3596  }
3597  
3598  .pd-top-layout {
3599    display: grid !important;
3600    grid-template-columns: 280px 1fr !important;
3601    gap: 24px !important;
3602    margin: 0 0 24px 0 !important;
3603    padding: 0 !important;
3604    page-break-inside: avoid;
3605    align-items: start !important;
3606  }
3607  
3608  /* Print header with logo - centered and larger */
3609  .product-single::before {
3610    content: "" !important;
3611    display: block !important;
3612    width: 150px !important;
3613    height: 50px !important;
3614    background: url('/assets/img/logo.png') no-repeat center !important;
3615    background-size: contain !important;
3616    margin: 0 auto 20px !important;
3617    visibility: visible !important;
3618    opacity: 1 !important;
3619  }
3620  
3621  /* Media card - left column */
3622  .pd-media-card {
3623    border: none !important;
3624    padding: 0 !important;
3625    margin: 0 !important;
3626    text-align: center;
3627    page-break-inside: avoid;
3628  }
3629  
3630  /* Hide desktop image, use mobile slider for both desktop and mobile */
3631  .pd-main-image {
3632    display: none !important;
3633  }
3634  
3635  /* Mobile slider - show first image only in print */
3636  .pd-mobile-slider {
3637    display: block !important;
3638    overflow: visible !important;
3639  }
3640  
3641  .pd-mslide {
3642    display: none !important;
3643  }
3644  
3645  .pd-mslide:first-child {
3646    display: block !important;
3647    border: 1px solid #e2e8f0 !important;
3648    max-width: 280px !important;
3649    margin: 0 auto !important;
3650    border-radius: 8px !important;
3651  }
3652  
3653  .pd-mslide:first-child img {
3654    max-height: 280px !important;
3655    width: 100% !important;
3656    height: auto !important;
3657    object-fit: contain !important;
3658  }
3659  
3660  /* Info card - right column */
3661  .pd-info-card {
3662    border: none !important;
3663    border-top: none !important;
3664    border-bottom: none !important;
3665    padding: 0 !important;
3666    margin: 0 !important;
3667    background: white !important;
3668    box-shadow: none !important;
3669  }
3670  
3671  /* Title */
3672  .pd-title-row {
3673    display: block !important;
3674    margin-bottom: 10px !important;
3675    border: none !important;
3676    border-top: none !important;
3677    border-bottom: none !important;
3678    padding: 0 !important;
3679    box-shadow: none !important;
3680    text-decoration: none !important;
3681  }
3682  
3683  /* Hide share button in print */
3684  .pd-title-row .pd-share-btn,
3685  .pd-title-row #pd-share-btn {
3686    display: none !important;
3687  }
3688  
3689  .pd-title {
3690    font-size: 13pt !important;
3691    color: #000 !important;
3692    margin: 0 0 10px 0 !important;
3693    page-break-after: avoid;
3694    font-weight: 700 !important;
3695    line-height: 1.3 !important;
3696    border: none !important;
3697    border-top: none !important;
3698    border-bottom: none !important;
3699    padding-bottom: 0 !important;
3700    box-shadow: none !important;
3701    text-decoration: none !important;
3702  }
3703  
3704  /* Price and QR container - side by side */
3705  .pd-price-qr-container {
3706    display: flex !important;
3707    gap: 16px !important;
3708    align-items: flex-start !important;
3709    margin-bottom: 14px !important;
3710    box-shadow: none !important;
3711    border: none !important;
3712    text-decoration: none !important;
3713  }
3714  
3715  /* Price block - left side */
3716  .pd-price-block {
3717    flex: 1 !important;
3718    margin: 0 !important;
3719    padding-bottom: 0 !important;
3720    border-bottom: none !important;
3721    box-shadow: none !important;
3722  }
3723  
3724  .pd-price-row {
3725    margin-bottom: 8px !important;
3726    border: none !important;
3727    border-top: none !important;
3728    border-bottom: none !important;
3729    padding: 0 !important;
3730  }
3731  
3732  /* Hide all empty or unnecessary elements in price row */
3733  .pd-price-row > span:empty,
3734  .pd-price-row > .sold-inline,
3735  #pd-sold-inline {
3736    display: none !important;
3737  }
3738  
3739  .pd-price-current {
3740    font-size: 18pt !important;
3741    color: #0d6efd !important;
3742  }
3743  
3744  /* Show price range for variant products in print */
3745  .pd-price-current[data-price-range]::after {
3746    content: attr(data-price-range) !important;
3747    display: block !important;
3748    font-size: 11pt !important;
3749    color: #64748b !important;
3750    margin-top: 4px !important;
3751    font-weight: normal !important;
3752  }
3753  
3754  /* Hide 'Range:' prefix text */
3755  .pd-price-current[data-price-range]::after {
3756    content: attr(data-price-range) !important;
3757  }
3758  
3759  .pd-price-original {
3760    font-size: 12pt !important;
3761    color: #64748b !important;
3762  }
3763  
3764  .pd-discount-badge {
3765    font-size: 10pt !important;
3766    padding: 2px 6px !important;
3767  }
3768  
3769  /* QR Code section - right side beside price */
3770  .pd-qr-print {
3771    display: block !important;
3772    flex: 0 0 100px !important;
3773    margin: 0 !important;
3774    padding: 8px !important;
3775    background: #f8fafc !important;
3776    border-radius: 8px !important;
3777    text-align: center !important;
3778    page-break-inside: avoid !important;
3779    border: 1px solid #cbd5e1 !important;
3780  }
3781  
3782  .pd-qr-label {
3783    font-size: 6.5pt !important;
3784    color: #64748b !important;
3785    margin-bottom: 5px !important;
3786    line-height: 1.2 !important;
3787    font-weight: 600 !important;
3788  }
3789  
3790  .pd-qr-image {
3791    width: 80px !important;
3792    height: 80px !important;
3793    display: block !important;
3794    margin: 0 auto !important;
3795  }
3796  
3797  /* Meta info */
3798  .pd-meta-inline {
3799    display: flex !important;
3800    gap: 12px !important;
3801    margin-bottom: 0 !important;
3802    padding-bottom: 14px !important;
3803    font-size: 10pt !important;
3804    border: none !important;
3805    border-top: none !important;
3806    border-bottom: none !important;
3807    padding-top: 0 !important;
3808    box-shadow: none !important;
3809    text-decoration: none !important;
3810    background: transparent !important;
3811  }
3812  
3813  /* Hide stock badge in print */
3814  .stock-badge,
3815  .stock-info-btn {
3816    display: none !important;
3817  }
3818  
3819  .sku {
3820    color: #64748b !important;
3821  }
3822  
3823  /* Hide sold badge if empty/zero */
3824  .sold-inline {
3825    display: none !important;
3826  }
3827  
3828  /* Hide variant summary section in print */
3829  .pd-variant-combined,
3830  #pd-variant-combined,
3831  .pd-variant-summary,
3832  [data-role="sold-chip"] {
3833    display: none !important;
3834  }
3835  
3836  /* Hide compare section completely in print */
3837  .pd-compare-section,
3838  .pd-compare-btn,
3839  #pd-compare-btn,
3840  .pd-compare-info,
3841  .compare-note,
3842  .compare-text,
3843  .compare-remove {
3844    display: none !important;
3845    visibility: hidden !important;
3846    height: 0 !important;
3847    margin: 0 !important;
3848    padding: 0 !important;
3849    overflow: hidden !important;
3850  }
3851  
3852  /* Variants - show all options with selected highlighted */
3853  .pd-variants {
3854    margin: 0 !important;
3855    padding: 0 !important;
3856    page-break-inside: avoid !important;
3857    border: none !important;
3858    border-top: none !important;
3859    border-bottom: none !important;
3860    box-shadow: none !important;
3861    background: transparent !important;
3862  }
3863  
3864  /* Add 'Variant:' label before variant groups */
3865  .pd-variants::before {
3866    content: "Variant:" !important;
3867    display: block !important;
3868    font-size: 11pt !important;
3869    font-weight: 700 !important;
3870    color: #0f172a !important;
3871    margin-bottom: 10px !important;
3872    border: none !important;
3873    padding: 0 !important;
3874    border-top: none !important;
3875    border-bottom: none !important;
3876  }
3877  
3878  .v-group {
3879    display: flex !important;
3880    align-items: center !important;
3881    gap: 8px !important;
3882    margin-bottom: 8px !important;
3883    line-height: 1 !important;
3884    page-break-inside: avoid !important;
3885    border: none !important;
3886    padding: 0 !important;
3887  }
3888  
3889  .v-group:last-child {
3890    margin-bottom: 0 !important;
3891  }
3892  
3893  .v-label {
3894    font-weight: 700 !important;
3895    margin: 0 !important;
3896    font-size: 9pt !important;
3897    color: #475569 !important;
3898    min-width: 60px !important;
3899    flex-shrink: 0 !important;
3900    border: none !important;
3901    padding: 0 !important;
3902  }
3903  
3904  .v-options {
3905    display: flex !important;
3906    gap: 6px !important;
3907    flex-wrap: wrap !important;
3908    border: none !important;
3909    padding: 0 !important;
3910    margin: 0 !important;
3911  }
3912  
3913  .v-chip {
3914    border: 1px solid #cbd5e1 !important;
3915    padding: 3px 10px !important;
3916    font-size: 9pt !important;
3917    background: #f8fafc !important;
3918    border-radius: 6px !important;
3919    display: inline-flex !important;
3920    align-items: center !important;
3921    page-break-inside: avoid !important;
3922  }
3923  
3924  /* Highlight selected variant with blue background */
3925  .v-chip.selected {
3926    background: #eff6ff !important;
3927    border-color: #2563eb !important;
3928    font-weight: 600 !important;
3929  }
3930  
3931  /* Hide radio inputs */
3932  .v-chip input[type="radio"] {
3933    display: none !important;
3934  }
3935  
3936  /* Hide "Habis" badge in print */
3937  .v-chip .chip-badge {
3938    display: none !important;
3939  }
3940  
3941  /* Short description - HIDDEN in print */
3942  .pd-short-desc {
3943    display: none !important;
3944  }
3945  
3946  /* QR Code section - right side beside price */
3947  .pd-qr-print {
3948    display: block !important;
3949    flex: 0 0 100px !important;
3950    margin: 0 !important;
3951    padding: 8px !important;
3952    background: #f8fafc !important;
3953    border-radius: 8px !important;
3954    text-align: center !important;
3955    page-break-inside: avoid !important;
3956    border: 1px solid #cbd5e1 !important;
3957  }
3958  
3959  .pd-qr-label {
3960    font-size: 6.5pt !important;
3961    color: #64748b !important;
3962    margin-bottom: 5px !important;
3963    line-height: 1.2 !important;
3964    font-weight: 600 !important;
3965  }
3966  
3967  .pd-qr-image {
3968    width: 80px !important;
3969    height: 80px !important;
3970    display: block !important;
3971    margin: 0 auto !important;
3972  }
3973  
3974  /* Full description - below 2 column layout */
3975  .pd-full-description {
3976    border: none !important;
3977    padding: 0 !important;
3978    margin: 24px 0 0 0 !important;
3979    page-break-before: avoid;
3980    grid-column: 1 / -1 !important;
3981  }
3982  
3983  .pd-full-desc-title {
3984    font-size: 13pt !important;
3985    font-weight: 700 !important;
3986    margin: 0 0 12px 0 !important;
3987    color: #000 !important;
3988    border-top: none !important;
3989    border-bottom: none !important;
3990    padding-top: 16px !important;
3991  }
3992  
3993  .pd-full-desc-body {
3994    font-size: 10pt !important;
3995    line-height: 1.6 !important;
3996    color: #334155 !important;
3997  }
3998  
3999  .pd-full-desc-body img {
4000    max-height: 200px !important;
4001    width: auto !important;
4002    page-break-inside: avoid;
4003    display: none !important; /* Hide images in print to save space */
4004  }
4005  
4006  /* Also target #deskripsi img if exists */
4007  #deskripsi img {
4008    max-width: 350px !important;
4009    height: auto !important;
4010  }
4011  
4012  .pd-full-desc-body table {
4013    border-collapse: collapse !important;
4014    width: 100% !important;
4015    margin: 12px 0 !important;
4016    font-size: 9pt !important;
4017  }
4018  
4019  .pd-full-desc-body th,
4020  .pd-full-desc-body td {
4021    border: 1px solid #cbd5e1 !important;
4022    padding: 6px 8px !important;
4023  }
4024  
4025  /* Print footer */
4026  .product-single::after {
4027    content: "Plaza IT - plazait.co.id | Printed: " attr(data-print-date);
4028    display: block;
4029    margin-top: 30px;
4030    padding-top: 16px;
4031    border-top: 1px solid #e2e8f0;
4032    font-size: 9pt;
4033    color: #64748b;
4034    text-align: center;
4035  }
4036  
4037  /* Page breaks */
4038  .pd-media-card,
4039  .pd-price-block,
4040  .pd-full-desc-title {
4041    page-break-after: avoid;
4042  }
4043  
4044  .pd-full-description {
4045    page-break-before: auto;
4046  }
4047}
4048
4049/* === Breadcrumb (truncate last item) === */
4050.pd-breadcrumb{
4051  display:flex;
4052  flex-wrap:nowrap;
4053  align-items:center;
4054  gap:.5rem;
4055  font-size:.72rem;
4056  margin:.7rem 0 1.2rem .5rem;
4057  color:var(--pd-muted);
4058  overflow:hidden;
4059}
4060.pd-breadcrumb a{color:var(--pd-accent);text-decoration:none}
4061.pd-breadcrumb a:hover{text-decoration:underline}
4062.pd-breadcrumb a,
4063.pd-breadcrumb span{flex:0 0 auto}
4064.pd-breadcrumb .current{
4065  flex:1 1 auto;
4066  min-width:0;
4067  white-space:nowrap;
4068  overflow:hidden;
4069  text-overflow:ellipsis;
4070  font-weight:600;
4071  color:var(--pd-dark);
4072  padding-right:2px;
4073}
4074
4075/* === Top layout === */
4076.pd-top-layout{
4077  display:grid;
4078  gap:var(--pd-gap);
4079  grid-template-columns:minmax(260px,340px) minmax(460px,1fr) 300px;
4080  align-items:start;
4081  margin-bottom:2.4rem;
4082}
4083
4084/* === Media column === */
4085.pd-media-card{
4086  background:#fff;
4087  border:1px solid var(--pd-border);
4088  border-radius:var(--pd-radius);
4089  padding:1rem 1rem 1.2rem;
4090}
4091.pd-main-image{margin:0 0 .8rem;border:1px solid var(--pd-soft-border);border-radius:10px;overflow:hidden;background:#fff;position:relative}
4092.pd-main-image img{width:100%;display:block;object-fit:contain;aspect-ratio:1/1}
4093
4094/* Gallery Thumbnails */
4095.pd-thumb-gallery{margin-top:.6rem;display:flex;flex-wrap:wrap;gap:.55rem}
4096.pd-thumb-btn{
4097  border:1px solid var(--pd-soft-border);
4098  background:#fff;
4099  cursor:pointer;
4100  width:64px;height:64px;padding:2px;
4101  display:flex;align-items:center;justify-content:center;
4102  overflow:hidden;border-radius:8px;
4103  transition:border-color .18s ease, box-shadow .18s ease, transform .18s ease;
4104  position:relative;
4105}
4106.pd-thumb-btn img{max-width:100%;max-height:100%;object-fit:contain;display:block}
4107.pd-thumb-btn:hover{border-color:var(--pd-accent)}
4108.pd-thumb-btn.active{border-color:var(--pd-accent);box-shadow:0 0 0 2px rgba(13,110,253,.25)}
4109.pd-thumb-btn:focus-visible{outline:2px solid var(--pd-accent);outline-offset:2px}
4110.pd-thumb-btn:active{transform:scale(.96)}
4111
4112/* === Info column === */
4113.pd-info-card{
4114  background:#fff;border:1px solid var(--pd-border);border-radius:var(--pd-radius);
4115  padding:1.4rem 1.6rem 2rem;
4116}
4117.pd-title{margin:0 0 1rem;font-size:1.2rem;line-height:1.25;font-weight:600;color:var(--pd-dark)}
4118.pd-price-block{margin:0 0 1rem}
4119.pd-price-row{display:flex;flex-wrap:wrap;align-items:center;gap:.75rem;margin:0 0 .35rem}
4120.pd-price-current{font-size:2rem;font-weight:700;color:var(--pd-accent)}
4121.pd-price-original{font-size:.9rem;text-decoration:line-through;color:#94a3b8}
4122.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}
4123.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}
4124.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}
4125.stock-badge.in{background:var(--pd-success-bg);color:var(--pd-success-text)}
4126.stock-badge.out{background:var(--pd-danger-bg);color:var(--pd-danger-text)}
4127.digital-badge{padding:4px 10px;border-radius:24px;background:linear-gradient(135deg,#e2a642 0%,#764ba2 100%);color:#fff;font-size:.63rem;font-weight:600;letter-spacing:.6px;text-transform:uppercase;display:inline-block;line-height:1;vertical-align:middle;border:none}
4128.sku{color:var(--pd-muted)}
4129.pd-short-desc{font-size:.85rem;line-height:1.5;margin:0 0 1.2rem;color:#334155;overflow-wrap:anywhere}
4130.pd-short-desc p{margin:0 0 .75rem}
4131.pd-short-desc ul,.pd-short-desc ol{margin:.5rem 0 .9rem 1.1rem;font-size:.85rem;line-height:1.45}
4132
4133/* Meta row for sold and rating badges */
4134.pd-meta-row {
4135  display: flex;
4136  align-items: center;
4137  flex-wrap: wrap;
4138  gap: 5px;
4139  margin-top: 8px;
4140  margin-bottom: 4px;
4141}
4142
4143/* Sales (Terjual) badge inline */
4144.sold-inline{
4145  display:inline-block;font-size:.65rem;font-weight:600;color:#475569;
4146  background:#f1f5f9;padding:3px 8px 4px;border-radius:6px;letter-spacing:.4px;margin-left:0;
4147}
4148
4149/* Price row divider */
4150.price-divider {
4151  display: inline-block;
4152  margin: 0 .4rem;
4153  color: #cbd5e1;
4154  font-weight: 400;
4155}
4156
4157/* Rating badge inline */
4158.rating-inline {
4159  display: inline-flex;
4160  align-items: center;
4161  gap: 4px;
4162  padding: 3px 8px 4px;
4163  background: #fff7ed;
4164  border-radius: 6px;
4165  font-size: .65rem;
4166  font-weight: 600;
4167  color: #C10DFF;
4168  text-decoration: none;
4169  transition: all .2s ease;
4170  cursor: pointer;
4171  margin-left: .5rem;
4172  white-space: nowrap;
4173}
4174
4175.rating-inline:hover {
4176  background: #ffedd5;
4177  transform: translateY(-1px);
4178  box-shadow: 0 2px 4px rgba(245,202,86,0.1);
4179}
4180
4181.rating-inline .star-icon {
4182  font-size: .75rem;
4183  color: #C10DFF;
4184  line-height: 1;
4185}
4186
4187.rating-inline .rating-value {
4188  color: #C10DFF;
4189}
4190
4191.rating-inline .rating-badge-count {
4192  color: #a36422;
4193  opacity: 0.85;
4194}
4195
4196@media (max-width: 640px) {
4197  .rating-inline {
4198    font-size: .65rem;
4199    padding: 3px 8px 4px;
4200  }
4201  .rating-inline .star-icon {
4202    font-size: .7rem;
4203  }
4204}
4205
4206/* Highlight animation for review section */
4207@keyframes highlightFlash {
4208  0%, 100% { background-color: transparent; }
4209  50% { background-color: rgba(245,202,86,0.1); }
4210}
4211
4212.highlight-flash {
4213  animation: highlightFlash 1.5s ease;
4214}
4215
4216/* === Variant chips === */
4217.pd-variants{margin-top:14px}
4218.v-group{margin-bottom:12px}
4219.v-label{font-weight:600;margin-bottom:6px}
4220.v-options{display:flex;flex-wrap:wrap;gap:8px}
4221.v-chip{
4222  display:inline-flex;align-items:center;gap:8px;
4223  padding:6px 10px;border:1px solid #d6d9de;border-radius:999px;
4224  cursor:pointer;background:#fff;transition:.15s ease;user-select:none
4225}
4226.v-chip:hover{border-color:#9aa3af;box-shadow:0 1px 0 rgba(0,0,0,.04)}
4227.v-chip.selected{border-color:#2563eb;background:#eff6ff}
4228.v-chip.disabled{opacity:.45;cursor:not-allowed}
4229.v-chip.out{border-style:dashed}
4230.v-chip input{display:none}
4231.v-chip .dot{width:14px;
4231height:14px;border-radius:50%;background:var(--dot,#999);box-shadow:inset 0 0 0 1px rgba(0,0,0,.2)}
4232.v-chip .dot--neutral{background:#9aa3af}
4233.v-chip .txt{font-size:.95rem;white-space:nowrap}
4234.v-chip .chip-badge{margin-left:2px;font-size:.72rem;line-height:1;padding:3px 6px;border-radius:6px;background:#fee2e2;color:#a36422;border:1px solid #fecaca}
4235.pd-variant-summary{margin-top:8px;color:#475569;font-size:.92rem}
4236.pd-variant-summary .vs-item{display:inline-flex;gap:4px;margin-right:10px}
4237
4238/* === Buy form (di sidebar kanan untuk desktop) === */
4239.pd-buy-form{display:flex;flex-wrap:wrap;align-items:flex-end;gap:1rem 1.4rem;margin:0}
4240.qty-label{display:block;font-size:.6rem;font-weight:600;letter-spacing:.7px;text-transform:uppercase;color:var(--pd-muted);margin-bottom:.35rem}
4241.pd-buy-form input[type=number]{width:90px;padding:.55rem .6rem;border:1px solid var(--pd-soft-border);border-radius:6px;font-size:.85rem}
4242.pd-actions{display:flex;gap:.8rem;flex-wrap:wrap}
4243
4244/* Tombol senada tema */
4245.btn-add-cart,.btn-buy,.btn-chat-icon{
4246  cursor:pointer;border:0;font-weight:700;border-radius:10px;font-size:.9rem;
4247  display:inline-flex;align-items:center;justify-content:center;gap:.4rem;
4248  min-height:44px;padding:0 1rem;transition:var(--pd-transition)
4249}
4250.btn-add-cart{background:var(--brand-500);color:#fff;box-shadow:0 2px 8px -2px rgba(13,110,253,.35)}
4251.btn-add-cart:hover{filter:brightness(.95)}
4252.btn-buy{background:#fff;color:var(--brand-500);border:2px solid var(--brand-500)}
4253.btn-buy:hover{background:rgba(13,110,253,.06)}
4254.btn-chat-icon{background:#fff;color:#C10DFF;border:2px solid #C10DFF;padding:0;width:44px;min-width:44px}
4255.btn-chat-icon:hover{background:rgba(245,202,86,0.06);border-color:#C10DFF}
4256.btn-chat-icon svg{width:20px;height:20px;flex-shrink:0}
4257.btn-add-cart:disabled,.btn-buy:disabled,.btn-chat-icon:disabled{opacity:.5;cursor:not-allowed}
4258
4259/* Buy with Voucher Button - Cleaner, more compact design */
4260.btn-buy-voucher{
4261  cursor:pointer;border:0;font-weight:600;border-radius:10px;
4262  display:inline-flex;align-items:center;justify-content:space-between;gap:10px;
4263  min-height:44px;padding:0 14px 0 12px;transition:all 0.3s ease;
4264  background:linear-gradient(135deg, #7c3aed 0%, #9333ea 100%);
4265  color:#fff;
4266  box-shadow:0 3px 10px rgba(124,58,237,0.3);
4267  position:relative;
4268  overflow:hidden;
4269  flex:1 1 auto
4270}
4271.btn-buy-voucher:hover{
4272  transform:translateY(-2px);
4273  box-shadow:0 5px 16px rgba(124,58,237,0.4)
4274}
4275.btn-buy-voucher:active{
4276  transform:translateY(0);
4277  box-shadow:0 2px 6px rgba(124,58,237,0.3)
4278}
4279.btn-buy-voucher .bv-icon{
4280  font-size:20px;
4281  line-height:1;
4282  flex-shrink:0
4283}
4284.btn-buy-voucher .bv-text{
4285  display:flex;
4286  flex-direction:column;
4287  align-items:flex-start;
4288  gap:2px;
4289  flex:1;
4290  min-width:0;
4291  font-size:14px;
4292  line-height:1.2
4293}
4294.btn-buy-voucher .bv-text small{
4295  font-size:10px;
4296  opacity:0.85;
4297  font-weight:500;
4298  letter-spacing:0.3px;
4299  font-family:monospace
4300}
4301.btn-buy-voucher .bv-save{
4302  font-size:12px;
4303  font-weight:700;
4304  background:rgba(255,255,255,0.25);
4305  padding:4px 8px;
4306  border-radius:6px;
4307  white-space:nowrap;
4308  flex-shrink:0
4309}
4310
4311/* Extra meta (opsional) */
4312.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}
4313.pd-extra-meta span{font-weight:600;color:var(--pd-muted);margin-right:.3rem}
4314.pd-extra-meta a{color:var(--pd-accent);text-decoration:none}
4315.pd-extra-meta a:hover{text-decoration:underline}
4316
4317/* === Sidebar === */
4318.pd-sidebar{display:flex;flex-direction:column;gap:1.2rem}
4319.pd-side-card{background:#fff;border:1px solid var(--pd-border);border-radius:var(--pd-radius);padding:1rem 1.1rem 1.2rem}
4320.pd-side-card h3{margin:0 0 .7rem;font-size:.8rem;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--pd-dark)}
4321.pd-side-card ul{list-style:none;margin:0;padding:0;display:grid;gap:.45rem;font-size:.7rem;color:#334155}
4322.pd-side-card li{position:relative;padding-left:13px;line-height:1.25}
4323.pd-side-card li:before{content:'';width:6px;height:6px;background:var(--pd-accent);border-radius:50%;position:absolute;
4323left:0;top:.45rem}
4324.pay-logos{display:flex;gap:.4rem;flex-wrap:wrap;margin:.1rem 0 .4rem}
4325.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}
4326
4327/* Buy card hanya desktop (mobile pakai FAB) */
4328.pd-buy-card{display:block}
4329@media (max-width:880px){ .pd-buy-card{display:none} .pd-title-row {padding-right: 0 !important;}}
4330
4331/* === Full Description === */
4332.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}
4333.pd-full-desc-title{margin:0 0 1.1rem;font-size:1.15rem;font-weight:600;color:var(--pd-dark)}
4334.pd-full-desc-body{font-size:.9rem;line-height:1.6;color:#334155;overflow-wrap:anywhere}
4335.pd-full-desc-body p{margin:0 0 1rem}
4336.pd-full-desc-body ul,.pd-full-desc-body ol{margin:.5rem 0 1rem 1.2rem}
4337.pd-full-desc-body table{width:100%;border-collapse:collapse;margin:1rem 0 1.2rem}
4338.pd-full-desc-body table,.pd-full-desc-body th,.pd-full-desc-body td{border-bottom:1px dotted #cbd5e1;vertical-align: top}
4339.pd-full-desc-body th,.pd-full-desc-body td{padding:.45rem .55rem;text-align:left}
4340.pd-full-desc-body img{max-width:100%;height:auto;display:block;}
4341
4342/* === Related (container) === */
4343.pd-related-full{
4344  background:#fff;border:1px solid var(--pd-border);
4345  border-radius:var(--pd-radius);padding:1.4rem 1.6rem 1.9rem;
4346  margin:0 0 2rem;
4347}
4348.pd-related-title{margin:0 0 1.1rem;font-size:1.05rem;font-weight:600;color:var(--pd-dark)}
4349
4350/* Related Grid (dirapikan: gabung selector agar tidak duplikat) */
4351.pd-related-grid,
4352.pd-related-grid--full{
4353  display:grid !important;
4354  gap:16px !important;
4355  grid-template-columns:repeat(auto-fill,minmax(170px,1fr)) !important;
4356  align-items:stretch;
4357}
4358
4359/* Fallback jika grid tidak didukung */
4360@supports not (display:grid){
4361  .pd-related-grid,
4362  .pd-related-grid--full{display:flex !important;flex-wrap:wrap}
4363  .pd-related-grid .pd-related-card,
4364  .pd-related-grid--full .pd-related-card{flex:0 0 calc(20% - 16px);max-width:calc(20% - 16px)}
4365  @media (max-width:1100px){
4366    .pd-related-grid .pd-related-card,
4367    .pd-related-grid--full .pd-related-card{flex:0 0 calc(25% - 16px);max-width:calc(25% - 16px)}
4368  }
4369  @media (max-width:820px){
4370    .pd-related-grid .pd-related-card,
4371    .pd-related-grid--full .pd-related-card{flex:0 0 calc(33.333% - 16px);max-width:calc(33.333% - 16px)}
4372  }
4373  @media (max-width:580px){
4374    .pd-related-grid .pd-related-card,
4375    .pd-related-grid--full .pd-related-card{flex:0 0 calc(50% - 16px);max-width:calc(50% - 16px)}
4376  }
4377}
4378
4379/* Related Card */
4380.pd-related-card{
4381  display:flex;flex-direction:column;border:1px solid var(--pd-soft-border);
4382  background:#fff;border-radius:10px;text-decoration:none;color:inherit;
4383  overflow:hidden;transition:var(--pd-transition);position:relative;min-width:0;
4384}
4385.pd-related-card:hover{transform:translateY(-4px);box-shadow:var(--pd-shadow)}
4386.pd-related-card .thumb{aspect-ratio:1/1;background:#f1f5f9;overflow:hidden;position:relative}
4387.pd-related-card img{width:100%;height:100%;object-fit:cover;display:block}
4388.mini-disc{position:absolute;top:6px;left:6px;background:#e2a642;color:#fff;font-size:.55rem;padding:3px 6px 2px;border-radius:4px;font-weight:600}
4389.pd-related-card .info{padding:.55rem .65rem .75rem;display:flex;flex-direction:column;gap:.25rem}
4390.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}
4391.pd-related-card .price{font-size:.7rem;font-weight:600;color:var(--pd-accent)}
4392.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;}
4393.pd-related-card .price .final {font-size: .82rem; font-weight: 700; color: #0d6efd;}
4394.pd-related-card .price .ori {font-size: .6rem; text-decoration: line-through; color: #94a3b8;font-weight: 500;}
4395
4396/* === Mobile slider (scroll-snap) — gambar utama tetap tampil === */
4397.pd-mobile-slider{display:none}
4398.pd-mobile-slider img{width:100%;display:block;object-fit:contain;aspect-ratio:1/1}
4399
4400/* Gallery counter badge */
4401.pd-gallery-counter{
4402  position:absolute;
4403  bottom:12px;
4404  left:12px;
4405  background:rgba(255,255,255,0.25);
4406  color:#9E9E9E;
4407  padding:4px 10px;
4408  border-radius:10px;
4409  font-size:12px;
4410  font-weight:600;
4411  z-index:100;
4412  backdrop-filter:blur(6px);
4413  -webkit-backdrop-filter:blur(6px);
4414  font-variant-numeric:tabular-nums;
4415  pointer-events:none;
4416  display:none;
4417  box-shadow:0 1px 4px rgba(0,0,0,.15);
4418  line-height:1;
4419  border:1px solid rgba(0,0,0,0.08);
4420}
4421
4422@media (max-width:640px){
4423  .pd-media-card{border: 0;padding: 0 1rem 0;}
4424  .pd-info-card {border: 0; padding: 0 1.3rem 0; }
4425  .pd-side-card {flex: 1 1 !important;}
4426  .pd-top-layout {gap: 0; margin-bottom: 0;}
4427  .pd-variants {margin-bottom: 1.5rem;}
4428  .pd-main-image{display:none}
4429  .pd-gallery-counter{display:block}
4430  .pd-mobile-slider-wrapper{
4431    position:relative;
4432    margin-bottom:.6rem;
4433  }
4434  .pd-mobile-slider{
4435    display:flex;gap:8px;overflow-x:auto;
4436    scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;
4437  }
4438  .pd-mslide{
4439    min-width:100%;flex:0 0 100%;scroll-snap-align:center;
4440    border:1px solid var(--pd-soft-border);border-radius:10px;overflow:hidden;background:#fff
4441  }
4442  .pd-thumb-gallery{display:none} /* hide thumbnails on mobile */
4443}
4444
4445/* === Mobile Floating Action Bar === */
4446.pd-mobile-fab{
4447  position:fixed;
4448  left:0;
4449  right:0;
4450  bottom:42px; /* Position above bottom-nav (60px min-height + 6px padding) */
4451  display:none;
4452  align-items:center;
4453  gap:.75rem;
4454  padding:12px 16px;
4455  background:rgba(255,255,255,0.95);
4456  backdrop-filter:blur(20px);
4457  -webkit-backdrop-filter:blur(20px);
4458  border-top:1px solid rgba(228,231,235,0.6);
4459  box-shadow:0 -4px 16px -4px rgba(0,0,0,0.12);
4460  z-index:999; /* Below bottom-nav (1000) */
4461  transition:bottom .3s cubic-bezier(0.4, 0.0, 0.2, 1);
4462}
4463
4464/* When bottom-nav is hidden, move FAB to bottom */
4465body:has(.bottom-nav.hidden) .pd-mobile-fab {
4466  bottom: 0;
4467}
4468.pd-mobile-fab .mf-actions{display:flex;gap:.5rem;width:100%}
4469.mf-btn{
4470  border-radius:10px;
4471  padding:.6rem 1rem;
4472  font-weight:600;
4473  font-size:14px;
4474  cursor:pointer;
4475  transition:all .2s;
4476  -webkit-tap-highlight-color:transparent;
4477  display:inline-flex;
4478  align-items:center;
4479  justify-content:center;
4480  gap:4px;
4481}
4482.mf-buy{
4483  background:#C10DFF;
4484  color:#fff;
4485  border:0;
4486  box-shadow:0 2px 8px rgba(245,202,86,0.3);
4487  flex:1;
4488  flex-direction:column;
4489  padding:.55rem 1rem;
4490  line-height:1;
4491  min-height:44px;
4492  font-family:inherit;
4493}
4494.mf-buy .mf-buy-text{
4495  font-size:11px;
4496  font-weight:600;
4497  opacity:0.95;
4498  font-family:inherit;
4499}
4500.mf-buy .mf-buy-price{
4501  font-size:13px;
4502  font-weight:800;
4503  letter-spacing:-0.3px;
4504  font-family:inherit;
4505  line-height:1;
4506}
4507.mf-buy:active{transform:scale(0.97)}
4508.mf-cart{
4509  background:#fff;
4510  color:#C10DFF;
4511  border:2px solid #C10DFF;
4512  width:44px;
4513  height:44px;
4514  min-height:44px;
4515  padding:0;
4516  flex-shrink:0;
4517  font-family:inherit;
4518}
4519.mf-cart:active{transform:scale(0.95)}
4520.mf-chat{background:#fff;color:#C10DFF;border:2px solid #C10DFF;width:44px;height:44px;min-height:44px;padding:0;flex-shrink:0;font-family:inherit}
4521.mf-chat:active{transform:scale(0.95)}
4522.mf-chat svg{width:20px;height:20px;flex-shrink:0}
4523.mf-share{
4524  background:#fff;
4525  color:#C10DFF;
4526  border:2px solid #C10DFF;
4527  width:44px;
4528  height:44px;
4529  padding:0;
4530  flex-shrink:0;
4531}
4532.mf-share:active{transform:scale(0.95)}
4533.mf-btn:disabled{opacity:.5;cursor:not-allowed}
4534@media (max-width:640px){
4535  .pd-mobile-fab{display:flex}
4536  .product-single{padding-bottom:0;} /* Space for FAB + bottom-nav */
4537}
4538
4539/* === Installment cards === */
4540.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}
4541.pd-installment-card h3{margin:0 0 .2rem;font-size:.78rem;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--pd-dark)}
4542
4543.inst-tabs{display:flex;gap:.4rem}
4544.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)}
4545.inst-tab.active,.inst-tab:hover{background:var(--pd-accent);color:#fff;border-color:var(--pd-accent)}
4546.inst-tab:focus{outline:2px solid var(--pd-accent);outline-offset:2px}
4547
4548.inst-tenors{display:flex;gap:.4rem;flex-wrap:wrap}
4549.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}
4550.tenor-btn.active,.tenor-btn:hover{background:#1d7df0;color:#fff;border-color:#1d7df0}
4551
4552.inst-result{display:flex;flex-direction:column;gap:.45rem;font-size:.63rem}
4553.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}
4554.inst-line.highlight{background:#1d7df0;color:#fff;border-color:#1d7df0;font-weight:600}
4555.inst-line .lbl{opacity:.8}
4556.inst-line .val{font-weight:600;letter-spacing:.3px}
4557.inst-footnote{font-size:.53rem;line-height:1.25;color:#64748b;margin-top:.2rem}
4558@media (max-width: 860px){
4559  .pd-installment-card{order:3}
4560  .inst-tabs .inst-tab,.inst-tenors .tenor-btn{font-size:.65rem}
4561  
4562  /* Voucher button responsive - make it simpler */
4563  .btn-buy-voucher{
4564    padding:0 12px;
4565    gap:8px
4566  }
4567  .btn-buy-voucher .bv-icon{
4568    font-size:18px
4569  }
4570  .btn-buy-voucher .bv-text{
4571    font-size:13px
4572  }
4573  .btn-buy-voucher .bv-text small{
4574    font-size:9px
4575  }
4576  .btn-buy-voucher .bv-save{
4577    font-size:11px;
4578    padding:3px 6px
4579  }
4580}
4581
4582/* Installment List v2 (eye-catching) */
4583.pd-installment-card2{
4584  background:linear-gradient(135deg,#ffffff 0%,#f1f7ff 65%,#e8f2ff 100%);
4585  border:1px solid var(--pd-border);border-radius:var(--pd-radius);
4586  padding:1rem 1.05rem 1.2rem;display:flex;flex-direction:column;gap:.5rem;
4587  position:relative;overflow:hidden
4588}
4589.pd-installment-card2:before{
4590  content:'';position:absolute;width:140px;height:140px;
4591  background:radial-gradient(circle at center,rgba(29,125,240,.18),rgba(29,125,240,0));
4592  top:-40px;right:-40px;pointer-events:none
4593}
4594.pd-installment-card2 h3{margin:0;font-size:.8rem;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:#0f2f54}
4595.pd-installment-card2 .inst-head{display:flex;align-items:center;justify-content:space-between;gap:.5rem}
4596.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)}
4597
4598.inst-bank-tabs{display:flex;gap:.45rem}
4599.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}
4600.bank-tab.active,.bank-tab:hover{background:#1d7df0;color:#fff;border-color:#1d7df0;box-shadow:0 3px 10px -4px rgba(29,125,240,.6)}
4601.bank-tab:focus{outline:2px solid #1d7df0;outline-offset:2px}
4602
4603.inst-month-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.55rem}
4604.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}
4605.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}
4606.inst-month-item:hover{transform:translateY(-2px);box-shadow:0 6px 14px -6px rgba(0,40,120,.25)}
4607.inst-month-item:hover:before{opacity:1}
4608.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)}
4609.amount{flex:1;font-size:.8rem;font-weight:600;color:#0f2f54;letter-spacing:.3px}
4610.tag{font-size:.55rem;font-weight:600;padding:.28rem .55rem;border-radius:30px;letter-spacing:.4px;text-transform:uppercase;white-space:nowrap}
4611.tag-zero{background:#16a34a;color:#fff;box-shadow:0 2px 8px -3px rgba(16,180,90,.55)}
4612
4613.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}
4614.inst-disclaimer{font-size:.52rem;line-height:1.25;color:#64748b;margin-top:-.2rem}
4615
4616@media (max-width:860px){
4617  .pd-installment-card2{order:3}
4618  .tenor-badge{min-width:38px;font-size:.75rem}
4619  .amount{font-size:.78rem}
4620}
4621
4622/* Alt Fintech Banner (compact) */
4623.inst-alt-banner--compact{
4624  background:linear-gradient(142deg,#1d7df0 0%,#3c98ff 55%,#67bdff 100%);
4625  color:#fff;border:0;padding:.95rem .95rem 1.05rem;border-radius:16px;
4626  box-shadow:0 6px 18px -6px rgba(0,70,160,.45);position:relative;overflow:hidden;min-height:auto;
4627}
4628.inst-alt-banner--compact:before,
4629.inst-alt-banner--compact:after{
4630  content:'';position:absolute;border-radius:50%;background:rgba(255,255,255,.15);filter:blur(2px);pointer-events:none;
4631}
4632.inst-alt-banner--compact:before{width:130px;height:130px;top:-50px;right:-35px}
4633.inst-alt-banner--compact:after{width:90px;height:90px;bottom:-40px;left:-30px}
4634.inst-alt-banner--compact .iab-top{margin:0 0 .55rem;display:flex;flex-direction:column;gap:.25rem}
4635.inst-alt-banner--compact .iab-title{font-size:.74rem;font-weight:700;letter-spacing:.7px;line-height:1.15}
4636.inst-alt-banner--compact .iab-sub{font-size:.55rem;font-weight:500;opacity:.95;letter-spacing:.4px}
4637
4638.iab-logos-compact{display:flex;gap:.55rem;align-items:center;margin:0 0 .6rem;flex-wrap:nowrap;justify-content:space-between}
4639.iab-logos-compact .logo-fin{width:30%}
4640.iab-logos-compact .logo{
4641  width:48px;height:48px;border-radius:14px;display:flex;align-items:center;justify-content:center;
4642  font-size:.62rem;font-weight:700;letter-spacing:.3px;position:relative;color:#fff;
4643  box-shadow:0 4px 10px -4px rgba(0,0,0,.35);background:rgba(255,255,255,.15);
4644  backdrop-filter:blur(4px);transition:.25s;text-shadow:0 1px 2px rgba(0,0,0,.4);user-select:none;
4645}
4646.iab-logos-compact .logo:before{
4647  content:'';position:absolute;inset:0;border-radius:inherit;
4648  background:linear-gradient(140deg,rgba(255,255,255,.35),rgba(255,255,255,0));mix-blend-mode:overlay;opacity:.55;pointer-events:none;
4649}
4650.iab-logos-compact .logo:hover{transform:translateY(-3px) scale(1.05);box-shadow:0 8px 18px -6px rgba(0,0,0,.45)}
4651
4652.iab-cta--compact{
4653  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;
4654}
4655.iab-cta--compact:hover{transform:translateY(-2px);box-shadow:0 8px 18px -7px rgba(0,0,0,.45)}
4656.iab-footnote--compact{margin-top:.45rem;font-size:.46rem;opacity:.85;letter-spacing:.25px}
4657@media (max-width:860px){
4658  .inst-alt-banner--compact{order:4;margin-top:.4rem;flex:1 1}
4659  .iab-logos-compact .logo{width:46px;height:46px}
4660}
4661
4662/* === Scroll To Top (baru) === */
4663.pd-scroll-top{
4664  position:fixed;
4665  right:20px;
4666  bottom:90px;
4667  width:48px;
4668  height:48px;
4669  border-radius:50%;
4670  background:linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
4671  color:#0f2f54;
4672  border:1px solid rgba(226, 232, 240, 0.8);
4673  box-shadow:0 8px 20px -8px rgba(0,0,0,.3), 0 2px 6px rgba(0,0,0,.1);
4674  display:flex;
4675  align-items:center;
4676  justify-content:center;
4677  cursor:pointer;
4678  z-index:1105;
4679  opacity:0;
4680  transform:translateY(12px) scale(.92);
4681  transition:all .3s cubic-bezier(0.4, 0, 0.2, 1);
4682  backdrop-filter:blur(8px);
4683  -webkit-backdrop-filter:blur(8px);
4684}
4685.pd-scroll-top:hover{
4686  box-shadow:0 12px 28px -10px rgba(0,0,0,.4), 0 4px 12px rgba(0,0,0,.15);
4687  transform:translateY(0) scale(1);
4688  background:linear-gradient(135deg, #ffffff 0%, #fefefe 100%);
4689  border-color:rgba(226, 232, 240, 1);
4690}
4691.pd-scroll-top:active{
4692  transform:translateY(0) scale(.96);
4693}
4694.pd-scroll-top:focus-visible{outline:2px solid var(--pd-accent);outline-offset:2px}
4695.pd-scroll-top.visible{opacity:1;transform:translateY(0) scale(1)}
4696@media (max-width:640px){
4697  /* Dynamic positioning via JavaScript */
4698  .pd-scroll-top{
4699    right:14px;
4700    bottom:var(--scroll-top-bottom, calc(68px + 18px + env(safe-area-inset-bottom)));
4701  }
4702}
4703
4704/* === Responsive layout tweaks === */
4705@media (max-width:1250px){
4706  .pd-top-layout{grid-template-columns:minmax(260px,330px) 1fr}
4707  .pd-sidebar{grid-column:1 / -1;flex-direction:row;flex-wrap:wrap;gap:1rem}
4708  /* Other cards 2-per-row; buy card always takes full row to avoid height-mismatch gaps */
4709  .pd-side-card{flex:1 1 calc(50% - .5rem)}
4710  .pd-buy-card{flex:0 0 100%}
4711}
4712@media (max-width:880px){
4713  .pd-top-layout{grid-template-columns:1fr}
4714  .pd-sidebar{flex-direction:column; padding: 0 1rem;}
4715  .pd-title{font-size:1.2rem}
4716  .pd-price-current{font-size:1.5rem}
4717}
4718@media (max-width:640px){
4719  /* Related: 2 kolom di mobile */
4720  .pd-related-full{padding:1.2rem;border:0;padding-top: .6rem;margin-bottom:.6rem;}
4721  .pd-related-grid,.pd-related-grid--full{grid-template-columns:repeat(2,1fr) !important;gap:12px !important}
4722}
4723@media (max-width:560px){
4724  .pd-actions{flex-direction:column}
4725  .btn-add-cart,.btn-buy,.btn-buy-voucher{width:100%}
4726  .pd-full-description{padding:1.2rem;border:0;padding-bottom:0;}
4727}
4728@media (max-width:380px){
4729  .pd-related-full{padding:.9rem .7rem 1.1rem}
4730  .pd-related-grid,.pd-related-grid--full{gap:8px !important}
4731  .pd-related-card .info{padding:.4rem .48rem .55rem}
4732}
4733
4734/* === Modern Countdown (Product Detail) === */
4735.pd-countdown-modern{
4736  --cd-bg1:#0ea5e9;          /* sky-500 */
4737  --cd-bg2:#6366f1;          /* indigo-500 */
4738  --cd-text:#0b1220;
4739  --cd-contrast:#ffffff;
4740  --cd-shadow: 0 8px 24px -10px rgba(0,0,0,.25);
4741  --cd-border: rgba(255,255,255,.25);
4742  --cd-box: rgba(255,255,255,.65);
4743  --cd-box-text:#0f172a;
4744  --cd-danger:#e2a642;
4745
4746  display:flex; gap:.75rem; align-items:center;
4747  background:linear-gradient(135deg,var(--cd-bg1),var(--cd-bg2));
4748  border:1px solid var(--cd-border);
4749  color:var(--cd-contrast);
4750  border-radius:14px;
4751  padding:.6rem .8rem;
4752  box-shadow:var(--cd-shadow);
4753  position:relative;
4754  overflow:hidden;
4755  margin-bottom: 1rem;
4756}
4757
4758.pd-countdown-modern .pd-cd-icon{
4759  width:28px;height:28px;flex:0 0 28px;
4760  display:grid;place-items:center;
4761  background:rgba(255,255,255,.22);
4762  border:1px solid var(--cd-border);
4763  border-radius:10px;
4764  box-shadow: inset 0 1px 0 rgba(255,255,255,.35);
4765}
4766.pd-countdown-modern .pd-cd-icon svg{width:18px;height:18px;fill:#fff;opacity:.95}
4767
4768.pd-countdown-modern .pd-cd-body{display:flex;flex-direction:column;gap:.35rem;min-width:0}
4769
4770.pd-countdown-modern .pd-cd-label{
4771  font-size:.72rem;font-weight:700;letter-spacing:.4px;opacity:.95;line-height:1;
4772  text-shadow:0 1px 0 rgba(0,0,0,.08);
4773}
4774
4775.pd-countdown-modern .pd-cd-time{
4776  display:flex; align-items:end; gap:.35rem; flex-wrap:wrap;
4777  line-height:1;
4778}
4779
4780.pd-countdown-modern .unit{
4781  display:flex; flex-direction:column; align-items:center; gap:.18rem;
4782  min-width:58px;
4783}
4784.pd-countdown-modern .num{
4785  display:inline-flex; align-items:center; justify-content:center;
4786  min-width:58px; height:40px; padding:0 .4rem;
4787  background:var(--cd-box);
4788  color:var(--cd-box-text);
4789  border:1px solid rgba(15,23,42,.08);
4790  border-radius:10px;
4791  font: 800 1.05rem ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
4792  letter-spacing:.5px;
4793  box-shadow: 0 4px 14px -6px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.55);
4794}
4795.pd-countdown-modern .unit-label{
4796  font-size:.62rem; font-weight:700; opacity:.9; text-shadow:0 1px 0 rgba(255,255,255,.15);
4797}
4798
4799.pd-countdown-modern .colon{
4800  font-weight:900; font-size:1.05rem; opacity:.95; transform: translateY(-2px);
4801  animation: cdBlink 1.2s infinite steps(2, jump-none);
4802}
4803@keyframes cdBlink { 0%{opacity:1} 50%{opacity:.25} 100%{opacity:1} }
4804
4805.pd-countdown-modern .pd-cd-progress{
4806  display:none;
4807  position:relative;
4808  height:6px; border-radius:999px; overflow:hidden;
4809  background:rgba(255,255,255,.25);
4810  border:1px solid rgba(255,255,255,.28);
4811}
4812.pd-countdown-modern .pd-cd-progress .bar{
4813  height:100%; width:0%; background:linear-gradient(90deg,#22c55e,#a3e635);
4814  box-shadow: 0 2px 8px rgba(0,0,0,.25) inset;
4815  transition:width .35s ease;
4816}
4817
4818/* State saat hampir habis */
4819.pd-countdown-modern.danger{
4820  --cd-bg1:#C10DFF; /* orange-500 */
4821  --cd-bg2:#e2a642; /* red-500   */
4822}
4823
4824/* Responsive */
4825@media (max-width:640px){
4826  .pd-countdown-modern{
4827    padding:.55rem .65rem; border-radius:12px; gap:.6rem;
4828  }
4829  .pd-countdown-modern .unit{min-width:52px}
4830  .pd-countdown-modern .num{min-width:52px;height:36px;font-size:.98rem}
4831}
4832
4833/* === Modern Countdown polish (shine + compact) === */
4834.pd-countdown-modern{
4835  position:relative;
4836}
4837.pd-countdown-modern::after{
4838  content:"";
4839  position:absolute; inset:-1px;
4840  background: linear-gradient(120deg, rgba(255,255,255,.12), rgba(255,255,255,0) 35%),
4841              radial-gradient(120% 120% at 0% 0%, rgba(255,255,255,.08), rgba(255,255,255,0) 60%);
4842  border-radius: inherit;
4843  pointer-events:none;
4844  mix-blend-mode: screen;
4845  animation: sheen 4.5s ease-in-out infinite;
4846}
4847@keyframes sheen {
4848  0%   { opacity:.35; transform: translateX(-10%) }
4849  50%  { opacity:.15; transform: translateX(4%) }
4850  100% { opacity:.35; transform: translateX(-10%) }
4851}
4852
4853/* Mode compact (aktifkan dengan data-mode="compact" di elemen) */
4854.pd-countdown-modern[data-mode="compact"]{
4855  gap:.55rem; padding:.5rem .6rem; border-radius:12px;
4856}
4857.pd-countdown-modern[data-mode="compact"] .unit{
4858  min-width:46px;
4859}
4860.pd-countdown-modern[data-mode="compact"] .num{
4861  min-width:46px; height:34px; font-size:.92rem;
4862}
4863.pd-countdown-modern[data-mode="compact"] .pd-cd-label{
4864  font-size:.66rem;
4865}
4866.pd-countdown-modern[data-mode="compact"] .pd-cd-icon{
4867  width:24px; height:24px; border-radius:8px;
4868}
4869.pd-countdown-modern[data-mode="compact"] .pd-cd-progress{
4870  height:5px;
4871}
4872
4873/* ============================================================
4874   Sidebar: shared refinements for medium + tablet (641px – 1250px)
4875   Buy card = form & paylater on the same row.
4876   Alt-fintech logos compact (no over-stretching).
4877   ============================================================ */
4878@media (min-width:641px) and (max-width:1250px) {
4879  /* Buy card: put qty/buy buttons and paylater promo inline */
4880  .pd-buy-card {
4881    display: flex !important;
4882    flex-direction: row;
4883    flex-wrap: wrap;
4884    align-items: center;
4885    gap: .6rem .75rem;
4886  }
4887  .pd-buy-card .pd-buy-form { flex: 1 1 0; min-width: 0; }
4888  .pd-buy-card .paylater-promo {
4889    flex: 0 0 auto;
4890    align-self: center;
4891    margin: 0;
4892    max-width: 44%;
4893  }
4894
4895  /* Alt-fintech banner: tighter logos */
4896  .iab-logos-compact {
4897    justify-content: center;
4898    gap: 1.25rem;
4899  }
4900  .iab-logos-compact .logo-fin {
4901    width: auto;
4902    max-height: 28px;
4903    object-fit: contain;
4904  }
4905}
4906
4907/* ============================================================
4908   Tablet layout (641px – 880px)
4909   Restores 2-column grid + horizontal sidebar for mid-size screens.
4910   Overrides the generic ≤880px mobile-collapse rules above.
4911   ============================================================ */
4912@media (min-width:641px) and (max-width:880px) {
4913  /* 2-column grid: image-card | info-card */
4914  .pd-top-layout {
4915    grid-template-columns: minmax(260px, 40%) 1fr;
4916    gap: 1rem;
4917  }
4918
4919  /* Sidebar: full-width horizontal flex row below the two main columns */
4920  .pd-sidebar {
4921    grid-column: 1 / -1;
4922    flex-direction: row;
4923    flex-wrap: wrap;
4924    gap: .75rem;
4925    padding: 0;
4926  }
4927  /* Banner cards fill 2-per-row */
4928  .pd-side-card { flex: 1 1 calc(50% - .375rem); }
4929  /* Buy card: full row (tallest card – avoids white-space gap if shared with shorter banners) */
4930  .pd-buy-card  { display: block !important; flex: 0 0 100%; }
4931
4932  /* Hide installment simulator + alt-fintech banner on tablet (too much vertical space) */
4933  .pd-installment-card2,
4934  .inst-alt-banner--compact { display: none !important; }
4935
4936  /* Restore sticky add-to-cart header (hidden at ≤880px by default) */
4937  .pd-sticky-header { display: flex !important; }
4938
4939  /* Typography – tighten slightly to fit the narrower info column */
4940  .pd-price-current { font-size: 1.65rem; }
4941  .pd-title         { font-size: 1.1rem;  }
4942  .pd-info-card     { padding: 1.1rem 1.2rem 1.5rem; }
4943}
4944</style>
4945  <style data-inlined="product-grid.css">
4946/* Product Grid (Firebird) */
4947
4948.product-archive {
4949  max-width: 1320px;
4950  margin-inline: auto;
4951  padding: 0 0 3rem;
4952  /*font-family: system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,sans-serif;*/
4953}
4954
4955.pa-toolbar {
4956  display: flex;
4957  flex-wrap: wrap;
4958  gap: 1rem;
4959  justify-content: space-between;
4960  align-items: flex-start;
4961}
4962
4963.pa-title {
4964  font-size: 1.4rem;
4965  margin: 0;
4966  font-weight: 600;
4967  line-height: 1.2;
4968  color: #0f172a;
4969}
4970
4971.pa-left {
4972  display: flex;
4973  flex-wrap: wrap;
4974  gap: .75rem;
4975  align-items: center;
4976}
4977
4978.pa-filter-badge {
4979  background: #f1f5f9;
4980  border: 1px solid #e2e8f0;
4981  padding: .45rem .6rem;
4982  border-radius: 8px;
4983  font-size: .7rem;
4984  display: flex;
4985  align-items: center;
4986  gap: .4rem;
4987  line-height: 1;
4988  color: #475569;
4989}
4990.pa-filter-badge strong { color: #0f172a; }
4991.pa-filter-badge .clear {
4992  display:inline-block;
4993  text-decoration:none;
4994  background:#e2e8f0;
4995  color:#475569;
4996  font-weight:600;
4997  padding:0 .35rem .05rem;
4998  border-radius:4px;
4999  transition:.2s;
5000  font-size:.75rem;
5001  line-height:1;
5002}
5003.pa-filter-badge .clear:hover { background:#cbd5e1; color:#1e293b; }
5004
5005.pa-right {
5006  display:flex;
5007  gap:1rem;
5008  flex-wrap:wrap;
5009  align-items:flex-start;
5010  margin-bottom: 0.5rem;
5011}
5012
5013.pa-search {
5014  display:flex;
5015  gap:.5rem;
5016  align-items:center;
5017  background:#fff;
5018  border:1px solid #e2e8f0;
5019  padding:.4rem .5rem;
5020  border-radius:8px;
5021}
5022.pa-search input[type=text] {
5023  border:0;
5024  outline:none;
5025  font-size:.8rem;
5026  padding:.35rem .4rem;
5027  min-width:200px;
5028}
5029.pa-search button {
5030  background:#0d6efd;
5031  border:0;
5032  color:#fff;
5033  font-size:.7rem;
5034  padding:.45rem .75rem;
5035  border-radius:6px;
5036  cursor:pointer;
5037  font-weight:600;
5038  letter-spacing:.3px;
5039  transition:.25s;
5040}
5041.pa-search button:hover { background:#0b5dc4; }
5042
5043.pa-sort {
5044  display:flex;
5045  gap:.4rem;
5046  align-items:center;
5047  font-size:.7rem;
5048  background:#fff;
5049  border:1px solid #e2e8f0;
5050  padding:.4rem .55rem;
5051  border-radius:8px;
5052}
5053.pa-sort select {
5054  font-size:.75rem;
5055  padding:.35rem .5rem;
5056  border:1px solid #cbd5e1;
5057  border-radius:6px;
5058  background:#fff;
5059  outline:none;
5060}
5061
5062.pa-empty {
5063  border:1px dashed #cbd5e1;
5064  padding:3rem 1rem;
5065  text-align:center;
5066  border-radius:12px;
5067  background:#fff;
5068}
5069.pa-empty p {
5070  margin:0 0 1rem;
5071  font-size:.9rem;
5072  color:#475569;
5073}
5074.btn-reset {
5075  display:inline-block;
5076  background:#0d6efd;
5077  color:#fff;
5078  font-size:.7rem;
5079  padding:.55rem 1rem;
5080  border-radius:8px;
5081  text-decoration:none;
5082  font-weight:600;
5083  letter-spacing:.4px;
5084  transition:.25s;
5085}
5086.btn-reset:hover { background:#0a58c7; }
5087
5088/* Grid */
5089.pa-grid {
5090  display:grid;
5091  gap:1rem;
5092  grid-template-columns:repeat(auto-fill, minmax(180px,1fr));
5093  align-items:stretch;
5094}
5095
5096.pa-card {
5097  background:#fff;
5098  border:1px solid #e2e8f0;
5099  border-radius:12px;
5100  display:flex;
5101  flex-direction:column;
5102  text-decoration:none;
5103  color:inherit;
5104  position:relative;
5105  overflow:hidden;
5106  transition:.28s cubic-bezier(.4,0,.2,1);
5107  min-width:0;
5108  padding: 0;
5109}
5110.pa-card:hover {
5111  transform:translateY(-4px);
5112  box-shadow:0 8px 22px -8px rgba(0,0,0,.15);
5113  border-color:#dce2ea;
5114}
5115
5116.pa-card .thumb {
5117  aspect-ratio:1/1;
5118  background:#f1f5f9;
5119  display:flex;
5120  align-items:center;
5121  justify-content:center;
5122  overflow:hidden;
5123  position:relative;
5124}
5125.pa-card img {
5126  width:100%;
5127  height:100%;
5128  object-fit:cover;
5129  display:block;
5130}
5131.badge-disc {
5132  position:absolute;
5133  top:8px;
5134  left:8px;
5135  background:#e2a642;
5136  color:#fff;
5137  font-size:.6rem;
5138  padding:4px 6px 3px;
5139  border-radius:6px;
5140  font-weight:700;
5141  letter-spacing:.4px;
5142}
5143
5144.pa-card .body {
5145  padding:.65rem .7rem .8rem;
5146  display:flex;
5147  flex-direction:column;
5148  gap:.45rem;
5149  min-height:90px;
5150}
5151
5152.pa-card .title {
5153  font-size:.75rem;
5154  line-height:1.25;
5155  margin:0;
5156  font-weight:600;
5157  color:#1e293b;
5158  height:2.4em;
5159  overflow:hidden;
5160}
5161
5162.pa-card .prices {
5163  display:flex;
5164  flex-wrap:wrap;
5165  align-items:baseline;
5166  gap:.4rem;
5167}
5168.pa-card .prices .final {
5169  font-size:.83rem;
5170  font-weight:700;
5171  color:#0d6efd;
5172}
5173.pa-card .prices .ori {
5174  font-size:.63rem;
5175  text-decoration:line-through;
5176  color:#94a3b8;
5177  font-weight:500;
5178}
5179
5180.pa-card .cat {
5181  margin-top:auto;
5182  font-size:.55rem;
5183  letter-spacing:.6px;
5184  text-transform:uppercase;
5185  background:#f1f5f9;
5186  border:1px solid #e2e8f0;
5187  padding:.28rem .45rem .25rem;
5188  border-radius:20px;
5189  font-weight:600;
5190  color:#475569;
5191  align-self:flex-start;
5192}
5193.pa-card .meta-line {
5194    margin-top:4px;
5195    font-size:.65rem;
5196    color:#475569;
5197}
5198.pa-card .meta-line .sold {
5199    display:inline-block;
5200    background:#f1f5f9;
5201    padding:2px 6px 3px;
5202    border-radius:4px;
5203    font-weight:600;
5204    letter-spacing:.3px;
5205    color:#334155;
5206}
5207
5208/* Pagination */
5209.pa-pagination {
5210  margin-top:2rem;
5211  display:flex;
5212  flex-wrap:wrap;
5213  gap:.4rem;
5214  justify-content:center;
5215  font-size:.7rem;
5216  align-items:center;
5217}
5218.pa-pagination a,
5219.pa-pagination span {
5220  min-width:34px;
5221  height:34px;
5222  display:flex;
5223  align-items:center;
5224  justify-content:center;
5225  border-radius:8px;
5226  text-decoration:none;
5227  font-weight:600;
5228  padding:0 .6rem;
5229  box-sizing:border-box;
5230  border:1px solid #e2e8f0;
5231  background:#fff;
5232  color:#334155;
5233  transition:.25s;
5234}
5235.pa-pagination a:hover {
5236  background:#f1f5f9;
5237  color:#0f172a;
5238}
5239.pa-pagination .current {
5240  background:#0d6efd;
5241  color:#fff;
5242  border-color:#0d6efd;
5243}
5244.pa-pagination .gap {
5245  border:none;
5246  background:transparent;
5247  font-weight:400;
5248  color:#94a3b8;
5249}
5250
5251/* =============== Mobile Filter Bar (Tokopedia-like) =============== */
5252.pa-mobile-filterbar{display:none}
5253@media (max-width: 640px){
5254  /* sembunyikan tombol lama */
5255  .pa-btn-filter{display:none}
5256
5257  .pa-mobile-filterbar{
5258    display:block;
5259    position:sticky;
5260    top:0; /* sesuaikan jika ada header fixed di atas */
5261    z-index:5;
5262    background:#fff;
5263    border-bottom:1px solid #e2e8f0;
5264    padding:.4rem .5rem;
5265    margin:-.4rem -1rem .6rem; /* full-bleed di container mobile */
5266  }
5267  .pa-mobile-filterbar .mf-scroll{
5268    display:flex;align-items:center;gap:.5rem;
5269    overflow-x:auto;
5270    -webkit-overflow-scrolling:touch;
5271    scrollbar-width:none;
5272  }
5273  .pa-mobile-filterbar .mf-scroll::-webkit-scrollbar{display:none}
5274
5275  .mf-chip{
5276    flex:0 0 auto;
5277    background:#fff;
5278    color:#334155;
5279    text-decoration:none;
5280    border:1px solid #e2e8f0;
5281    border-radius:999px;
5282    padding:.44rem .7rem;
5283    font-size:.78rem;
5284    font-weight:600;
5285    letter-spacing:.2px;
5286    white-space:nowrap;
5287  }
5288  .mf-chip:active{transform:scale(.98)}
5289  .mf-chip.active{
5290    background:#eaf2ff;
5291    border-color:#0d6efd;
5292    color:#0d6efd;
5293  }
5294
5295  .mf-filter-btn{
5296    position:relative;
5297    flex:0 0 auto;
5298    width:38px;height:38px;
5299    border-radius:50%;
5300    border:1.5px solid #0d6efd;
5301    background:#fff;
5302    display:inline-flex;align-items:center;justify-content:center;
5303    color:#0d6efd;
5304    cursor:pointer;
5305  }
5306  .mf-filter-btn:active{transform:scale(.96)}
5307  .mf-filter-btn svg{width:18px;height:18px;display:block}
5308  .mf-filter-btn .mf-badge{
5309    position:absolute;top:0;right:-4px;
5310    min-width:18px;height:18px;border-radius:9px;
5311    background:#e2a642;color:#fff;
5312    font-size:.65rem;font-weight:700;
5313    display:flex;align-items:center;justify-content:center;
5314    padding:0 4px;line-height:1;
5315    box-shadow:0 2px 6px -2px rgba(226,166,66,.6);
5316  }
5317}
5318
5319/* Responsive */
5320@media (max-width: 640px) {
5321  .product-archive { padding: 0 1rem 3rem; }
5322  .pa-grid { grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); }
5323  .pa-search input[type=text]{ min-width:140px; }
5324  .pa-toolbar { flex-direction:column; align-items:stretch; margin-bottom: 0; display: none;}
5325  .pa-right { display: none; }
5326  .pa-sort { width: 100%; justify-content:center; }
5327  .pa-mobile-filterbar { display: flex !important; }
5328}
5329
5330.pa-mobile-filterbar {
5331  display: none;
5332  align-items: center;
5333  gap: 8px;
5334  padding: 8px 0 8px 12px;
5335  background: #fff;
5336  width: 100vw;
5337}
5338.pa-mobile-filterbar .mf-scroll {
5339  display: flex;
5340  gap: 8px;
5341  overflow-x: auto;
5342  -webkit-overflow-scrolling: touch;
5343  scrollbar-width: none; /* Firefox */
5344  flex: 1 1 auto; /* takes remaining width */
5345}
5346.pa-mobile-filterbar .mf-scroll::-webkit-scrollbar { display: none; } /* WebKit */
5347.pa-mobile-filterbar .mf-chip {
5348  flex: 0 0 auto; /* prevent shrinking, keep width based on content */
5349  white-space: nowrap;
5350}
5351.pa-mobile-filterbar .mf-filter-btn {
5352  flex: 0 0 auto; /* do not shrink */
5353  position: relative; /* keep in place, not inside scroller */
5354  display: inline-flex;
5355  align-items: center;
5356  gap: 6px;
5357}
5358
5359/* Optional: ensure on small screens the button stays visible with min width */
5360@media (max-width: 768px) {
5361  .pa-mobile-filterbar .mf-filter-btn {
5362    padding: 6px 10px;
5363  }
5364}
5365
5366/* Product Specification Chips */
5367.pa-card .spec-chips {
5368  font-size: .6rem;
5369  color: #64748b;
5370  font-weight: 500;
5371  letter-spacing: .2px;
5372  margin-top: 2px;
5373  margin-bottom: 4px;
5374  line-height: 1.2;
5375  opacity: 0.9;
5376  max-width: 100%;
5377  overflow: hidden;
5378  text-overflow: ellipsis;
5379  white-space: nowrap;
5380}
5381
5382.pa-card .spec-chips .spec-divider {
5383  color: #94a3b8;
5384  font-weight: 400;
5385  margin: 0 1px;
5386}
5387</style>
5388  <style data-inlined="site-notice.css">
5389/**
5390 * Site Notice Banner - Reusable Notification System
5391 * Dismissible top bar for announcements, holidays, maintenance, etc.
5392 */
5393
5394.holiday-notice {
5395    position: fixed;
5396    top: 0;
5397    left: 0;
5398    right: 0;
5399    z-index: 9999;
5400    background: linear-gradient(135deg, #e2a642 0%, #C10DFF 100%);
5401    border-bottom: 2px solid rgba(255, 255, 255, 0.2);
5402    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
5403    animation: slideDown 0.4s cubic-bezier(0.4, 0, 0.2, 1);
5404    transform-origin: top;
5405}
5406
5407.holiday-notice.hidden {
5408    animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
5409}
5410
5411@keyframes slideDown {
5412    from {
5413        transform: translateY(-100%);
5414        opacity: 0;
5415    }
5416    to {
5417        transform: translateY(0);
5418        opacity: 1;
5419    }
5420}
5421
5422@keyframes slideUp {
5423    from {
5424        transform: translateY(0);
5425        opacity: 1;
5426    }
5427    to {
5428        transform: translateY(-100%);
5429        opacity: 0;
5430    }
5431}
5432
5433.holiday-notice-container {
5434    max-width: 1400px;
5435    margin: 0 auto;
5436    padding: 12px 20px;
5437    display: flex;
5438    align-items: center;
5439    justify-content: space-between;
5440    gap: 16px;
5441}
5442
5443.holiday-notice-content {
5444    display: flex;
5445    align-items: center;
5446    gap: 12px;
5447    flex: 1;
5448}
5449
5450.holiday-notice-icon {
5451    font-size: 20px;
5452    line-height: 1;
5453    flex-shrink: 0;
5454}
5455
5456.holiday-notice-text {
5457    color: #ffffff;
5458    font-size: 14px;
5459    font-weight: 500;
5460    line-height: 1.5;
5461    margin: 0;
5462    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
5463}
5464
5465.holiday-notice-emoji {
5466    display: inline-block;
5467    animation: bounce 2s ease-in-out infinite;
5468}
5469
5470@keyframes bounce {
5471    0%, 100% {
5472        transform: translateY(0);
5473    }
5474    50% {
5475        transform: translateY(-4px);
5476    }
5477}
5478
5479.holiday-notice-close {
5480    background: rgba(255, 255, 255, 0.2);
5481    border: 1px solid rgba(255, 255, 255, 0.3);
5482    border-radius: 6px;
5483    padding: 6px;
5484    cursor: pointer;
5485    color: #ffffff;
5486    transition: all 0.2s ease;
5487    flex-shrink: 0;
5488    display: flex;
5489    align-items: center;
5490    justify-content: center;
5491    width: 32px;
5492    height: 32px;
5493}
5494
5495.holiday-notice-close:hover {
5496    background: rgba(255, 255, 255, 0.3);
5497    transform: scale(1.05);
5498}
5499
5500.holiday-notice-close:active {
5501    transform: scale(0.95);
5502}
5503
5504.holiday-notice-close:focus {
5505    outline: 2px solid rgba(255, 255, 255, 0.5);
5506    outline-offset: 2px;
5507}
5508
5509/* Body padding adjustment when banner is visible */
5510body.has-holiday-notice {
5511    padding-top: 52px;
5512}
5513
5514/* Tablet Styles */
5515@media (max-width: 1024px) {
5516    .holiday-notice-container {
5517        padding: 10px 16px;
5518    }
5519    
5520    .holiday-notice-text {
5521        font-size: 13px;
5522    }
5523    
5524    .holiday-notice-icon {
5525        font-size: 18px;
5526    }
5527    
5528    body.has-holiday-notice {
5529        padding-top: 48px;
5530    }
5531}
5532
5533/* Mobile Styles */
5534@media (max-width: 640px) {
5535    .holiday-notice-container {
5536        padding: 10px 12px;
5537        gap: 8px;
5538    }
5539    
5540    .holiday-notice-content {
5541        gap: 8px;
5542    }
5543    
5544    .holiday-notice-text {
5545        font-size: 12px;
5546        line-height: 1.4;
5547    }
5548    
5549    .holiday-notice-icon {
5550        font-size: 16px;
5551    }
5552    
5553    .holiday-notice-close {
5554        width: 28px;
5555        height: 28px;
5556    }
5557    
5558    .holiday-notice-close svg {
5559        width: 16px;
5560        height: 16px;
5561    }
5562    
5563    body.has-holiday-notice {
5564        padding-top: 44px;
5565    }
5566}
5567
5568/* Ensure header doesn't get covered - removed z-index as it conflicts with dropdown stacking */
5569/* .header,
5570.navbar,
5571header {
5572    position: relative;
5573    z-index: 100;
5574} */
5575
5576</style>
5577        <style data-inlined="benchmark.css">
5578/**
5579 * Benchmark Section Styles
5580 * For product detail page benchmark display
5581 */
5582
5583.pd-benchmark-section {
5584    background: #fff;
5585    border-radius: 12px;
5586    padding: 24px;
5587    margin: 32px 0;
5588    box-shadow: 0 2px 8px rgba(0,0,0,0.06);
5589    border: 1px solid #e5e7eb;
5590}
5591
5592.benchmark-header {
5593    margin-bottom: 20px;
5594}
5595
5596.benchmark-title {
5597    font-size: 22px;
5598    font-weight: 700;
5599    color: #1a1a1a;
5600    margin: 0 0 12px 0;
5601    display: flex;
5602    align-items: center;
5603    gap: 8px;
5604}
5605
5606.benchmark-hint {
5607    font-size: 14px;
5608    color: #6b7280;
5609    margin: 0;
5610}
5611
5612/* Preview Badges */
5613.benchmark-preview-badges {
5614    display: flex;
5615    flex-wrap: wrap;
5616    gap: 10px;
5617    margin-top: 12px;
5618}
5619
5620.benchmark-badge {
5621    display: inline-flex;
5622    align-items: center;
5623    gap: 6px;
5624    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
5625    color: #fff;
5626    padding: 8px 16px;
5627    border-radius: 20px;
5628    font-size: 13px;
5629    font-weight: 500;
5630    box-shadow: 0 2px 6px rgba(102, 126, 234, 0.25);
5631    transition: all 0.2s ease;
5632}
5633
5634.benchmark-badge:hover {
5635    transform: translateY(-2px);
5636    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.35);
5637}
5638
5639.benchmark-badge strong {
5640    font-weight: 600;
5641    opacity: 0.9;
5642}
5643
5644/* Load Button */
5645.btn-load-benchmark {
5646    display: flex;
5647    align-items: center;
5648    justify-content: center;
5649    gap: 10px;
5650    width: 100%;
5651    max-width: 420px;
5652    padding: 14px 24px;
5653    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
5654    color: #fff;
5655    border: none;
5656    border-radius: 8px;
5657    font-size: 15px;
5658    font-weight: 600;
5659    cursor: pointer;
5660    transition: all 0.3s ease;
5661    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
5662    margin: 0 auto;
5663}
5664
5665.btn-load-benchmark:hover:not(:disabled) {
5666    transform: translateY(-2px);
5667    box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
5668}
5669
5670.btn-load-benchmark:disabled {
5671    opacity: 0.6;
5672    cursor: not-allowed;
5673}
5674
5675.btn-load-benchmark .btn-icon {
5676    font-size: 20px;
5677}
5678
5679.btn-load-benchmark.loading .btn-icon {
5680    animation: spin 1s linear infinite;
5681}
5682
5683@keyframes spin {
5684    from { transform: rotate(0deg); }
5685    to { transform: rotate(360deg); }
5686}
5687
5688/* Benchmark Content */
5689.benchmark-content {
5690    margin-top: 24px;
5691    padding-top: 24px;
5692    border-top: 2px solid #f3f4f6;
5693}
5694
5695.benchmark-loading {
5696    text-align: center;
5697    padding: 40px 20px;
5698    color: #6b7280;
5699}
5700
5701.benchmark-loading-spinner {
5702    width: 48px;
5703    height: 48px;
5704    margin: 0 auto 16px;
5705    border: 4px solid #f3f4f6;
5706    border-top-color: #667eea;
5707    border-radius: 50%;
5708    animation: spin 1s linear infinite;
5709}
5710
5711.benchmark-error {
5712    text-align: center;
5713    padding: 32px 20px;
5714    color: #e2a642;
5715    background: #fee2e2;
5716    border-radius: 8px;
5717}
5718
5719/* Benchmark Data Display */
5720.benchmark-data-grid {
5721    display: grid;
5722    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
5723    gap: 16px;
5724    margin-bottom: 24px;
5725}
5726
5727.benchmark-card {
5728    background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%);
5729    border: 1px solid #e5e7eb;
5730    border-radius: 10px;
5731    padding: 18px;
5732    text-align: center;
5733    transition: all 0.2s ease;
5734}
5735
5736.benchmark-card:hover {
5737    transform: translateY(-3px);
5738    box-shadow: 0 6px 16px rgba(102, 126, 234, 0.15);
5739}
5740
5741.benchmark-card-label {
5742    font-size: 13px;
5743    color: #6b7280;
5744    font-weight: 500;
5745    margin-bottom: 8px;
5746    text-transform: uppercase;
5747    letter-spacing: 0.5px;
5748}
5749
5750.benchmark-card-value {
5751    font-size: 24px;
5752    font-weight: 700;
5753    color: #1a1a1a;
5754    line-height: 1.2;
5755}
5756
5757.benchmark-card-value.large {
5758    font-size: 28px;
5759}
5760
5761.benchmark-card-subtitle {
5762    font-size: 12px;
5763    color: #9ca3af;
5764    margin-top: 4px;
5765}
5766
5767/* Spec Details */
5768.benchmark-specs {
5769    background: #f9fafb;
5770    border-radius: 10px;
5771    padding: 20px;
5772    margin-top: 20px;
5773}
5774
5775.benchmark-specs-title {
5776    font-size: 16px;
5777    font-weight: 600;
5778    color: #1a1a1a;
5779    margin: 0 0 16px 0;
5780}
5781
5782.benchmark-spec-row {
5783    display: flex;
5784    justify-content: space-between;
5785    padding: 10px 0;
5786    border-bottom: 1px solid #e5e7eb;
5787}
5788
5789.benchmark-spec-row:last-child {
5790    border-bottom: none;
5791}
5792
5793.benchmark-spec-label {
5794    font-size: 14px;
5795    color: #6b7280;
5796    font-weight: 500;
5797}
5798
5799.benchmark-spec-value {
5800    font-size: 14px;
5801    color: #1a1a1a;
5802    font-weight: 600;
5803    text-align: right;
5804}
5805
5806/* Score Badges */
5807.benchmark-score-badge {
5808    display: inline-block;
5809    padding: 6px 14px;
5810    border-radius: 20px;
5811    font-size: 13px;
5812    font-weight: 600;
5813    margin-top: 8px;
5814}
5815
5816.benchmark-score-badge.excellent {
5817    background: #10b981;
5818    color: #fff;
5819}
5820
5821.benchmark-score-badge.good {
5822    background: #3b82f6;
5823    color: #fff;
5824}
5825
5826.benchmark-score-badge.average {
5827    background: #C10DFF;
5828    color: #fff;
5829}
5830
5831.benchmark-score-badge.poor {
5832    background: #e2a642;
5833    color: #fff;
5834}
5835
5836/* Attribution */
5837.benchmark-attribution {
5838    text-align: center;
5839    margin-top: 24px;
5840    padding-top: 20px;
5841    border-top: 1px solid #e5e7eb;
5842    font-size: 13px;
5843    color: #9ca3af;
5844}
5845
5846.benchmark-attribution a {
5847    color: #667eea;
5848    text-decoration: none;
5849    font-weight: 500;
5850}
5851
5852.benchmark-attribution a:hover {
5853    text-decoration: underline;
5854}
5855
5856/* Responsive */
5857@media (max-width: 768px) {
5858    .pd-benchmark-section {
5859        padding: 16px;
5860        margin: 20px 0;
5861    }
5862    
5863    .benchmark-title {
5864        font-size: 18px;
5865    }
5866    
5867    .benchmark-preview-badges {
5868        flex-direction: column;
5869        gap: 8px;
5870    }
5871    
5872    .benchmark-badge {
5873        justify-content: center;
5874    }
5875    
5876    .btn-load-benchmark {
5877        font-size: 14px;
5878        padding: 12px 20px;
5879    }
5880    
5881    .benchmark-data-grid {
5882        grid-template-columns: 1fr 1fr;
5883        gap: 12px;
5884    }
5885    
5886    .benchmark-card {
5887        padding: 14px;
5888    }
5889    
5890    .benchmark-card-value {
5891        font-size: 20px;
5892    }
5893    
5894    .benchmark-spec-row {
5895        flex-direction: column;
5896        gap: 4px;
5897    }
5898    
5899    .benchmark-spec-value {
5900        text-align: left;
5901    }
5902}
5903
5904@media (max-width: 480px) {
5905    .benchmark-data-grid {
5906        grid-template-columns: 1fr;
5907    }
5908}
5909
5910</style>
5911    <style data-inlined="product-filters.css">
5912/* =========================================================
5913   Product Filters (Desktop + Mobile Drawer)
5914   Light Only + FIX:
5915   - Desktop: f-actions selalu tampil (static bottom)
5916   - Mobile: konten filter dapat discroll
5917   ========================================================= */
5918
5919/* ========== Layout wrapper (tidak mengubah grid produk) ========== */
5920.product-archive.with-filters .pa-layout {
5921  display:flex;
5922  gap:24px;
5923  align-items:flex-start;
5924}
5925.pa-main { flex:1; min-width:0; }
5926
5927/* =========================================================
5928   DESKTOP PANEL
5929   ========================================================= */
5930.pa-filters {
5931  width:300px;
5932  background:linear-gradient(145deg,#ffffff 0%,#f8fafc 55%,#f1f5f9 100%);
5933  border:1px solid #e2e8f0;
5934  padding:18px 18px 12px; /* bottom dikurangi karena f-actions punya padding sendiri */
5935  border-radius:18px;
5936  position:sticky;
5937  top:80px;
5938  height:calc(100vh - 100px); /* tinggi stabil untuk inner scroll */
5939  font-size:14px;
5940  box-shadow:0 1px 2px rgba(0,0,0,.04),0 4px 14px rgba(15,23,42,.08);
5941  backdrop-filter:blur(3px);
5942  -webkit-backdrop-filter:blur(3px);
5943  transition:box-shadow .3s, background .4s;
5944  display:flex;
5945  flex-direction:column;
5946  overflow:hidden; /* cegah double scroll, inner yang scroll */
5947}
5948.pa-filters::before {
5949  content:"";
5950  position:absolute;
5951  inset:0 auto 0 0;
5952  width:7px;
5953  border-radius:18px 0 0 18px;
5954  background:linear-gradient(180deg,#C10DFF 0%,#C10DFF 35%,#1d6ce0 100%);
5955}
5956.pa-filters.active { box-shadow:0 8px 32px -4px rgba(15,23,42,.35); }
5957
5958/* Scroll area (desktop/mode shared) */
5959.filters-scroll {
5960  flex:1;
5961  min-height:0;          /* wajib agar overflow terbaca dalam flex container */
5962  overflow:auto;
5963  padding-right:6px;
5964  margin-right:-6px;      /* kompensasi scrollbar */
5965  scrollbar-width:thin;
5966  overscroll-behavior:contain;
5967}
5968.filters-scroll::-webkit-scrollbar { width:8px; }
5969.filters-scroll::-webkit-scrollbar-thumb {
5970  background:#cbd5e1;
5971  border-radius:20px;
5972  border:2px solid #f1f5f9;
5973}
5974.filters-scroll::-webkit-scrollbar-thumb:hover { background:#94a3b8; }
5975
5976/* Heading */
5977.pa-filters h2 {
5978  margin:0 0 14px;
5979  font-size:20px;
5980  font-weight:700;
5981  letter-spacing:.4px;
5982  line-height:1.15;
5983  background:linear-gradient(90deg,#0f172a,#1d6ce0 60%,#0f172a);
5984  -webkit-background-clip:text;
5985  color:transparent;
5986  background-size:200% 100%;
5987  animation:titleSheen 8s linear infinite;
5988}
5989@keyframes titleSheen {
5990  0%{background-position:0 0}
5991  50%{background-position:100% 0}
5992  100%{background-position:0 0}
5993}
5994
5995/* Form groups */
5996.pa-filters .f-group { margin-bottom:18px; position:relative; }
5997.pa-filters label {
5998  display:block;
5999  font-weight:600;
6000  margin:0 0 6px;
6001  font-size:13px;
6002  color:#0f172a;
6003  letter-spacing:.3px;
6004}
6005
6006/* Inputs / selects */
6007.pa-filters input[type=text],
6008.pa-filters input[type=number],
6009.pa-filters select {
6010  width:100%;
6011  padding:7px 10px;
6012  border:1px solid #cbd5e1;
6013  border-radius:8px;
6014  font-size:13px;
6015  min-height:34px;
6016  background:#ffffff;
6017  box-sizing:border-box;
6018  color:#0f172a;
6019  transition:border-color .25s, box-shadow .25s, background .25s;
6020  -webkit-appearance:none;
6021  -moz-appearance:none;
6022  appearance:none;
6023}
6024.pa-filters select {
6025  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");
6026  background-repeat:no-repeat;
6027  background-position:right 10px center;
6028  background-size:12px;
6029  padding-right:32px;
6030}
6031.pa-filters input[type=text]:hover,
6032.pa-filters input[type=number]:hover,
6033.pa-filters select:hover { border-color:#94b5ec; }
6034.pa-filters input[type=text]:focus,
6035.pa-filters input[type=number]:focus,
6036.pa-filters select:focus {
6037  outline:none;
6038  border-color:#1d6ce0;
6039  box-shadow:0 0 0 3px rgba(29,108,224,.25);
6040  background:#f8fbff;
6041}
6042.pa-filters .range { display:flex; gap:10px; }
6043.pa-filters .range input { flex:1; }
6044.pa-filters .hint { font-size:11px; color:#64748b; margin-top:6px; }
6045
6046/* Spec blocks */
6047.f-specs { margin-top:4px; }
6048.spec-attr {
6049  margin:0 0 14px;
6050  padding:12px 12px 10px;
6051  border:1px solid #e2e8f0;
6052  border-radius:14px;
6053  background:linear-gradient(130deg,#ffffff 0%,#f7faff 85%);
6054  position:relative;
6055  transition:border-color .3s, box-shadow .3s, background .4s;
6056}
6057.spec-attr.collapsed { padding-bottom:8px; }
6058.spec-attr.has-selected {
6059  border-color:#1d6ce0;
6060  box-shadow:0 0 0 2px rgba(29,108,224,.15),0 2px 8px -2px rgba(29,108,224,.35);
6061  background:linear-gradient(135deg,#ffffff 0%,#e9f2ff 100%);
6062}
6063.spec-attr-title {
6064  font-weight:600;
6065  margin:0 0 8px;
6066  font-size:13px;
6067  display:flex;
6068  justify-content:space-between;
6069  align-items:center;
6070  cursor:pointer;
6071  color:#0f172a;
6072  user-select:none;
6073}
6074.spec-attr-title .chevron {
6075  font-size:10px;
6076  transition:transform .25s, opacity .25s;
6077  opacity:.65;
6078}
6079.spec-attr.collapsed .spec-attr-title .chevron {
6080  transform:rotate(-90deg);
6081  opacity:.5;
6082}
6083
6084/* Spec options */
6085.spec-attr-options { display:flex; flex-wrap:wrap; gap:6px; }
6086.spec-attr-options label {
6087  display:inline-flex;
6088  align-items:center;
6089  gap:4px;
6090  font-weight:500;
6091  border:1px solid #d4dde7;
6092  padding:6px 9px;
6093  border-radius:30px;
6094  cursor:pointer;
6095  background:#f3f6fa;
6096  font-size:12px;
6097  line-height:1.1;
6098  transition:background .25s, border-color .25s, color .25s, box-shadow .25s;
6099  position:relative;
6100}
6101.spec-attr-options label:hover { background:#e8f1ff; border-color:#b1cff3; }
6102.spec-attr-options input {
6103  margin:0;
6104  position:absolute;
6105  inset:0;
6106  opacity:0;
6107  cursor:pointer;
6108}
6109.spec-attr-options label:has(input:checked) {
6110  background:linear-gradient(135deg,#1d6ce0 0%,#155ab8 100%);
6111  border-color:#1d6ce0;
6112  color:#ffffff;
6113  box-shadow:0 2px 6px -1px rgba(29,108,224,.55);
6114}
6115.spec-attr-options label:has(input:checked)::after {
6116  content:"";
6117  position:absolute;
6118  right:6px;
6119  top:6px;
6120  width:6px;
6121  height:6px;
6122  background:#fff;
6123  border-radius:50%;
6124  box-shadow:0 0 0 4px rgba(255,255,255,.25),0 0 0 7px rgba(29,108,224,.5);
6125  animation:pulseDot 1.9s ease-out infinite;
6126}
6127@keyframes pulseDot {
6128  0%{transform:scale(.8);opacity:.9}
6129  60%{transform:scale(1.4);opacity:.15}
6130  100%{transform:scale(.8);opacity:.9}
6131}
6132
6133/* Badges */
6134.pa-filter-badges { margin:0 0 18px; display:flex; flex-wrap:wrap; gap:6px; }
6135.pa-filter-badge {
6136  display:inline-flex;
6137  gap:6px;
6138  align-items:center;
6139  background:linear-gradient(135deg,#eef4ff 0%,#e0edff 100%);
6140  border:1px solid #b4cff3;
6141  padding:5px 10px 5px 12px;
6142  border-radius:22px;
6143  margin:2px 4px 0 0;
6144  font-size:11px;
6145  font-weight:500;
6146  line-height:1;
6147  color:#0f172a;
6148  position:relative;
6149  overflow:hidden;
6150}
6151.pa-filter-badge::before {
6152  content:"";
6153  position:absolute;
6154  inset:0;
6155  background:linear-gradient(90deg,rgba(255,255,255,0),rgba(255,255,255,.35),rgba(255,255,255,0));
6156  transform:translateX(-100%);
6157  animation:badgeSheen 6s linear infinite;
6158}
6159@keyframes badgeSheen {
6160  0%{transform:translateX(-100%)}
6161  55%{transform:translateX(120%)}
6162  100%{transform:translateX(120%)}
6163}
6164.pa-filter-badge a.clear {
6165  text-decoration:none;
6166  color:#c9832a;
6167  font-weight:700;
6168  padding:0 3px;
6169  display:inline-flex;
6170  align-items:center;
6171  justify-content:center;
6172  border-radius:50%;
6173  line-height:1;
6174  transition:background .25s, color .25s;
6175}
6176.pa-filter-badge a.clear:hover { background:#ffeded; color:#a36422; }
6177.pa-btn-filter { width: 100%;}
6178
6179/* =========================================================
6180   ACTIONS (DESKTOP) - STATIC BOTTOM
6181   ========================================================= */
6182.f-actions {
6183  margin-top:10px;
6184  display:flex;
6185  gap:12px;
6186  flex-wrap:wrap;
6187  padding:16px 0 4px;
6188  border-top:1px solid #e2e8f0;
6189  background:linear-gradient(180deg,rgba(255,255,255,0) 0%,#f1f5f9 70%);
6190  flex-shrink:0;
6191}
6192.f-actions button {
6193  background:linear-gradient(135deg,#1d6ce0 0%,#155ab8 100%);
6194  color:#fff;
6195  border:none;
6196  padding:10px 20px;
6197  border-radius:12px;
6198  cursor:pointer;
6199  font-size:14px;
6200  font-weight:600;
6201  letter-spacing:.3px;
6202  display:inline-flex;
6203  align-items:center;
6204  gap:6px;
6205  box-shadow:0 4px 14px -2px rgba(29,108,224,.45);
6206  transition:transform .25s, box-shadow .3s;
6207}
6208.f-actions button:hover { transform:translateY(-3px); box-shadow:0 8px 26px -4px rgba(29,108,224,.6); }
6209.f-actions button:active { transform:translateY(-1px) scale(.98); box-shadow:0 4px 14px -4px rgba(29,108,224,.55); }
6210.f-actions .reset {
6211  font-size:12px;
6212  text-decoration:none;
6213  color:#e2a642;
6214  align-self:center;
6215  font-weight:600;
6216  padding:4px 6px;
6217  border-radius:8px;
6218  transition:background .25s, color .25s;
6219}
6220.f-actions .reset:hover { background:#ffe6e6; color:#c9832a; text-decoration:underline; }
6221.pa-filters .close-drawer { display: none; }
6222
6223/* Form wrapper: flex container agar inner .filters-scroll bisa scroll di desktop */
6224.filter-form {
6225  flex: 1;
6226  min-height: 0;
6227  display: flex;
6228  flex-direction: column;
6229  overflow: hidden;
6230}
6231
6232/* =========================================================
6233   FILTER TOGGLE (MOBILE TRIGGER)
6234   ========================================================= */
6235.filter-toggle {
6236  display:none;
6237  margin:0 0 14px;
6238  background:linear-gradient(135deg,#1d6ce0 0%,#155ab8 100%);
6239  color:#fff;
6240  border:none;
6241  padding:11px 18px;
6242  font-size:14px;
6243  font-weight:600;
6244  border-radius:14px;
6245  cursor:pointer;
6246  letter-spacing:.4px;
6247  box-shadow:0 4px 14px -2px rgba(29,108,224,.55);
6248  position:relative;
6249  z-index:100;
6250  transition:box-shadow .3s, transform .25s;
6251}
6252.filter-toggle:hover { box-shadow:0 8px 26px -4px rgba(29,108,224,.6); transform:translateY(-2px); }
6253.filter-toggle:active { transform:translateY(0); box-shadow:0 4px 14px -4px rgba(29,108,224,.55); }
6254.filter-toggle .count-badge {
6255  display:inline-block;
6256  background:linear-gradient(135deg,#C10DFF 0%,#C10DFF 55%,#C10DFF 100%);
6257  color:#1d2b44;
6258  font-size:11px;
6259  font-weight:700;
6260  padding:3px 7px 4px;
6261  border-radius:30px;
6262  margin-left:8px;
6263  box-shadow:0 0 0 2px #ffffff,0 0 0 4px #1d6ce0;
6264}
6265
6266/* Overlay (mobile) */
6267.pa-filters-overlay { display:none; }
6268
6269/* =========================================================
6270   MOBILE DRAWER
6271   ========================================================= */
6272@media (max-width:768px){
6273  .product-archive.with-filters .pa-layout { flex-direction:column; gap:12px; }
6274  .filter-toggle { display:inline-flex; align-items:center; gap:6px; }
6275
6276  .pa-filters-overlay {
6277    position:fixed;
6278    inset:0;
6279    background:rgba(0,20,45,.55);
6280    z-index:998;
6281    opacity:0;
6282    transition:opacity .35s;
6283    display:block;
6284    pointer-events:none;
6285    backdrop-filter:blur(2px);
6286  }
6287  .pa-filters-overlay.active { opacity:1; pointer-events:auto; }
6288
6289  .pa-filters {
6290    position:fixed;
6291    top:auto;
6292    left:0; right:0; bottom:0;
6293    max-height:92vh;
6294    height:auto;
6295    width:100%;
6296    border-radius:30px 30px 0 0;
6297    border:1px solid #dbe4ef;
6298    padding:26px 22px 0;
6299    z-index:10000; /* di atas bottom-nav (9998) */
6300    transform:translateY(102%);
6301    transition:transform .55s cubic-bezier(.68,-0.35,.25,1);
6302    box-shadow:0 -4px 40px -6px rgba(15,23,42,.45);
6303    overflow:hidden; /* outer hidden, inner scroll */
6304  }
6305  .pa-filters.active { transform:translateY(0); }
6306
6307  /* Drag handle indicator */
6308  .pa-filters::after {
6309    content:"";
6310    display:block;
6311    position:absolute;
6312    top:10px;
6313    left:50%;
6314    transform:translateX(-50%);
6315    width:36px;
6316    height:4px;
6317    background:#cbd5e1;
6318    border-radius:4px;
6319  }
6320  body.filter-open { overflow:hidden; touch-action:none; }
6321
6322  /* Restore mobile-friendly input sizes (prevent iOS zoom on focus) */
6323  .pa-filters input[type=text],
6324  .pa-filters input[type=number],
6325  .pa-filters select {
6326    font-size:16px;
6327    min-height:44px;
6328    padding:10px 12px;
6329    border-radius:10px;
6330  }
6331  .pa-filters select { padding-right:32px; }
6332
6333  .filters-scroll {
6334    flex:1;
6335    overflow:auto;
6336    padding:0 0 120px; /* ruang di atas bar actions */
6337    margin:0;
6338    -webkit-overflow-scrolling:touch;
6339  }
6340
6341  .pa-filters h2 { font-size:19px; margin-bottom:10px; }
6342
6343  .pa-filters .close-drawer {
6344    display: block;
6345    position:absolute;
6346    right:14px; top:12px;
6347    background:linear-gradient(135deg,#e2a642 0%,#c9832a 100%);
6348    color:#fff;
6349    padding:7px 14px;
6350    border:none;
6351    border-radius:10px;
6352    font-size:12px;
6353    font-weight:600;
6354    cursor:pointer;
6355    box-shadow:0 3px 10px -2px rgba(226,166,66,.55);
6356    transition:transform .25s, box-shadow .3s;
6357    z-index: 99;
6358  }
6359  .pa-filters .close-drawer:hover { transform:translateY(-2px); box-shadow:0 6px 18px -4px rgba(226,166,66,.6); }
6360
6361  .spec-attr { border-radius:18px; }
6362  .spec-attr-options label { font-size:11px; padding:5px 9px; }
6363
6364  /* Actions fixed di viewport bawah */
6365  .f-actions {
6366    position:fixed;
6367    left:0; right:0; bottom:0;
6368    margin:0;
6369    border-top:1px solid #d6dee8;
6370    padding:14px 16px calc(16px + env(safe-area-inset-bottom));
6371    background:#ffffffea;
6372    backdrop-filter:blur(6px);
6373    box-shadow:0 -4px 22px -6px rgba(15,23,42,.25);
6374    justify-content:space-between;
6375    z-index:10001; /* di atas pa-filters (10000) dan bottom-nav (9998) */
6376  }
6377
6378  /* Sembunyikan bottom-nav saat filter drawer terbuka */
6379  body.filter-open .bottom-nav {
6380    display: none !important;
6381  }
6382  .f-actions button {
6383    flex:1 1 auto;
6384    justify-content:center;
6385    padding:14px 14px;
6386    font-size:15px;
6387    border-radius:14px;
6388  }
6389  .f-actions .reset {
6390    order:-1;
6391    font-size:12px;
6392    padding:2px 8px;
6393  }
6394}
6395
6396/* Scrollbar (outer panel desktop only kept for compatibility) */
6397.pa-filters::-webkit-scrollbar { width:0; } /* disembunyikan karena inner yang scroll */
6398
6399/* Utilities */
6400.hidden { display:none !important; }
6401.gradient-text {
6402  background:linear-gradient(90deg,#1d6ce0,#155ab8,#1d6ce0);
6403  -webkit-background-clip:text;
6404  color:transparent;
6405  animation:titleSheen 10s linear infinite;
6406}
6407.glow-focus:focus {
6408  outline:none !important;
6409  box-shadow:0 0 0 3px rgba(29,108,224,.4) !important;
6410  border-color:#1d6ce0 !important;
6411}
6412
6413/* Simple fade (opsional) */
6414@keyframes fadeIn {
6415  from { opacity:0; transform:translateY(8px); }
6416  to { opacity:1; transform:translateY(0); }
6417}
6418</style>
6419  <style data-inlined="mega-menu-tiles.css">
6420/* Tile Mega Menu — Modern E-commerce Enhanced (2025) */
6421
6422/* Design Tokens */
6423:root {
6424  --mm2-bg: #ffffff;
6425  --mm2-surface: #ffffff;
6426  --mm2-surface-elevated: rgba(255,255,255,0.98);
6427  --mm2-muted: #f8fafc;
6428  --mm2-border: #e2e8f0;
6429  --mm2-border-strong: #cbd5e1;
6430  --mm2-accent: #6366f1;
6431  --mm2-accent-hover: #4f46e5;
6432  --mm2-accent-10: rgba(99,102,241,.10);
6433  --mm2-accent-20: rgba(99,102,241,.20);
6434  --mm2-fg: #0f172a;
6435  --mm2-fg-muted: #64748b;
6436  --mm2-fg-light: #94a3b8;
6437  
6438  /* Enhanced Shadows - Multi-layer elevation */
6439  --mm2-shadow-sm: 0 2px 8px rgba(15,23,42,.06), 0 1px 2px rgba(15,23,42,.04);
6440  --mm2-shadow-md: 0 8px 24px rgba(15,23,42,.10), 0 2px 8px rgba(15,23,42,.06);
6441  --mm2-shadow-lg: 0 20px 60px rgba(15,23,42,.14), 0 8px 24px rgba(15,23,42,.08);
6442  --mm2-shadow-xl: 0 32px 80px rgba(15,23,42,.18), 0 12px 32px rgba(15,23,42,.10);
6443  --mm2-shadow-hover: 0 16px 40px rgba(15,23,42,.16), 0 6px 16px rgba(15,23,42,.08);
6444  
6445  /* Border Radius */
6446  --mm2-radius-xl: 20px;
6447  --mm2-radius-lg: 16px;
6448  --mm2-radius-md: 12px;
6449  --mm2-radius-sm: 10px;
6450  
6451  --mm2-z: 60;
6452  
6453  /* Animation Timing */
6454  --mm2-transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
6455  --mm2-transition-base: 250ms cubic-bezier(0.4, 0, 0.2, 1);
6456  --mm2-transition-slow: 350ms cubic-bezier(0.4, 0, 0.2, 1);
6457}
6458
6459.navbar { position: relative; z-index: var(--mm2-z); }
6460
6461/* Trigger Button - Enhanced */
6462.mm2-trigger {
6463  display: inline-flex; align-items: center; gap: 8px;
6464  padding: 10px 14px; border-radius: var(--mm2-radius-md);
6465  border: 1.5px solid var(--mm2-border);
6466  background: var(--mm2-surface); color: var(--mm2-fg);
6467  cursor: pointer; user-select: none;
6468  transition: all var(--mm2-transition-fast);
6469  text-decoration: none;
6470  font-weight: 600;
6471  box-shadow: var(--mm2-shadow-sm);
6472}
6473.mm2-trigger:hover { 
6474  border-color: var(--mm2-accent); 
6475  background: var(--mm2-muted);
6476  box-shadow: var(--mm2-shadow-md);
6477  transform: translateY(-1px);
6478}
6479.mm2-trigger:active { transform: translateY(0); }
6480.mm2-trigger svg {max-width: unset !important; transition: transform var(--mm2-transition-fast); }
6481.mm2-trigger:hover svg { transform: scale(1.05); }
6482
6483/* Backdrop - Enhanced Glassmorphism */
6484.mm2-backdrop {
6485  position: fixed; 
6486  inset: 0;
6487  top: 0;
6488  left: 0;
6489  right: 0;
6490  bottom: 0;
6491  background: rgba(15,23,42,.40);
6492  backdrop-filter: blur(8px) saturate(1.2);
6493  -webkit-backdrop-filter: blur(8px) saturate(1.2);
6494  opacity: 0; 
6495  pointer-events: none;
6496  z-index: 499;
6497  transition: opacity var(--mm2-transition-base);
6498}
6499.mm2-open .mm2-backdrop { opacity: 1; pointer-events: auto; }
6500
6501/* Panel - Glassmorphism Enhanced */
6502.mm2-panel {
6503  position: fixed;
6504  left: 50%;
6505  top: 120px;
6506  transform: translateX(-50%) translateY(-16px);
6507  width: min(1200px, 95vw);
6508  background: var(--mm2-surface-elevated);
6509  backdrop-filter: blur(20px) saturate(1.8);
6510  -webkit-backdrop-filter: blur(20px) saturate(1.8);
6511  border: 1.5px solid var(--mm2-border-strong);
6512  border-radius: var(--mm2-radius-xl);
6513  box-shadow: var(--mm2-shadow-xl);
6514  display: none;
6515  overflow: hidden;
6516  opacity: 0;
6517  z-index: 500;
6518  transition: transform var(--mm2-transition-slow), opacity var(--mm2-transition-base);
6519}
6520.mm2-open .mm2-panel { 
6521  display: block; 
6522  opacity: 1; 
6523  transform: translateX(-50%) translateY(0);
6524}
6525
6526/* View switcher */
6527.mm2-view { display: none; }
6528.mm2-open .mm2-view.is-active { display: block; }
6529
6530/* Top grid (level-1 tiles) - Enhanced */
6531.mm2-top {
6532  padding: 24px;
6533  max-height: 75vh; overflow: auto;
6534}
6535.mm2-grid {
6536  display: grid;
6537  grid-template-columns: repeat(4, minmax(0,1fr));
6538  gap: 14px;
6539}
6540@media (max-width: 1200px) { .mm2-grid { grid-template-columns: repeat(3, minmax(0,1fr)); } }
6541@media (max-width: 960px)  { .mm2-grid { grid-template-columns: repeat(2, minmax(0,1fr)); gap: 12px; } }
6542
6543/* Stagger animation for cards */
6544.mm2-open .mm2-card {
6545  animation: slideInUp 0.3s cubic-bezier(0.4, 0, 0.2, 1) backwards;
6546}
6547.mm2-open .mm2-card:nth-child(1)  { animation-delay: 0.02s; }
6548.mm2-open .mm2-card:nth-child(2)  { animation-delay: 0.04s; }
6549.mm2-open .mm2-card:nth-child(3)  { animation-delay: 0.06s; }
6550.mm2-open .mm2-card:nth-child(4)  { animation-delay: 0.08s; }
6551.mm2-open .mm2-card:nth-child(5)  { animation-delay: 0.10s; }
6552.mm2-open .mm2-card:nth-child(6)  { animation-delay: 0.12s; }
6553.mm2-open .mm2-card:nth-child(7)  { animation-delay: 0.14s; }
6554.mm2-open .mm2-card:nth-child(8)  { animation-delay: 0.16s; }
6555.mm2-open .mm2-card:nth-child(9)  { animation-delay: 0.18s; }
6556.mm2-open .mm2-card:nth-child(10) { animation-delay: 0.20s; }
6557.mm2-open .mm2-card:nth-child(11) { animation-delay: 0.22s; }
6558.mm2-open .mm2-card:nth-child(12) { animation-delay: 0.24s; }
6559.mm2-open .mm2-card:nth-child(n+13) { animation-delay: 0.26s; }
6560
6561@keyframes slideInUp {
6562  from {
6563    opacity: 0;
6564    transform: translateY(12px);
6565  }
6566  to {
6567    opacity: 1;
6568    transform: translateY(0);
6569  }
6570}
6571
6572.mm2-card {
6573  position: relative;
6574  display: flex; gap: 14px; align-items: center;
6575  padding: 16px; border-radius: var(--mm2-radius-md);
6576  background: var(--mm2-surface);
6577  border: 1.5px solid var(--mm2-border);
6578  text-decoration: none; color: var(--mm2-fg);
6579  transition: all var(--mm2-transition-fast);
6580  min-height: 72px;
6581  box-shadow: var(--mm2-shadow-sm);
6582}
6583.mm2-card:hover { 
6584  border-color: var(--mm2-accent);
6585  background: linear-gradient(135deg, rgba(255,255,255,1), rgba(248,250,252,1));
6586  box-shadow: var(--mm2-shadow-hover);
6587  transform: translateY(-3px);
6588}
6589.mm2-card:active { transform: translateY(-1px); }
6590
6591/* Icon with category-specific gradients */
6592.mm2-card .mm2-ico {
6593  width: 48px; height: 48px; border-radius: 12px;
6594  display: inline-flex; align-items: center; justify-content: center;
6595  background: linear-gradient(135deg, #e0e7ff, #fff);
6596  border: 1.5px solid #c7d2fe;
6597  color: #4f46e5;
6598  flex-shrink: 0;
6599  transition: all var(--mm2-transition-fast);
6600  box-shadow: 0 2px 8px rgba(99,102,241,0.1);
6601}
6602.mm2-card:hover .mm2-ico {
6603  transform: scale(1.08) rotate(2deg);
6604  box-shadow: 0 4px 16px rgba(99,102,241,0.2);
6605}
6606.mm2-card .mm2-ico svg { transition: transform var(--mm2-transition-fast); }
6607.mm2-card:hover .mm2-ico svg { transform: scale(1.1); }
6608
6609.mm2-card .mm2-title { 
6610  font-weight: 700; 
6611  font-size: 15px;
6612  letter-spacing: -0.01em;
6613  line-height: 1.3;
6614}
6615.mm2-card .mm2-sub { 
6616  color: var(--mm2-fg-muted); 
6617  font-size: 12.5px; 
6618  margin-top: 3px;
6619  display: flex;
6620  align-items: center;
6621  gap: 4px;
6622  white-space: nowrap;
6623  overflow: hidden;
6624  text-overflow: ellipsis;
6625}
6626.mm2-card .mm2-count {
6627  display: inline-flex;
6628  padding: 2px 8px;
6629  border-radius: 6px;
6630  background: var(--mm2-accent-10);
6631  color: var(--mm2-accent);
6632  font-weight: 600;
6633  font-size: 11px;
6634  margin-left: 4px;
6635}
6636
6637/* Detail view (level-2 + level-3) - Enhanced */
6638.mm2-detail {
6639  position: relative;
6640  padding: 16px 24px 24px;
6641  max-height: 70vh; overflow: auto;
6642}
6643.mm2-detail-header {
6644  display: flex; align-items: center; gap: 14px;
6645  padding: 10px 0 16px; border-bottom: 2px solid var(--mm2-muted); margin-bottom: 16px;
6646}
6647.mm2-back {
6648  display: inline-flex; align-items: center; justify-content: center;
6649  width: 40px; height: 40px; border-radius: 12px;
6650  border: 1.5px solid var(--mm2-border); background: #fff; color: var(--mm2-fg);
6651  cursor: pointer;
6652  transition: all var(--mm2-transition-fast);
6653  box-shadow: var(--mm2-shadow-sm);
6654}
6655.mm2-back:hover {
6656  border-color: var(--mm2-accent);
6657  background: var(--mm2-muted);
6658  transform: translateX(-3px);
6659  box-shadow: var(--mm2-shadow-md);
6660}
6661.mm2-detail-title { 
6662  font-weight: 800; 
6663  font-size: 18px;
6664  letter-spacing: -0.02em;
6665}
6666
6667/* Two-pane layout (desktop) - Enhanced */
6668.mm2-detail-layout {
6669  display: grid;
6670  grid-template-columns: 280px 1fr;
6671  gap: 20px;
6672}
6673
6674/* Left: subnav (level-2 list) - Enhanced */
6675.mm2-subnav {
6676  position: sticky; top: 8px;
6677  align-self: start;
6678  max-height: calc(70vh - 56px);
6679  overflow: auto;
6680  padding-right: 6px;
6681}
6682.mm2-subnav::-webkit-scrollbar { width: 8px; height: 8px; }
6683.mm2-subnav::-webkit-scrollbar-track { background: transparent; }
6684.mm2-subnav::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 8px; }
6685.mm2-subnav::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
6686
6687.mm2-subitem {
6688  display: flex; align-items: center; justify-content: flex-start; gap: 10px;
6689  width: 100%;
6690  padding: 12px 14px;
6691  border-radius: 12px;
6692  border: 1.5px solid transparent;
6693  background: transparent;
6694  color: var(--mm2-fg);
6695  cursor: pointer;
6696  text-align: left;
6697  transition: all var(--mm2-transition-fast);
6698  margin-bottom: 6px;
6699  font-weight: 600;
6700  font-size: 14px;
6701  position: relative;
6702}
6703.mm2-subitem::before {
6704  content: '';
6705  position: absolute;
6706  left: 0;
6707  top: 50%;
6708  transform: translateY(-50%);
6709  width: 3px;
6710  height: 0;
6711  background: var(--mm2-accent);
6712  border-radius: 0 2px 2px 0;
6713  transition: height var(--mm2-transition-fast);
6714}
6715.mm2-subitem:hover { 
6716  background: var(--mm2-muted);
6717  color: var(--mm2-accent-hover);
6718}
6719.mm2-subitem.active {
6720  background: linear-gradient(135deg, rgba(99,102,241,0.08), rgba(99,102,241,0.04));
6721  border-color: var(--mm2-accent-20);
6722  color: var(--mm2-accent-hover);
6723  box-shadow: inset 0 0 0 1px rgba(99,102,241,0.1);
6724}
6725.mm2-subitem.active::before {
6726  height: 24px;
6727}
6728
6729/* Right: subcontent (level-3 grid) - Enhanced */
6730.mm2-subcontent { min-height: 240px; }
6731.mm2-links {
6732  display: grid; gap: 12px 14px;
6733  grid-template-columns: repeat(3, minmax(0,1fr));
6734}
6735@media (max-width: 1200px) { .mm2-links { grid-template-columns: repeat(2, minmax(0,1fr)); } }
6736
6737.mm2-link {
6738  display: flex; align-items: center; gap: 12px;
6739  padding: 14px; border: 1.5px solid var(--mm2-border); border-radius: var(--mm2-radius-md);
6740  background: var(--mm2-surface); color: var(--mm2-fg); text-decoration: none;
6741  min-height: 52px;
6742  font-weight: 500;
6743  font-size: 14px;
6744  transition: all var(--mm2-transition-fast);
6745  box-shadow: var(--mm2-shadow-sm);
6746  position: relative;
6747  justify-content: space-between;
6748}
6749.mm2-link:hover { 
6750  background: var(--mm2-muted); 
6751  border-color: var(--mm2-accent);
6752  transform: translateX(4px);
6753  box-shadow: var(--mm2-shadow-md);
6754}
6755.mm2-link-text {
6756  flex: 1;
6757}
6758
6759/* Product Thumbnails */
6760.mm2-thumbs {
6761  display: flex;
6762  gap: 6px;
6763  align-items: center;
6764  margin-left: auto;
6765  flex-shrink: 0;
6766}
6767.mm2-thumb {
6768  width: 36px;
6769  height: 36px;
6770  border-radius: 8px;
6771  object-fit: cover;
6772  border: 1.5px solid var(--mm2-border);
6773  box-shadow: 0 2px 6px rgba(0,0,0,0.08);
6774  transition: all var(--mm2-transition-fast);
6775  background: var(--mm2-muted);
6776}
6777.mm2-link:hover .mm2-thumb {
6778  border-color: var(--mm2-accent);
6779  box-shadow: 0 4px 10px rgba(99,102,241,0.15);
6780  transform: scale(1.08);
6781}
6782
6783/* Thumbnail Skeleton Loader */
6784.mm2-thumb-skeleton {
6785  width: 36px;
6786  height: 36px;
6787  border-radius: 8px;
6788  background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 50%, #f1f5f9 75%);
6789  background-size: 200% 100%;
6790  animation: shimmer 1.5s infinite;
6791  border: 1.5px solid var(--mm2-border);
6792}
6793
6794.mm2-badge {
6795  font-size: 11px; padding: 3px 10px; margin-left: auto;
6796  border-radius: 12px; background: var(--mm2-accent-10); color: var(--mm2-accent);
6797  font-weight: 700;
6798  border: 1px solid var(--mm2-accent-20);
6799}
6800
6801/* "See all" highlight card - Enhanced */
6802.mm2-seeall {
6803  border: 2px dashed var(--mm2-accent);
6804  background: linear-gradient(135deg, rgba(99,102,241,.08), rgba(99,102,241,.02));
6805  font-weight: 700;
6806}
6807.mm2-seeall:hover {
6808  background: linear-gradient(135deg, rgba(99,102,241,.14), rgba(99,102,241,.06));
6809  border-style: solid;
6810  transform: translateX(6px) scale(1.02);
6811}
6812
6813/* Mobile panel styles - Enhanced with drag handle & better touch targets */
6814@media (max-width: 960px) {
6815  .mm2-panel {
6816    position: fixed;
6817    left: 0;
6818    right: 0;
6819    bottom: 0;
6820    top: auto;
6821    width: 100%;
6822    max-height: 88vh;
6823    transform: translateX(0) translateY(100%);
6824    border-radius: var(--mm2-radius-xl) var(--mm2-radius-xl) 0 0;
6825    border-bottom: none;
6826  }
6827  .mm2-open .mm2-panel {
6828    transform: translateX(0) translateY(0); z-index: 9999;
6829  }
6830  
6831  /* Enhanced backdrop for mobile */
6832  .mm2-backdrop {
6833    backdrop-filter: blur(6px) saturate(1.2);
6834    -webkit-backdrop-filter: blur(6px) saturate(1.2);
6835  }
6836  
6837  /* Mobile close button - Soft glassmorphism */
6838  .mm2-mobile-close {
6839    position: absolute;
6840    top: 4px;
6841    right: 8px;
6842    width: 28px;
6843    height: 28px;
6844    border-radius: 50%;
6845    border: 1px solid rgba(226, 232, 240, 0.6);
6846    background: rgba(255, 255, 255, 0.85);
6847    backdrop-filter: blur(12px) saturate(1.8);
6848    -webkit-backdrop-filter: blur(12px) saturate(1.8);
6849    color: var(--mm2-fg-muted);
6850    display: inline-flex;
6851    align-items: center;
6852    justify-content: center;
6853    cursor: pointer;
6854    transition: all var(--mm2-transition-fast);
6855    box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
6856    z-index: 20;
6857  }
6858  .mm2-mobile-close:hover {
6859    background: rgba(254, 242, 242, 0.95);
6860    border-color: rgba(226,166,66,0.8);
6861    color: #e2a642;
6862    box-shadow: 0 4px 12px rgba(226,166,66,0.15);
6863  }
6864  .mm2-mobile-close:active {
6865    transform: scale(0.92);
6866  }
6867  
6868  /* Drag handle indicator */
6869  .mm2-top::before,
6870  .mm2-detail::before {
6871    content: '';
6872    position: sticky;
6873    top: 0;
6874    left: 50%;
6875    transform: translateX(-50%);
6876    width: 40px;
6877    height: 4px;
6878    background: var(--mm2-border-strong);
6879    border-radius: 999px;
6880    margin: 12px auto 20px;
6881    display: block;
6882    z-index: 10;
6883    transition: background var(--mm2-transition-fast);
6884  }
6885  .mm2-panel:active .mm2-top::before,
6886  .mm2-panel:active .mm2-detail::before {
6887    background: #94a3b8;
6888  }
6889  
6890  .mm2-top {
6891    padding: 10px;  
6892  }
6893  
6894  .mm2-top, .mm2-detail { 
6895    max-height: min(82vh, 720px);
6896    padding-top: 8px;
6897  }
6898
6899  /* Two-pane collapses: subnav becomes GRID (no horizontal scroll) */
6900  .mm2-detail-layout { display: block; }
6901  .mm2-subnav {
6902    position: static; max-height: none; overflow: visible;
6903    display: grid; gap: 10px; padding: 10px 4px 16px; margin-bottom: 16px;
6904    grid-template-columns: repeat(2, minmax(0,1fr));
6905    border-bottom: 2px solid var(--mm2-muted);
6906  }
6907  .mm2-subitem {
6908    margin: 0; width: 100%; text-align: center;
6909    border-radius: 14px;
6910    background: #fff; border: 1.5px solid var(--mm2-border);
6911    white-space: normal; line-height: 1.3; min-height: 48px;
6912    justify-content: center;
6913    padding: 12px 10px;
6914    font-size: 13.5px;
6915    box-shadow: var(--mm2-shadow-sm);
6916  }
6917  .mm2-subitem::before { display: none; }
6918  .mm2-subitem:hover {
6919    transform: scale(1.02);
6920  }
6921  .mm2-subitem.active {
6922    background: linear-gradient(135deg, var(--mm2-accent), var(--mm2-accent-hover));
6923    border-color: var(--mm2-accent-hover);
6924    color: white;
6925    box-shadow: 0 4px 12px rgba(99,102,241,0.3);
6926    font-weight: 700;
6927  }
6928
6929  /* Level-3 grid tetap 2 kolom dengan spacing lebih baik */
6930  .mm2-links { 
6931    grid-template-columns: repeat(2, minmax(0,1fr));
6932    gap: 10px;
6933  }
6934  
6935  /* Mobile: hide thumbnails untuk save space */
6936  .mm2-thumbs {
6937    display: none;
6938  }
6939  
6940  /* Larger touch targets for mobile */
6941  .mm2-card {
6942    min-height: 80px;
6943    padding: 18px 14px;
6944  }
6945  .mm2-card .mm2-ico {
6946    width: 50px;
6947    height: 50px;
6948  }
6949  .mm2-card .mm2-title {
6950    font-size: 14px;
6951  }
6952  .mm2-card .mm2-sub {
6953    font-size: 11.5px;
6954  }
6955  .mm2-link {
6956    min-height: 56px;
6957    padding: 16px;
6958  }
6959  .mm2-back {
6960    width: 44px;
6961    height: 44px;
6962  }
6963  
6964  /* Grid spacing */
6965  .mm2-grid {
6966    gap: 10px;
6967  }
6968}
6969</style>
6970  <style data-inlined="product-compare.css">
6971/* Product Compare Feature Styles */
6972
6973/* Compare Button in Product Detail - Modern Chip Style */
6974.pd-compare-section {
6975  margin: 0.5rem 0;
6976  display: flex;
6977  align-items: center;
6978  gap: 0.5rem;
6979}
6980
6981.pd-compare-btn {
6982  display: inline-flex;
6983  align-items: center;
6984  gap: 0.4rem;
6985  padding: 0.5rem 0.85rem;
6986  border: 1px solid #cbd5e1;
6987  border-radius: 20px;
6988  background: #fff;
6989  color: #64748b;
6990  font-weight: 600;
6991  font-size: 0.8rem;
6992  cursor: pointer;
6993  transition: all 0.2s ease;
6994  position: relative;
6995  white-space: nowrap;
6996}
6997
6998.pd-compare-btn:hover {
6999  border-color: #3b82f6;
7000  color: #3b82f6;
7001  background: #f0f9ff;
7002  transform: translateY(-1px);
7003  box-shadow: 0 2px 6px rgba(59, 130, 246, 0.15);
7004}
7005
7006.pd-compare-btn.added {
7007  border-color: #16a34a;
7008  color: #16a34a;
7009  background: #f0fdf4;
7010}
7011
7012.pd-compare-btn .compare-remove {
7013  display: none;
7014}
7015
7016.pd-compare-btn.added .compare-text {
7017  display: none;
7018}
7019
7020.pd-compare-btn.added .compare-remove {
7021  display: flex !important;
7022  align-items: center;
7023  gap: 0.25rem;
7024  font-weight: 600;
7025  font-size: 0.9rem;
7026  color: #e2a642 !important;
7027  opacity: 1 !important;
7028  visibility: visible !important;
7029  z-index: 1;
7030  position: relative;
7031}
7032
7033.pd-compare-btn.added .compare-remove[hidden] {
7034  display: flex !important;
7035}
7036
7037/* Force visibility for added state */
7038.pd-compare-btn.added .compare-remove {
7039  display: flex !important;
7040}
7041
7042[hidden].compare-remove {
7043  display: none !important;
7044}
7045
7046.pd-compare-btn.added [hidden].compare-remove {
7047  display: flex !important;
7048}
7049
7050/* Ensure the added state text is always visible */
7051.pd-compare-btn.added {
7052  border-color: #e2a642 !important;
7053  color: #e2a642 !important;
7054  background: #fef2f2 !important;
7055}
7056
7057.pd-compare-btn.added .compare-remove svg {
7058  color: #e2a642 !important;
7059}
7060
7061.pd-compare-btn.added:hover {
7062  border-color: #15803d;
7063  color: #15803d;
7064  background: #dcfce7;
7065  transform: translateY(-1px);
7066  box-shadow: 0 2px 6px rgba(22, 163, 74, 0.2);
7067}
7068
7069.pd-compare-btn .compare-icon {
7070  width: 16px;
7071  height: 16px;
7072  flex-shrink: 0;
7073}
7074
7075.pd-compare-info {
7076  display: inline-flex;
7077  align-items: center;
7078}
7079
7080.compare-note {
7081  color: #94a3b8;
7082  font-size: 0.7rem;
7083  font-weight: 500;
7084  transition: color 0.2s ease;
7085}
7086
7087.pd-compare-btn.added + .pd-compare-info .compare-note {
7088  color: #16a34a;
7089  font-weight: 600;
7090}
7091
7092/* Compare Badge in Navigation */
7093.compare-indicator {
7094  position: relative;
7095}
7096
7097.compare-indicator .badge {
7098  position: absolute;
7099  top: -8px;
7100  right: -8px;
7101  min-width: 18px;
7102  height: 18px;
7103  padding: 0 5px;
7104  background: #e2a642;
7105  color: white;
7106  border-radius: 9px;
7107  font-size: 11px;
7108 
7109
7110/* Floating Compare Badge (Sticky) */
7111.compare-floating-badge {
7112  position: fixed;
7113  right: 16px;
7114  bottom: calc(20px + 60px); /* Above scroll-to-top button */
7115  width: auto;
7116  min-width: 56px;
7117  height: 56px;
7118  padding: 0 16px;
7119  border-radius: 28px;
7120  background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
7121  color: #fff;
7122  box-shadow: 0 8px 24px -8px rgba(59, 130, 246, 0.5);
7123  cursor: pointer;
7124  z-index: 1104;
7125  opacity: 0;
7126  transform: translateY(8px) scale(0.9);
7127  transition: opacity 0.3s ease, transform 0.3s ease, box-shadow 0.2s ease;
7128  display: flex;
7129  align-items: center;
7130  justify-content: center;
7131  gap: 8px;
7132  font-weight: 600;
7133  font-size: 0.85rem;
7134  pointer-events: none;
7135}
7136
7137.compare-floating-badge.visible {
7138  opacity: 1;
7139  transform: translateY(0) scale(1);
7140  pointer-events: auto;
7141}
7142
7143.compare-floating-badge:hover {
7144  box-shadow: 0 12px 32px -10px rgba(59, 130, 246, 0.6);
7145  transform: translateY(-2px) scale(1.02);
7146}
7147
7148.compare-floating-badge:active {
7149  transform: translateY(0) scale(0.98);
7150}
7151
7152.compare-floating-badge .cfb-icon {
7153  width: 20px;
7154  height: 20px;
7155  flex-shrink: 0;
7156}
7157
7158.compare-floating-badge .cfb-count {
7159  font-size: 0.9rem;
7160  font-weight: 700;
7161  min-width: 20px;
7162  text-align: center;
7163  background: rgba(255, 255, 255, 0.25);
7164  padding: 2px 8px;
7165  border-radius: 12px;
7166  line-height: 1;
7167  display: flex;
7168  align-items: center;
7169  justify-content: center;
7170  height: 24px;
7171}
7172
7173.compare-floating-badge .cfb-text {
7174  display: none;
7175}
7176
7177@media (min-width: 641px) {
7178  .compare-floating-badge .cfb-text {
7179    display: block;
7180    white-space: nowrap;
7181  }
7182}
7183
7184@media (max-width: 640px) {
7185  .compare-floating-badge {
7186    right: 14px;
7187    bottom: calc(var(--scroll-top-bottom, calc(68px + 18px + env(safe-area-inset-bottom))) + 60px);
7188    min-width: 48px;
7189    height: 48px;
7190    padding: 0 12px;
7191    border-radius: 24px;
7192  }
7193  
7194  .compare-floating-badge .cfb-icon {
7195    width: 18px;
7196    height: 18px;
7197  }
7198  
7199  .compare-floating-badge .cfb-count {
7200    font-size: 0.8rem;
7201    padding: 2px 6px;
7202    height: 20px;
7203  }
7204} font-weight: 600;
7205  display: flex;
7206  align-items: center;
7207  justify-content: center;
7208  transform: scale(0);
7209  transition: transform 0.2s ease;
7210}
7211
7212.compare-indicator .badge.active {
7213  transform: scale(1);
7214}
7215
7216/* Compare Sidebar */
7217.compare-sidebar {
7218  position: fixed;
7219  top: 0;
7220  right: -400px;
7221  width: 400px;
7222  height: 100vh;
7223  background: #ffffff;
7224  box-shadow: -4px 0 20px rgba(0, 0, 0, 0.1);
7225  z-index: 9999;
7226  display: flex;
7227  flex-direction: column;
7228  transition: right 0.3s ease;
7229}
7230
7231.compare-sidebar.open {
7232  right: 0;
7233}
7234
7235.compare-overlay {
7236  position: fixed;
7237  top: 0;
7238  left: 0;
7239  width: 100%;
7240  height: 100%;
7241  background: rgba(0, 0, 0, 0.5);
7242  z-index: 9998;
7243  opacity: 0;
7244  visibility: hidden;
7245  transition: all 0.3s ease;
7246}
7247
7248.compare-overlay.active {
7249  opacity: 1;
7250  visibility: visible;
7251}
7252
7253.compare-header {
7254  display: flex;
7255  align-items: center;
7256  justify-content: space-between;
7257  padding: 1.25rem;
7258  border-bottom: 1px solid #e2e8f0;
7259  background: #f8fafc;
7260}
7261
7262.compare-title {
7263  display: flex;
7264  align-items: center;
7265  gap: 0.5rem;
7266  font-size: 1rem;
7267  font-weight: 600;
7268  color: #1e293b;
7269  margin: 0;
7270}
7271
7272.compare-count {
7273  color: #64748b;
7274  font-weight: 400;
7275}
7276
7277.compare-close {
7278  display: flex;
7279  align-items: center;
7280  justify-content: center;
7281  width: 32px;
7282  height: 32px;
7283  border: none;
7284  border-radius: 6px;
7285  background: transparent;
7286  color: #64748b;
7287  cursor: pointer;
7288  transition: all 0.2s ease;
7289}
7290
7291.compare-close:hover {
7292  background: #e2e8f0;
7293  color: #374151;
7294}
7295
7296.compare-content {
7297  flex: 1;
7298  overflow-y: auto;
7299  padding: 1rem;
7300}
7301
7302.compare-empty {
7303  display: flex;
7304  flex-direction: column;
7305  align-items: center;
7306  justify-content: center;
7307  height: 100%;
7308  text-align: center;
7309  padding: 2rem 1rem;
7310}
7311
7312.compare-empty h5 {
7313  margin: 1rem 0 0.5rem;
7314  color: #374151;
7315}
7316
7317.compare-empty p {
7318  color: #64748b;
7319  font-size: 0.875rem;
7320  line-height: 1.5;
7321}
7322
7323.compare-list {
7324  display: flex;
7325  flex-direction: column;
7326  gap: 0.75rem;
7327}
7328
7329.compare-item {
7330  display: flex;
7331  align-items: center;
7332  gap: 0.75rem;
7333  padding: 0.75rem;
7334  border: 1px solid #e2e8f0;
7335  border-radius: 8px;
7336  background: #ffffff;
7337  transition: all 0.2s ease;
7338}
7339
7340.compare-item:hover {
7341  border-color: #cbd5e1;
7342  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
7343}
7344
7345.compare-item-image {
7346  flex-shrink: 0;
7347  width: 48px;
7348  height: 48px;
7349  border-radius: 6px;
7350  overflow: hidden;
7351  border: 1px solid #e2e8f0;
7352}
7353
7354.compare-item-image img {
7355  width: 100%;
7356  height: 100%;
7357  object-fit: cover;
7358}
7359
7360.compare-item-info {
7361  flex: 1;
7362  min-width: 0;
7363}
7364
7365.compare-item-name {
7366  font-weight: 500;
7367  font-size: 0.875rem;
7368  color: #1e293b;
7369  margin-bottom: 0.25rem;
7370  line-height: 1.3;
7371  display: -webkit-box;
7372  -webkit-line-clamp: 2;
7373  -webkit-box-orient: vertical;
7374  overflow: hidden;
7375}
7376
7377.compare-item-price {
7378  font-weight: 600;
7379  font-size: 0.8rem;
7380  color: #059669;
7381}
7382
7383.compare-item-remove {
7384  flex-shrink: 0;
7385  display: flex;
7386  align-items: center;
7387  justify-content: center;
7388  width: 28px;
7389  height: 28px;
7390  border: none;
7391  border-radius: 4px;
7392  background: transparent;
7393  color: #e2a642;
7394  cursor: pointer;
7395  transition: all 0.2s ease;
7396}
7397
7398.compare-item-remove:hover {
7399  background: #fef2f2;
7400  color: #a36422;
7401}
7402
7403.compare-actions {
7404  padding: 1rem;
7405  border-top: 1px solid #e2e8f0;
7406  display: flex;
7407  gap: 0.75rem;
7408}
7409
7410.compare-actions .btn {
7411  flex: 1;
7412  padding: 0.75rem 1rem;
7413  border-radius: 6px;
7414  font-weight: 600;
7415  font-size: 0.875rem;
7416  display: flex;
7417  align-items: center;
7418  justify-content: center;
7419  gap: 0.5rem;
7420  cursor: pointer;
7421  transition: all 0.2s ease;
7422}
7423
7424.btn-outline-danger {
7425  border: 1px solid #e2a642;
7426  background: transparent;
7427  color: #e2a642;
7428}
7429
7430.btn-outline-danger:hover {
7431  background: #e2a642;
7432  color: white;
7433}
7434
7435.btn-primary {
7436  border: 1px solid #3b82f6;
7437  background: #3b82f6;
7438  color: white;
7439}
7440
7441.btn-primary:hover {
7442  background: #2563eb;
7443  border-color: #2563eb;
7444}
7445
7446.btn-primary:disabled {
7447  opacity: 0.5;
7448  cursor: not-allowed;
7449}
7450
7451.btn-primary:disabled:hover {
7452  background: #3b82f6;
7453  border-color: #3b82f6;
7454}
7455
7456/* AI Comparison Modal */
7457.ai-compare-modal {
7458  position: fixed;
7459  top: 0;
7460  left: 0;
7461  width: 100%;
7462  height: 100%;
7463  z-index: 10000;
7464  display: none;
7465  align-items: center;
7466  justify-content: center;
7467}
7468
7469.ai-compare-modal.open {
7470  display: flex;
7471}
7472
7473.ai-compare-modal .modal-backdrop {
7474  position: absolute;
7475  top: 0;
7476  left: 0;
7477  width: 100%;
7478  height: 100%;
7479  background: rgba(0, 0, 0, 0.6);
7480  backdrop-filter: blur(4px);
7481}
7482
7483.modal-container {
7484  position: relative;
7485  width: 90%;
7486  max-width: 1000px;
7487  max-height: 90vh;
7488  background: #ffffff;
7489  border-radius: 12px;
7490  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
7491  display: flex;
7492  flex-direction: column;
7493  overflow: hidden;
7494}
7495
7496.modal-header {
7497  display: flex;
7498  align-items: center;
7499  justify-content: space-between;
7500  padding: 1.5rem 2rem;
7501  border-bottom: 1px solid #e2e8f0;
7502  background: #f8fafc;
7503}
7504
7505.modal-title {
7506  display: flex;
7507  align-items: center;
7508  gap: 0.75rem;
7509  font-size: 1.25rem;
7510  font-weight: 600;
7511  color: #1e293b;
7512  margin: 0;
7513}
7514
7515.modal-close {
7516  display: flex;
7517  align-items: center;
7518  justify-content: center;
7519  width: 36px;
7520  height: 36px;
7521  border: none;
7522  border-radius: 8px;
7523  background: transparent;
7524  color: #64748b;
7525  cursor: pointer;
7526  transition: all 0.2s ease;
7527}
7528
7529.modal-close:hover {
7530  background: #e2e8f0;
7531  color: #374151;
7532}
7533
7534.modal-content {
7535  flex: 1;
7536  overflow-y: auto;
7537  padding: 2rem;
7538}
7539
7540/* AI Loading State */
7541.ai-loading {
7542  display: flex;
7543  flex-direction: column;
7544  align-items: center;
7545  justify-content: center;
7546  padding: 3rem 2rem;
7547  text-align: center;
7548}
7549
7550.loading-animation {
7551  margin-bottom: 1rem;
7552}
7553
7554.typing-indicator {
7555  display: flex;
7556  align-items: center;
7557  justify-content: center;
7558  gap: 4px;
7559  margin-bottom: 1rem;
7560}
7561
7562.typing-indicator span {
7563  width: 8px;
7564  height: 8px;
7565  background: #3b82f6;
7566  border-radius: 50%;
7567  animation: typing 1.4s infinite ease-in-out;
7568}
7569
7570.typing-indicator span:nth-child(2) {
7571  animation-delay: 0.2s;
7572}
7573
7574.typing-indicator span:nth-child(3) {
7575  animation-delay: 0.4s;
7576}
7577
7578@keyframes typing {
7579  0%, 80%, 100% {
7580    opacity: 0.3;
7581    transform: scale(0.8);
7582  }
7583  40% {
7584    opacity: 1;
7585    transform: scale(1);
7586  }
7587}
7588
7589.ai-loading p {
7590  font-weight: 500;
7591  color: #1e293b;
7592  margin-bottom: 0.5rem;
7593}
7594
7595.ai-loading small {
7596  color: #64748b;
7597}
7598
7599/* AI Error State */
7600.ai-error {
7601  display: flex;
7602  flex-direction: column;
7603  align-items: center;
7604  justify-content: center;
7605  padding: 3rem 2rem;
7606  text-align: center;
7607}
7608
7609.ai-error h4 {
7610  margin: 1rem 0 0.5rem;
7611  color: #e2a642;
7612}
7613
7614.ai-error p {
7615  color: #64748b;
7616  margin-bottom: 1.5rem;
7617}
7618
7619/* AI Results */
7620.ai-results > div {
7621  margin-bottom: 2rem;
7622}
7623
7624.ai-summary-card,
7625.ai-comparison-table,
7626.ai-detailed-analysis,
7627.ai-recommendations,
7628.ai-tips {
7629  background: #f8fafc;
7630  border: 1px solid #e2e8f0;
7631  border-radius: 12px;
7632  padding: 1.5rem;
7633}
7634
7635.ai-results h4 {
7636  display: flex;
7637  align-items: center;
7638  gap: 0.5rem;
7639  font-size: 1.1rem;
7640  font-weight: 600;
7641  color: #1e293b;
7642  margin-bottom: 1rem;
7643}
7644
7645.summary-text {
7646  font-size: 1rem;
7647  line-height: 1.6;
7648  color: #374151;
7649  margin-bottom: 1rem;
7650}
7651
7652.winner-badge {
7653  padding: 1rem;
7654  background: linear-gradient(135deg, #10b981 0%, #059669 100%);
7655  color: white;
7656  border-radius: 8px;
7657}
7658
7659.winner-badge strong {
7660  display: block;
7661  margin-bottom: 0.25rem;
7662}
7663
7664.winner-badge small {
7665  opacity: 0.9;
7666  font-size: 0.875rem;
7667}
7668
7669/* Comparison Table */
7670.comparison-grid {
7671  display: grid;
7672  gap: 1rem;
7673  margin-top: 1rem;
7674}
7675
7676.comparison-category {
7677  background: white;
7678  border: 1px solid #e2e8f0;
7679  border-radius: 8px;
7680  overflow: hidden;
7681}
7682
7683.category-header {
7684  background: #f1f5f9;
7685  padding: 0.75rem 1rem;
7686  font-weight: 600;
7687  color: #1e293b;
7688  border-bottom: 1px solid #e2e8f0;
7689}
7690
7691.category-products {
7692  display: grid;
7693  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
7694  gap: 1px;
7695  background: #e2e8f0;
7696}
7697
7698.product-score {
7699  background: white;
7700  padding: 1rem;
7701  text-align: center;
7702}
7703
7704.score-value {
7705  display: inline-flex;
7706  align-items: center;
7707  gap: 0.25rem;
7708  margin-bottom: 0.5rem;
7709}
7710
7711.score-stars {
7712  color: #C10DFF;
7713}
7714
7715.score-note {
7716  font-size: 0.8rem;
7717  color: #64748b;
7718  line-height: 1.3;
7719}
7720
7721/* Product name header in comparison table */
7722.product-name-header {
7723  margin-bottom: 0.5rem;
7724}
7725
7726.product-name-header strong {
7727  font-size: .8rem; /* h5 size (18px) */
7728  font-weight: 600;
7729  color: #1e293b;
7730  line-height: 1.3;
7731  display: block;
7732  word-break: break-word;
7733}
7734
7735.product-price {
7736  font-size: 0.8rem;
7737  font-weight: 600;
7738  color: #059669;
7739}
7740
7741/* Detailed Analysis */
7742.products-analysis {
7743  display: grid;
7744  gap: 1rem;
7745  margin-top: 1rem;
7746}
7747
7748.product-analysis {
7749  background: white;
7750  border: 1px solid #e2e8f0;
7751  border-radius: 8px;
7752  padding: 1rem;
7753}
7754
7755.product-analysis h5 {
7756  color: #1e293b;
7757  margin-bottom: 0.75rem;
7758  font-weight: 600;
7759}
7760
7761.analysis-section {
7762  margin-bottom: 0.75rem;
7763}
7764
7765.analysis-section:last-child {
7766  margin-bottom: 0;
7767}
7768
7769.analysis-section h6 {
7770  font-size: 0.875rem;
7771  font-weight: 600;
7772  margin-bottom: 0.5rem;
7773}
7774
7775.analysis-section h6.strengths {
7776  color: #059669;
7777}
7778
7779.analysis-section h6.weaknesses {
7780  color: #e2a642;
7781}
7782
7783.analysis-section h6.best-for {
7784  color: #7c3aed;
7785}
7786
7787.analysis-section ul {
7788  list-style: none;
7789  padding: 0;
7790  margin: 0;
7791}
7792
7793.analysis-section li {
7794  display: flex;
7795  align-items: flex-start;
7796  gap: 0.5rem;
7797  margin-bottom: 0.25rem;
7798  font-size: 0.875rem;
7799  line-height: 1.4;
7800}
7801
7802.analysis-section li:before {
7803  content: "•";
7804  color: #64748b;
7805  font-weight: bold;
7806  margin-top: 0.1rem;
7807}
7808
7809.best-for-text {
7810  font-size: 0.875rem;
7811  color: #374151;
7812  font-style: italic;
7813}
7814
7815/* Recommendations */
7816.recommendations-grid {
7817  display: grid;
7818  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
7819  gap: 1rem;
7820  margin-top: 1rem;
7821}
7822
7823.recommendation-card {
7824  background: white;
7825  border: 1px solid #e2e8f0;
7826  border-radius: 8px;
7827  padding: 1rem;
7828  text-align: center;
7829}
7830
7831.recommendation-card h5 {
7832  font-size: 1rem;
7833  margin-bottom: 0.75rem;
7834  color: #1e293b;
7835}
7836
7837.recommendation-card p {
7838  font-size: 0.875rem;
7839  color: #374151;
7840  line-height: 1.5;
7841}
7842
7843/* Tips */
7844.tips-list {
7845  list-style: none;
7846  padding: 0;
7847  margin: 1rem 0 0 0;
7848}
7849
7850.tips-list li {
7851  display: flex;
7852  align-items: flex-start;
7853  gap: 0.75rem;
7854  margin-bottom: 0.75rem;
7855  font-size: 0.9rem;
7856  line-height: 1.5;
7857  color: #374151;
7858}
7859
7860.tips-list li:before {
7861  content: "💡";
7862  font-size: 1rem;
7863  margin-top: 0.1rem;
7864}
7865
7866/* Modal Footer */
7867.modal-footer {
7868  display: flex;
7869  justify-content: space-between;
7870  align-items: center;
7871  padding: 1.5rem 2rem;
7872  border-top: 1px solid #e2e8f0;
7873  background: #f8fafc;
7874}
7875
7876.btn-outline-secondary {
7877  border: 1px solid #64748b;
7878  background: transparent;
7879  color: #64748b;
7880  padding: 0.75rem 1.5rem;
7881  border-radius: 6px;
7882  font-weight: 600;
7883  display: flex;
7884  align-items: center;
7885  gap: 0.5rem;
7886  cursor: pointer;
7887  transition: all 0.2s ease;
7888}
7889
7890.btn-outline-secondary:hover {
7891  background: #64748b;
7892  color: white;
7893}
7894
7895/* Responsive for chip button */
7896@media (max-width: 880px) {
7897  .pd-compare-section {
7898    margin: 1rem 0;
7899    display: block;
7900  }
7901
7902  .pd-compare-btn {
7903    width: 100%;
7904    font-size: 0.85rem;
7905    padding: 0.65rem 1rem;
7906    border-radius: 10px;
7907    justify-content: center;
7908    min-height: 44px;
7909  }
7910  
7911  .pd-compare-btn .compare-icon {
7912    width: 16px;
7913    height: 16px;
7914  }
7915
7916  .pd-compare-info {
7917    margin-top: 0.5rem;
7918    display: block;
7919    text-align: center;
7920  }
7921
7922  .compare-note {
7923    font-size: 0.7rem;
7924    display: block;
7925  }
7926}
7927
7928/* Responsive Design */
7929@media (max-width: 768px) {
7930  .compare-sidebar {
7931    width: 100%;
7932    right: -100%;
7933    /* Fix for mobile viewport height changes */
7934    height: 100vh;
7935    height: 100dvh; /* Dynamic viewport height for modern browsers */
7936  }
7937  
7938  .compare-actions {
7939    /* Ensure actions are always visible */
7940    position: sticky;
7941    bottom: 0;
7942    background: white;
7943    border-top: 2px solid #e2e8f0;
7944    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
7945    z-index: 10;
7946  }
7947  
7948  .compare-actions .btn {
7949    /* Larger touch targets for mobile */
7950    min-height: 48px;
7951    font-size: 0.9rem;
7952    font-weight: 700;
7953  }
7954  
7955  .modal-container {
7956    width: 95%;
7957    max-height: 95vh;
7958    max-height: 95dvh; /* Dynamic viewport height */
7959  }
7960  
7961  .modal-header,
7962  .modal-content,
7963  .modal-footer {
7964    padding: 1rem;
7965  }
7966  
7967  .recommendations-grid {
7968    grid-template-columns: 1fr;
7969  }
7970  
7971  .category-products {
7972    grid-template-columns: 1fr;
7973  }
7974  
7975  .modal-footer {
7976    flex-direction: column;
7977    gap: 0.75rem;
7978  }
7979  
7980  .modal-footer .btn {
7981    width: 100%;
7982  }
7983  
7984  /* Ensure compare button is visible on mobile */
7985  .pd-compare-section {
7986    display: block !important;
7987    visibility: visible !important;
7988    opacity: 1 !important;
7989    margin: 1rem 0;
7990    padding: 0;
7991    position: relative !important;
7992    z-index: 1 !important;
7993    max-width: 100%;
7994    overflow: hidden;
7995  }
7996  
7997  .pd-compare-btn {
7998    display: flex !important;
7999    visibility: visible !important;
8000    opacity: 1 !important;
8001    width: 100%;
8002    padding: 0.75rem 1rem;
8003    font-size: 0.9rem;
8004    min-height: 48px; /* Minimum touch target for mobile */
8005    border: 2px solid #e2e8f0 !important;
8006    background: #fff !important;
8007    position: relative !important;
8008    z-index: 2 !important;
8009    box-sizing: border-box;
8010    white-space: normal !important; /* Allow text wrapping on mobile */
8011    flex-wrap: wrap;
8012    gap: 0.5rem;
8013  }
8014  
8015  .pd-compare-btn .compare-text {
8016    display: inline !important;
8017    visibility: visible !important;
8018    font-weight: 600;
8019    color: #475569 !important;
8020  }
8021  
8022  .pd-compare-btn .compare-remove {
8023    display: none !important; /* Hidden by default */
8024  }
8025  
8026  .pd-compare-btn.added .compare-remove {
8027    display: flex !important; /* Only show when added */
8028    flex-wrap: wrap;
8029    align-items: center;
8030    gap: 0.4rem;
8031    white-space: normal !important;
8032    word-break: break-word;
8033  }
8034  
8035  .pd-compare-btn .compare-icon {
8036    display: inline-block !important;
8037    width: 18px;
8038    height: 18px;
8039    flex-shrink: 0;
8040  }
8041  
8042  .pd-compare-info {
8043    margin-top: 0.5rem;
8044  }
8045  
8046  .compare-note {
8047    font-size: 0.85rem !important;
8048    display: block !important;
8049    visibility: visible !important;
8050    color: #64748b !important;
8051  }
8052}
8053
8054/* iOS Safari specific fixes */
8055@media (max-width: 480px) {
8056  #compareBtn { 
8057    width:46px;
8058    height:46px;
8059  }
8060  
8061  .pd-compare-section {
8062    -webkit-transform: translateZ(0);
8063    transform: translateZ(0);
8064  }
8065  
8066  .pd-compare-btn {
8067    -webkit-appearance: none !important;
8068    appearance: none !important;
8069    -webkit-tap-highlight-color: transparent;
8070    touch-action: manipulation;
8071  }
8072  
8073  /* Fix sidebar height for iOS address bar */
8074  .compare-sidebar {
8075    /* Use fixed positioning that accounts for iOS address bar */
8076    height: 100vh;
8077    height: -webkit-fill-available; /* iOS Safari compatibility */
8078    min-height: 100vh;
8079    min-height: -webkit-fill-available;
8080  }
8081  
8082  /* Ensure compare actions stay at bottom */
8083  .compare-actions {
8084    position: sticky;
8085    bottom: 0;
8086    background: white;
8087    border-top: 2px solid #e2e8f0;
8088    box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.15);
8089    padding: 1.25rem 1rem;
8090    z-index: 20;
8091  }
8092  
8093  .compare-actions .btn {
8094    min-height: 52px; /* Larger for easier tapping */
8095    font-size: 1rem;
8096    font-weight: 700;
8097    border-radius: 8px;
8098  }
8099  
8100  /* Adjust content padding to account for sticky actions */
8101  .compare-content {
8102    padding-bottom: 6rem; /* Extra space for sticky actions */
8103  }
8104}
8105
8106/* High-resolution mobile devices (iPhone 12 Pro, etc.) */
8107@media (-webkit-min-device-pixel-ratio: 2) and (max-width: 430px) {
8108  .pd-compare-section {
8109    display: block !important;
8110  }
8111  
8112  .pd-compare-btn {
8113    display: flex !important;
8114    min-height: 44px;
8115    font-size: 16px; /* Prevent zoom on iOS */
8116  }
8117  
8118  /* Enhanced fixes for iPhone 12 Pro and similar devices */
8119  .compare-sidebar {
8120    /* Ensure full height coverage */
8121    height: 100vh;
8122    height: 100dvh; /* Dynamic viewport height */
8123    height: -webkit-fill-available; /* iOS fallback */
8124    max-height: 100vh;
8125    max-height: 100dvh;
8126    max-height: -webkit-fill-available;
8127    overflow: hidden; /* Prevent scroll issues */
8128  }
8129  
8130  .compare-content {
8131    /* Flexible content area */
8132    flex: 1;
8133    overflow-y: auto;
8134    -webkit-overflow-scrolling: touch;
8135    padding: 1rem 1rem 8rem 1rem; /* Extra bottom padding */
8136  }
8137  
8138  .compare-actions {
8139    /* Force actions to stay visible */
8140    position: fixed;
8141    bottom: 0;
8142    left: 0;
8143    right: 0;
8144    width: 100%;
8145    background: white;
8146    border-top: 2px solid #e2e8f0;
8147    box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.2);
8148    padding: 1rem;
8149    z-index: 9999;
8150    display: flex !important;
8151    gap: 0.75rem;
8152  }
8153  
8154  .compare-actions .btn {
8155    flex: 1;
8156    min-height: 30px; /* Extra large for high-DPI screens */
8157    font-size: .8rem;
8158    font-weight: 600;
8159    border-radius: 12px;
8160    display: flex !important;
8161    align-items: center;
8162    justify-content: center;
8163    gap: 0.5rem;
8164  }
8165  
8166  /* Ensure AI analyze button is always visible and prominent */
8167  #compareAnalyze {
8168    background: #3b82f6 !important;
8169    border-color: #3b82f6 !important;
8170    color: white !important;
8171    box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
8172  }
8173  
8174  #compareAnalyze:not(:disabled) {
8175    background: #2563eb !important;
8176    border-color: #2563eb !important;
8177    transform: scale(1.02);
8178  }
8179  
8180  #compareAnalyze:disabled {
8181    opacity: 0.6 !important;
8182    background: #9ca3af !important;
8183    border-color: #9ca3af !important;
8184  }
8185}
8186
8187/* AI Disclaimer Styles */
8188.ai-disclaimer {
8189  display: flex;
8190  align-items: flex-start;
8191  gap: 0.75rem;
8192  padding: 1rem;
8193  background: #fef3c7;
8194  border: 1px solid #C10DFF;
8195  border-radius: 8px;
8196  margin: 0;
8197}
8198
8199.disclaimer-icon {
8200  font-size: 1.25rem;
8201  flex-shrink: 0;
8202  margin-top: 0.125rem;
8203}
8204
8205.disclaimer-text {
8206  font-size: 0.85rem;
8207  line-height: 1.4;
8208  color: #ae7e21;
8209}
8210
8211.disclaimer-text strong {
8212  color: #765212;
8213  font-weight: 600;
8214}
8215
8216/* Compare Disclaimer in Sidebar */
8217.compare-disclaimer {
8218  margin-top: 1rem;
8219  padding: 1rem;
8220  background: #f0f9ff;
8221  border: 1px solid #0ea5e9;
8222  border-radius: 8px;
8223}
8224
8225.compare-disclaimer h6 {
8226  display: flex;
8227  align-items: center;
8228  gap: 0.5rem;
8229  font-size: 0.875rem;
8230  font-weight: 600;
8231  color: #0c4a6e;
8232  margin-bottom: 0.5rem;
8233}
8234
8235.compare-disclaimer p {
8236  font-size: 0.8rem;
8237  line-height: 1.4;
8238  color: #075985;
8239  margin-bottom: 0.5rem;
8240}
8241
8242.compare-disclaimer p:last-child {
8243  margin-bottom: 0;
8244}
8245
8246.compare-disclaimer ul {
8247  list-style: none;
8248  padding: 0;
8249  margin: 0.5rem 0 0 0;
8250}
8251
8252.compare-disclaimer li {
8253  display: flex;
8254  align-items: flex-start;
8255  gap: 0.5rem;
8256  margin-bottom: 0.25rem;
8257  font-size: 0.75rem;
8258  line-height: 1.3;
8259  color: #075985;
8260}
8261
8262.compare-disclaimer li:before {
8263  content: "•";
8264  color: #0ea5e9;
8265  font-weight: bold;
8266  margin-top: 0.1rem;
8267  flex-shrink: 0;
8268}
8269
8270/* Mobile Browser Address Bar Fixes */
8271@supports (height: 100dvh) {
8272  /* Modern browsers with dynamic viewport support */
8273  @media (max-width: 768px) {
8274    .compare-sidebar {
8275      height: 100dvh !important;
8276      max-height: 100dvh !important;
8277    }
8278    
8279    .modal-container {
8280      max-height: 95dvh !important;
8281    }
8282  }
8283}
8284
8285@supports (height: -webkit-fill-available) {
8286  /* iOS Safari specific support */
8287  @media (max-width: 768px) {
8288    .compare-sidebar {
8289      height: -webkit-fill-available !important;
8290      max-height: -webkit-fill-available !important;
8291    }
8292  }
8293}
8294
8295/* Fallback for older browsers */
8296@media (max-width: 768px) and (orientation: portrait) {
8297  .compare-sidebar {
8298    /* Fixed height that works across different mobile browsers */
8299    min-height: 600px;
8300    height: calc(100vh - env(keyboard-inset-height, 0px));
8301    height: calc(100vh - env(safe-area-inset-bottom, 0px));
8302  }
8303  
8304  /* Ensure actions are always accessible */
8305  .compare-actions {
8306    position: sticky !important;
8307    bottom: env(safe-area-inset-bottom, 0px) !important;
8308    margin-bottom: 0 !important;
8309    padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0px)) !important;
8310  }
8311}
8312
8313/* PWA and fullscreen app support */
8314@media (display-mode: standalone) {
8315  .compare-sidebar {
8316    height: 100vh !important;
8317    height: 100dvh !important;
8318  }
8319}
8320
8321/* Utilities */
8322.btn-sm {
8323  padding: 0.5rem 0.75rem;
8324  font-size: 0.8rem;
8325}
8326
8327[hidden] {
8328  display: none !important;
8329}
8330
8331/* ===== UX ENHANCEMENTS ===== */
8332
8333/* Compare Button Highlight Animation */
8334.pulse-highlight {
8335  animation: pulse-glow 2s ease-in-out 3;
8336  box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7);
8337}
8338
8339@keyframes pulse-glow {
8340  0% {
8341    box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7);
8342  }
8343  50% {
8344    box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
8345  }
8346  100% {
8347    box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
8348  }
8349}
8350
8351/* Flying Product Animation */
8352.flying-product {
8353  position: fixed;
8354  z-index: 9999;
8355  pointer-events: none;
8356}
8357
8358.flying-product img {
8359  border-radius: 8px;
8360  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
8361}
8362
8363/* Sticky Compare Bar - REMOVED (using only fly-to-navbar animation) */
8364
8365/* Inline Compare Preview - REMOVED (keeping it simple and clean) */
8366</style>
8367  <style data-inlined="product-card-actions.css">
8368/**
8369 * Product Card Quick Actions - 3 Dot Menu + Bottom Sheet
8370 * Tokopedia-inspired design
8371 */
8372
8373/* ===== 3 Dot Button on Product Card ===== */
8374.prod-card {
8375    position: relative;
8376}
8377
8378.prod-card-actions-btn {
8379    position: absolute;
8380    top: 8px;
8381    right: 8px;
8382    width: 32px;
8383    height: 32px;
8384    border-radius: 50%;
8385    background: rgba(255, 255, 255, 0.95);
8386    border: 1px solid rgba(0, 0, 0, 0.08);
8387    display: inline-flex;
8388    align-items: center;
8389    justify-content: center;
8390    cursor: pointer;
8391    z-index: 3;
8392    transition: all 0.2s ease;
8393    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
8394    -webkit-tap-highlight-color: transparent;
8395}
8396
8397.prod-card-actions-btn:hover {
8398    background: #fff;
8399    border-color: rgba(0, 0, 0, 0.15);
8400    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
8401    transform: scale(1.05);
8402}
8403
8404.prod-card-actions-btn:active {
8405    transform: scale(0.95);
8406}
8407
8408.prod-card-actions-btn svg {
8409    width: 20px;
8410    height: 20px;
8411    color: #64748b;
8412    display: block;
8413}
8414
8415/* Desktop: Show on hover */
8416@media (min-width: 641px) {
8417    .prod-card-actions-btn {
8418        opacity: 0;
8419        pointer-events: none;
8420    }
8421    
8422    .prod-card:hover .prod-card-actions-btn {
8423        opacity: 1;
8424        pointer-events: auto;
8425    }
8426}
8427
8428/* Mobile: Always visible but smaller */
8429@media (max-width: 640px) {
8430    .prod-card-actions-btn {
8431        width: 28px;
8432        height: 28px;
8433        top: 6px;
8434        right: 6px;
8435        opacity: 95%;
8436    }
8437    
8438    .prod-card-actions-btn svg {
8439        width: 18px;
8440        height: 18px;
8441    }
8442}
8443
8444/* ===== Bottom Sheet Overlay ===== */
8445.pca-overlay {
8446    position: fixed;
8447    inset: 0;
8448    background: rgba(15, 23, 42, 0.6);
8449    backdrop-filter: blur(4px);
8450    z-index: 99998;
8451    opacity: 0;
8452    visibility: hidden;
8453    transition: all 0.3s ease;
8454}
8455
8456.pca-overlay.active {
8457    opacity: 1;
8458    visibility: visible;
8459}
8460
8461/* ===== Bottom Sheet Container ===== */
8462.pca-bottom-sheet {
8463    position: fixed;
8464    bottom: 0;
8465    left: 0;
8466    right: 0;
8467    background: #fff;
8468    border-radius: 20px 20px 0 0;
8469    box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.15);
8470    z-index: 99999;
8471    max-width: 600px;
8472    margin: 0 auto;
8473    transform: translateY(100%);
8474    transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
8475}
8476
8477.pca-bottom-sheet.active {
8478    transform: translateY(0);
8479}
8480
8481/* Drag Handle */
8482.pca-sheet-handle {
8483    width: 40px;
8484    height: 4px;
8485    background: #cbd5e1;
8486    border-radius: 999px;
8487    margin: 12px auto 8px;
8488}
8489
8490/* Product Preview */
8491.pca-sheet-product {
8492    display: flex;
8493    gap: 12px;
8494    padding: 12px 20px;
8495    border-bottom: 1px solid #f1f5f9;
8496}
8497
8498.pca-product-image {
8499    width: 60px;
8500    height: 60px;
8501    border-radius: 8px;
8502    object-fit: cover;
8503    flex-shrink: 0;
8504    border: 1px solid #e2e8f0;
8505}
8506
8507.pca-product-info {
8508    flex: 1;
8509    min-width: 0;
8510}
8511
8512.pca-product-name {
8513    font-size: 14px;
8514    font-weight: 600;
8515    color: #0f172a;
8516    margin: 0 0 4px 0;
8517    display: -webkit-box;
8518    -webkit-line-clamp: 2;
8519    -webkit-box-orient: vertical;
8520    overflow: hidden;
8521    line-height: 1.3;
8522}
8523
8524.pca-product-price {
8525    font-size: 15px;
8526    font-weight: 700;
8527    color: #0ea5e9;
8528    margin: 0;
8529}
8530
8531.pca-product-stock {
8532    font-size: 12px;
8533    color: #64748b;
8534    margin: 2px 0 0 0;
8535}
8536
8537.pca-product-stock.in {
8538    color: #16a34a;
8539}
8540
8541.pca-product-stock.low {
8542    color: #C10DFF;
8543}
8544
8545.pca-product-stock.out {
8546    color: #e2a642;
8547}
8548
8549/* Action Menu */
8550.pca-sheet-actions {
8551    padding: 8px 0 20px;
8552}
8553
8554.pca-action-item {
8555    display: flex;
8556    align-items: center;
8557    gap: 16px;
8558    padding: 16px 20px;
8559    background: transparent;
8560    border: 0;
8561    width: 100%;
8562    cursor: pointer;
8563    transition: background-color 0.15s ease;
8564    -webkit-tap-highlight-color: transparent;
8565}
8566
8567.pca-action-item:hover {
8568    background: #f8fafc;
8569}
8570
8571.pca-action-item:active {
8572    background: #f1f5f9;
8573}
8574
8575.pca-action-item:disabled {
8576    opacity: 0.4;
8577    cursor: not-allowed;
8578}
8579
8580.pca-action-item:disabled:hover {
8581    background: transparent;
8582}
8583
8584.pca-action-icon {
8585    width: 24px;
8586    height: 24px;
8587    color: #475569;
8588    flex-shrink: 0;
8589}
8590
8591.pca-action-content {
8592    flex: 1;
8593    text-align: left;
8594}
8595
8596.pca-action-label {
8597    font-size: 15px;
8598    font-weight: 600;
8599    color: #0f172a;
8600    margin: 0 0 2px 0;
8601}
8602
8603.pca-action-desc {
8604    font-size: 12px;
8605    color: #64748b;
8606    margin: 0;
8607}
8608
8609.pca-action-badge {
8610    display: inline-flex;
8611    align-items: center;
8612    justify-content: center;
8613    min-width: 20px;
8614    height: 20px;
8615    padding: 0 6px;
8616    background: #0ea5e9;
8617    color: #fff;
8618    font-size: 11px;
8619    font-weight: 700;
8620    border-radius: 999px;
8621    margin-left: auto;
8622}
8623
8624.pca-action-badge.compare {
8625    background: #8b5cf6;
8626}
8627
8628/* Action Item Colors */
8629.pca-action-item.cart .pca-action-icon {
8630    color: #0ea5e9;
8631}
8632
8633.pca-action-item.compare .pca-action-icon {
8634    color: #8b5cf6;
8635}
8636
8637.pca-action-item.compare.active {
8638    background: rgba(139, 92, 246, 0.05);
8639}
8640
8641.pca-action-item.compare.active .pca-action-label {
8642    color: #8b5cf6;
8643}
8644
8645.pca-action-item.compare.active .pca-action-icon {
8646    color: #8b5cf6;
8647}
8648
8649.pca-action-item.share .pca-action-icon {
8650    color: #64748b;
8651}
8652
8653/* Loading State */
8654.pca-action-item.loading {
8655    pointer-events: none;
8656}
8657
8658.pca-action-item.loading .pca-action-icon {
8659    animation: spin 1s linear infinite;
8660}
8661
8662@keyframes spin {
8663    from { transform: rotate(0deg); }
8664    to { transform: rotate(360deg); }
8665}
8666
8667/* Success State Animation */
8668.pca-action-item.success .pca-action-icon {
8669    animation: checkmark 0.5s ease;
8670}
8671
8672@keyframes checkmark {
8673    0% { transform: scale(0); }
8674    50% { transform: scale(1.2); }
8675    100% { transform: scale(1); }
8676}
8677
8678/* ===== Safe Area for Mobile Devices ===== */
8679@supports (padding-bottom: env(safe-area-inset-bottom)) {
8680    .pca-sheet-actions {
8681        padding-bottom: calc(20px + env(safe-area-inset-bottom));
8682    }
8683}
8684
8685/* ===== Responsive Tweaks ===== */
8686@media (max-width: 640px) {
8687    .pca-bottom-sheet {
8688        border-radius: 16px 16px 0 0;
8689    }
8690    
8691    .pca-sheet-product {
8692        padding: 10px 16px;
8693    }
8694    
8695    .pca-product-image {
8696        width: 50px;
8697        height: 50px;
8698    }
8699    
8700    .pca-product-name {
8701        font-size: 13px;
8702    }
8703    
8704    .pca-product-price {
8705        font-size: 14px;
8706    }
8707    
8708    .pca-action-item {
8709        padding: 14px 16px;
8710        gap: 12px;
8711    }
8712    
8713    .pca-action-icon {
8714        width: 22px;
8715        height: 22px;
8716    }
8717    
8718    .pca-action-label {
8719        font-size: 14px;
8720    }
8721}
8722
8723/* ===== Animation for Flying Product ===== */
8724.flying-product {
8725    position: fixed;
8726    pointer-events: none;
8727    z-index: 99999;
8728    animation: fadeOut 0.8s ease forwards;
8729}
8730
8731@keyframes fadeOut {
8732    to {
8733        opacity: 0;
8734        transform: scale(0.5);
8735    }
8736}
8737
8738/* ===== Desktop Dropdown (Alternative) ===== */
8739@media (min-width: 1024px) {
8740    /* Optional: Could add desktop dropdown menu here */
8741    /* For now, using bottom sheet for consistency */
8742}
8743
8744/* ===== Print Styles ===== */
8745@media print {
8746    .prod-card-actions-btn,
8747    .pca-overlay,
8748    .pca-bottom-sheet {
8749        display: none !important;
8750    }
8751}
8752
8753</style>
8754  <style data-inlined="modal.css">
8755/* Modern Modal Dialog for Cart Operations */
8756.modal-overlay {
8757  position: fixed;
8758  top: 0;
8759  left: 0;
8760  right: 0;
8761  bottom: 0;
8762  background: rgba(0, 0, 0, 0.5);
8763  backdrop-filter: blur(4px);
8764  display: flex;
8765  align-items: center;
8766  justify-content: center;
8767  z-index: 9999;
8768  opacity: 0;
8769  visibility: hidden;
8770  transition: all 0.2s ease;
8771}
8772
8773.modal-overlay.show {
8774  opacity: 1;
8775  visibility: visible;
8776}
8777
8778.modal-dialog {
8779  background: white;
8780  border-radius: 16px;
8781  box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15);
8782  max-width: 400px;
8783  width: 90%;
8784  max-height: 90vh;
8785  overflow-y: auto;
8786  transform: scale(0.95);
8787  transition: transform 0.2s ease;
8788}
8789
8790.modal-overlay.show .modal-dialog {
8791  transform: scale(1);
8792}
8793
8794.modal-header {
8795  padding: 24px 24px 16px;
8796  border-bottom: 1px solid #f1f5f9;
8797}
8798
8799.modal-title {
8800  font-size: 20px;
8801  font-weight: 700;
8802  color: #0f172a;
8803  margin: 0;
8804  display: flex;
8805  align-items: center;
8806  gap: 12px;
8807}
8808
8809.modal-body {
8810  padding: 16px 24px 24px;
8811}
8812
8813.modal-message {
8814  font-size: 16px;
8815  line-height: 1.5;
8816  color: #475569;
8817  margin: 0;
8818}
8819
8820.modal-actions {
8821  display: flex;
8822  gap: 12px;
8823  margin-top: 24px;
8824}
8825
8826.modal-btn {
8827  flex: 1;
8828  padding: 12px 16px;
8829  border: none;
8830  border-radius: 8px;
8831  font-size: 14px;
8832  font-weight: 600;
8833  cursor: pointer;
8834  transition: all 0.2s ease;
8835  text-align: center;
8836}
8837
8838.modal-btn-secondary {
8839  background: #f8fafc;
8840  color: #64748b;
8841  border: 1px solid #e2e8f0;
8842}
8843
8844.modal-btn-secondary:hover {
8845  background: #f1f5f9;
8846  border-color: #cbd5e1;
8847}
8848
8849.modal-btn-danger {
8850  background: #e2a642;
8851  color: white;
8852}
8853
8854.modal-btn-danger:hover {
8855  background: #e2a642;
8856}
8857
8858.modal-btn-primary {
8859  background: #3b82f6;
8860  color: white;
8861}
8862
8863.modal-btn-primary:hover {
8864  background: #2563eb;
8865}
8866
8867.modal-icon {
8868  font-size: 24px;
8869  width: 32px;
8870  height: 32px;
8871  display: flex;
8872  align-items: center;
8873  justify-content: center;
8874  border-radius: 8px;
8875}
8876
8877.modal-icon.danger {
8878  background: #fef2f2;
8879  color: #e2a642;
8880}
8881
8882.modal-icon.warning {
8883  background: #fffbeb;
8884  color: #C10DFF;
8885}
8886
8887.modal-icon.success {
8888  background: #f0fdf4;
8889  color: #22c55e;
8890}
8891
8892.modal-icon.info {
8893  background: #eff6ff;
8894  color: #3b82f6;
8895}
8896
8897/* Mobile responsive */
8898@media (max-width: 640px) {
8899  .modal-dialog {
8900    margin: 16px;
8901    max-width: none;
8902    width: auto;
8903  }
8904  
8905  .modal-header,
8906  .modal-body {
8907    padding-left: 20px;
8908    padding-right: 20px;
8909  }
8910  
8911  .modal-actions {
8912    flex-direction: column;
8913  }
8914  
8915  .modal-btn {
8916    flex: none;
8917  }
8918}
8919</style>
8920      
8921        <style data-inlined="reviews-points.css">
8922/**
8923 * Reviews & Points System - Styles
8924 * Product reviews, rating display, and point redemption UI
8925 */
8926
8927/* ===================================
8928   PRODUCT REVIEWS
8929   =================================== */
8930
8931.product-tabs {
8932  display: flex;
8933  border-bottom: 2px solid #e5e7eb;
8934  margin: 24px 0 16px;
8935  gap: 8px;
8936}
8937
8938.tab-btn {
8939  padding: 12px 20px;
8940  border: none;
8941  background: none;
8942  cursor: pointer;
8943  font-size: 15px;
8944  font-weight: 500;
8945  color: #6b7280;
8946  border-bottom: 3px solid transparent;
8947  transition: all 0.2s ease;
8948  position: relative;
8949  top: 2px;
8950}
8951
8952.tab-btn:hover {
8953  color: #111827;
8954}
8955
8956.tab-btn.active {
8957  color: #2563eb;
8958  border-bottom-color: #2563eb;
8959}
8960
8961.tab-content {
8962  padding: 20px 0;
8963}
8964
8965/* Review Statistics */
8966.review-stats {
8967  display: grid;
8968  grid-template-columns: 200px 1fr;
8969  gap: 32px;
8970  padding: 24px;
8971  background: #f9fafb;
8972  border-radius: 12px;
8973  margin-bottom: 24px;
8974}
8975
8976.rating-summary {
8977  text-align: center;
8978}
8979
8980.avg-rating {
8981  display: block;
8982  font-size: 48px;
8983  font-weight: 700;
8984  color: #111827;
8985  line-height: 1;
8986}
8987
8988.rating-summary .stars {
8989  font-size: 24px;
8990  color: #C10DFF;
8991  margin: 8px 0;
8992}
8993
8994.total-reviews {
8995  display: block;
8996  font-size: 14px;
8997  color: #6b7280;
8998  margin-top: 4px;
8999}
9000
9001.rating-breakdown {
9002  display: flex;
9003  flex-direction: column;
9004  gap: 8px;
9005}
9006
9007.rating-bar {
9008  display: flex;
9009  align-items: center;
9010  gap: 12px;
9011}
9012
9013.rating-label {
9014  font-size: 14px;
9015  color: #374151;
9016  width: 32px;
9017  text-align: right;
9018}
9019
9020.bar-container {
9021  flex: 1;
9022  height: 8px;
9023  background: #e5e7eb;
9024  border-radius: 4px;
9025  overflow: hidden;
9026}
9027
9028.bar-fill {
9029  height: 100%;
9030  background: linear-gradient(90deg, #C10DFF 0%, #C10DFF 100%);
9031  transition: width 0.3s ease;
9032}
9033
9034.rating-count {
9035  font-size: 14px;
9036  color: #6b7280;
9037  width: 40px;
9038  text-align: right;
9039}
9040
9041/* Review Filters */
9042.review-filters {
9043  display: flex;
9044  justify-content: space-between;
9045  align-items: center;
9046  margin-bottom: 20px;
9047}
9048
9049.review-sort {
9050  padding: 8px 12px;
9051  border: 1px solid #d1d5db;
9052  border-radius: 8px;
9053  font-size: 14px;
9054  background: white;
9055}
9056
9057.write-review-btn {
9058  padding: 10px 20px;
9059  background: #2563eb;
9060  color: white;
9061  border: none;
9062  border-radius: 8px;
9063  font-weight: 500;
9064  cursor: pointer;
9065  transition: background 0.2s;
9066}
9067
9068.write-review-btn:hover {
9069  background: #1d4ed8;
9070}
9071
9072/* Review List */
9073.reviews-list {
9074  display: flex;
9075  flex-direction: column;
9076  gap: 16px;
9077}
9078
9079.review-item {
9080  padding: 20px;
9081  background: white;
9082  border: 1px solid #e5e7eb;
9083  border-radius: 12px;
9084  transition: box-shadow 0.2s;
9085}
9086
9087.review-item:hover {
9088  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
9089}
9090
9091.review-header {
9092  display: flex;
9093  justify-content: space-between;
9094  align-items: flex-start;
9095  margin-bottom: 8px;
9096}
9097
9098.review-author {
9099  display: flex;
9100  align-items: center;
9101  gap: 8px;
9102}
9103
9104.review-author strong {
9105  font-size: 15px;
9106  color: #111827;
9107}
9108
9109.verified-badge {
9110  display: inline-flex;
9111  align-items: center;
9112  gap: 4px;
9113  font-size: 12px;
9114  color: #059669;
9115  background: #d1fae5;
9116  padding: 2px 8px;
9117  border-radius: 4px;
9118  font-weight: 500;
9119}
9120
9121.anonymous-badge {
9122  display: inline-flex;
9123  align-items: center;
9124  font-size: 12px;
9125  color: #6b7280;
9126  background: #f3f4f6;
9127  padding: 2px 6px;
9128  border-radius: 4px;
9129  margin-left: 4px;
9130  cursor: help;
9131}
9132
9133.review-rating .stars {
9134  font-size: 16px;
9135  color: #C10DFF;
9136}
9137
9138.review-date {
9139  font-size: 13px;
9140  color: #9ca3af;
9141  margin-bottom: 12px;
9142}
9143
9144.review-text {
9145  font-size: 15px;
9146  line-height: 1.6;
9147  color: #374151;
9148  margin-bottom: 12px;
9149}
9150
9151.review-images {
9152  display: flex;
9153  gap: 8px;
9154  margin-bottom: 12px;
9155  flex-wrap: wrap;
9156}
9157
9158.review-img {
9159  width: 80px;
9160  height: 80px;
9161  object-fit: cover;
9162  border-radius: 8px;
9163  cursor: pointer;
9164  transition: transform 0.2s;
9165}
9166
9167.review-img:hover {
9168  transform: scale(1.05);
9169}
9170
9171.review-actions {
9172  display: flex;
9173  gap: 12px;
9174  padding-top: 12px;
9175  border-top: 1px solid #f3f4f6;
9176}
9177
9178.btn-helpful {
9179  display: flex;
9180  align-items: center;
9181  gap: 6px;
9182  padding: 6px 12px;
9183  background: #f9fafb;
9184  border: 1px solid #e5e7eb;
9185  border-radius: 6px;
9186  font-size: 13px;
9187  color: #6b7280;
9188  cursor: pointer;
9189  transition: all 0.2s;
9190}
9191
9192.btn-helpful:hover {
9193  background: #f3f4f6;
9194  color: #111827;
9195  border-color: #d1d5db;
9196}
9197
9198.btn-helpful.voted {
9199  background: #dbeafe;
9200  color: #1d4ed8;
9201  border-color: #93c5fd;
9202}
9203
9204.helpful-icon {
9205  font-size: 14px;
9206}
9207
9208.no-reviews {
9209  text-align: center;
9210  padding: 60px 20px;
9211  color: #9ca3af;
9212  font-size: 15px;
9213}
9214
9215.load-more-btn {
9216  display: block;
9217  margin: 24px auto 0;
9218  padding: 12px 32px;
9219  background: white;
9220  border: 1px solid #d1d5db;
9221  border-radius: 8px;
9222  font-size: 14px;
9223  font-weight: 500;
9224  color: #374151;
9225  cursor: pointer;
9226  transition: all 0.2s;
9227}
9228
9229.load-more-btn:hover {
9230  background: #f9fafb;
9231  border-color: #9ca3af;
9232}
9233
9234/* ===================================
9235   POINT BALANCE WIDGET (LEGACY - kept for backward compatibility)
9236   =================================== */
9237
9238.point-balance-card {
9239  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
9240  color: white;
9241  padding: 24px;
9242  border-radius: 16px;
9243  margin-bottom: 24px;
9244  box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
9245}
9246
9247.point-header {
9248  display: flex;
9249  justify-content: space-between;
9250  align-items: center;
9251  margin-bottom: 12px;
9252}
9253
9254.point-header h3 {
9255  font-size: 18px;
9256  font-weight: 600;
9257  margin: 0;
9258  display: flex;
9259  align-items: center;
9260  gap: 8px;
9261}
9262
9263.point-header .balance {
9264  font-size: 32px;
9265  font-weight: 700;
9266}
9267
9268.point-value {
9269  font-size: 14px;
9270  opacity: 0.9;
9271  margin: 8px 0 16px;
9272}
9273
9274.point-stats {
9275  display: flex;
9276  gap: 20px;
9277  font-size: 13px;
9278  opacity: 0.85;
9279  margin-bottom: 16px;
9280}
9281
9282.btn-view-history {
9283  display: inline-block;
9284  padding: 10px 20px;
9285  background: rgba(255, 255, 255, 0.2);
9286  color: white;
9287  text-decoration: none;
9288  border-radius: 8px;
9289  font-size: 14px;
9290  font-weight: 500;
9291  transition: background 0.2s;
9292}
9293
9294.btn-view-history:hover {
9295  background: rgba(255, 255, 255, 0.3);
9296}
9297
9298/* ===================================
9299   CHECKOUT POINT REDEMPTION
9300   =================================== */
9301
9302.checkout-points {
9303  padding: 16px;
9304  background: #fef3c7;
9305  border: 2px solid #C10DFF;
9306  border-radius: 12px;
9307  margin-bottom: 16px;
9308}
9309
9310.points-header {
9311  display: flex;
9312  align-items: center;
9313  gap: 10px;
9314  margin-bottom: 12px;
9315}
9316
9317.points-header input[type="checkbox"] {
9318  width: 18px;
9319  height: 18px;
9320  cursor: pointer;
9321}
9322
9323.points-header label {
9324  font-size: 15px;
9325  color: #765212;
9326  cursor: pointer;
9327  margin: 0;
9328}
9329
9330#points-slider {
9331  padding: 16px;
9332  background: white;
9333  border-radius: 8px;
9334  margin-top: 12px;
9335}
9336
9337#points-slider label {
9338  display: block;
9339  font-size: 14px;
9340  color: #374151;
9341  margin-bottom: 8px;
9342  font-weight: 500;
9343}
9344
9345#points-amount {
9346  width: 100%;
9347  margin-bottom: 8px;
9348}
9349
9350#points-display {
9351  display: flex;
9352  justify-content: space-between;
9353  font-size: 14px;
9354  color: #111827;
9355  margin-top: 8px;
9356}
9357
9358#points-display .discount-amount {
9359  color: #059669;
9360  font-weight: 600;
9361}
9362
9363.price-row.point-discount {
9364  color: #059669;
9365  font-weight: 500;
9366}
9367
9368/* ===================================
9369   REVIEWABLE ORDERS
9370   =================================== */
9371
9372.reviewable-orders {
9373  background: white;
9374  border: 1px solid #e5e7eb;
9375  border-radius: 12px;
9376  padding: 20px;
9377  margin-bottom: 24px;
9378}
9379
9380.reviewable-orders h3 {
9381  font-size: 18px;
9382  margin-bottom: 16px;
9383  color: #111827;
9384}
9385
9386.order-card {
9387  display: flex;
9388  gap: 16px;
9389  padding: 16px;
9390  background: #f9fafb;
9391  border-radius: 8px;
9392  margin-bottom: 12px;
9393}
9394
9395.order-card:last-child {
9396  margin-bottom: 0;
9397}
9398
9399.order-card img {
9400  width: 80px;
9401  height: 80px;
9402  object-fit: cover;
9403  border-radius: 8px;
9404}
9405
9406.order-info {
9407  flex: 1;
9408}
9409
9410.order-info h4 {
9411  font-size: 15px;
9412  margin: 0 0 4px;
9413  color: #111827;
9414}
9415
9416.order-info p {
9417  font-size: 13px;
9418  color: #6b7280;
9419  margin: 0 0 12px;
9420}
9421
9422.btn-review {
9423  padding: 8px 16px;
9424  background: #059669;
9425  color: white;
9426  border: none;
9427  border-radius: 6px;
9428  font-size: 14px;
9429  font-weight: 500;
9430  cursor: pointer;
9431  transition: background 0.2s;
9432}
9433
9434.btn-review:hover {
9435  background: #047857;
9436}
9437
9438/* ===================================
9439   TOAST NOTIFICATIONS
9440   =================================== */
9441
9442.review-toast {
9443  position: fixed;
9444  bottom: 24px;
9445  right: 24px;
9446  padding: 14px 20px;
9447  background: #111827;
9448  color: white;
9449  border-radius: 8px;
9450  font-size: 14px;
9451  line-height: 1.4;
9452  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
9453  transform: translateY(100px);
9454  opacity: 0;
9455  transition: all 0.3s ease;
9456  z-index: 10000;
9457  max-width: 400px;
9458  word-wrap: break-word;
9459}
9460
9461.review-toast.show {
9462  transform: translateY(0);
9463  opacity: 1;
9464}
9465
9466/* Mobile: Position above bottom-nav */
9467@media (max-width: 768px) {
9468  .review-toast {
9469    bottom: 80px; /* Above bottom-nav (50px height + padding + safe margin) */
9470    right: 16px;
9471    left: 16px;
9472    max-width: calc(100% - 32px);
9473  }
9474}
9475
9476.review-toast.toast-success {
9477  background: #059669;
9478}
9479
9480.review-toast.toast-error {
9481  background: #e2a642;
9482}
9483
9484.review-toast.toast-info {
9485  background: #2563eb;
9486}
9487
9488/* ===================================
9489   RESPONSIVE
9490   =================================== */
9491
9492@media (max-width: 768px) {
9493  .point-balance-card {
9494    border-radius: 0;
9495  }
9496  
9497  .review-stats {
9498    grid-template-columns: 70px 1fr;
9499    gap: 20px;
9500    padding: 16px;
9501  }
9502
9503  .rating-summary {
9504    text-align: center;
9505  }
9506  
9507  .rating-summary .avg-rating {
9508    font-size: 32px;
9509  }
9510  
9511  .rating-summary .stars {
9512    font-size: 18px;
9513    margin: 4px 0;
9514  }
9515  
9516  .total-reviews {
9517    font-size: 11px;
9518  }
9519  
9520  /* Rating breakdown - more compact */
9521  .rating-breakdown {
9522    gap: 2px;
9523  }
9524  
9525  .rating-breakdown .rating-bar {
9526    gap: 4px;
9527  }
9528  
9529  .rating-breakdown .rating-label {
9530    font-size: 11px;
9531    width: 16px;
9532    min-width: 16px;
9533    text-align: left;
9534  }
9535  
9536  .rating-breakdown .bar-container {
9537    height: 6px;
9538  }
9539  
9540  .rating-breakdown .rating-count {
9541    font-size: 11px;
9542    width: 16px;
9543    min-width: 16px;
9544  }
9545  
9546  .review-header {
9547    flex-direction: column;
9548    gap: 8px;
9549    align-items: flex-start;
9550  }
9551  
9552  .review-rating {
9553    order: -1;
9554    margin-bottom: 4px;
9555  }
9556  
9557  .review-rating .stars {
9558    font-size: 14px;
9559  }
9560
9561  .review-filters {
9562    flex-direction: column;
9563    gap: 12px;
9564    align-items: stretch;
9565  }
9566
9567  .review-sort {
9568    width: 100%;
9569  }
9570
9571  .point-header .balance {
9572    font-size: 24px;
9573  }
9574
9575  .point-stats {
9576    flex-direction: column;
9577    gap: 8px;
9578  }
9579
9580  .order-card {
9581    flex-direction: column;
9582  }
9583
9584  .order-card img {
9585    width: 100%;
9586    height: 200px;
9587  }
9588}
9589
9590/* ===================================
9591   LOADING & ERROR STATES
9592   =================================== */
9593
9594.review-loading {
9595  text-align: center;
9596  padding: 40px;
9597  color: #9ca3af;
9598  font-size: 14px;
9599}
9600
9601.review-error {
9602  text-align: center;
9603  padding: 40px;
9604  color: #e2a642;
9605  font-size: 14px;
9606  background: #fee2e2;
9607  border-radius: 8px;
9608}
9609
9610.review-empty {
9611  text-align: center;
9612  padding: 10px;
9613  color: #6b7280;
9614  font-size: 14px;
9615}
9616
9617/* ===================================
9618   REVIEWABLE ORDERS LIST
9619   =================================== */
9620
9621.reviewable-orders-list {
9622  display: flex;
9623  flex-direction: column;
9624  gap: 16px;
9625}
9626
9627.reviewable-order-item {
9628  display: grid;
9629  grid-template-columns: 80px 1fr auto;
9630  gap: 16px;
9631  padding: 16px;
9632  background: #fff;
9633  border: 1px solid #e5e7eb;
9634  border-radius: 12px;
9635  align-items: center;
9636  transition: box-shadow 0.2s;
9637}
9638
9639.reviewable-order-item:hover {
9640  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
9641}
9642
9643.reviewable-order-item .roi-image {
9644  width: 80px;
9645  height: 80px;
9646  object-fit: cover;
9647  border-radius: 8px;
9648  border: 1px solid #e5e7eb;
9649}
9650
9651.reviewable-order-item .roi-info {
9652  display: flex;
9653  flex-direction: column;
9654  gap: 4px;
9655}
9656
9657.reviewable-order-item .roi-info h4 {
9658  margin: 0;
9659  font-size: 15px;
9660  font-weight: 600;
9661  color: #111827;
9662  line-height: 1.4;
9663}
9664
9665.reviewable-order-item .roi-meta {
9666  margin: 0;
9667  font-size: 13px;
9668  color: #6b7280;
9669}
9670
9671.reviewable-order-item .btn-write-review {
9672  white-space: nowrap;
9673  padding: 10px 20px;
9674  font-size: 14px;
9675}
9676
9677@media (max-width: 640px) {
9678  .reviewable-order-item {
9679    grid-template-columns: 60px 1fr;
9680    gap: 12px;
9681  }
9682  
9683  .reviewable-order-item .roi-image {
9684    width: 60px;
9685    height: 60px;
9686  }
9687  
9688  .reviewable-order-item .btn-write-review {
9689    grid-column: 1 / -1;
9690    width: 100%;
9691  }
9692}
9693
9694/* ===================================
9695   REVIEW SUBMISSION MODAL
9696   =================================== */
9697
9698/* Modal Overlay Background */
9699#review-modal-overlay {
9700  position: fixed;
9701  top: 0;
9702  left: 0;
9703  right: 0;
9704  bottom: 0;
9705  background: rgba(0, 0, 0, 0.6) !important;
9706  backdrop-filter: blur(8px);
9707  display: flex;
9708  align-items: center;
9709  justify-content: center;
9710  z-index: 99999 !important;
9711  padding: 20px;
9712}
9713
9714.review-modal {
9715  max-width: 600px !important;
9716  width: 90% !important;
9717  background: white;
9718  border-radius: 16px !important;
9719  box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
9720  max-height: 90vh;
9721  display: flex;
9722  flex-direction: column;
9723  overflow: hidden !important;
9724}
9725
9726#review-modal-overlay .modal-content,
9727.review-modal .modal-content {
9728  padding: 0 !important;
9729  flex: initial !important;
9730  overflow-y: visible !important;
9731}
9732
9733.review-modal .modal-header {
9734  display: flex;
9735  justify-content: space-between;
9736  align-items: center;
9737  padding: 20px 24px;
9738  border-bottom: 1px solid #e5e7eb;
9739}
9740
9741.review-modal .modal-title {
9742  margin: 0;
9743  font-size: 20px;
9744  font-weight: 700;
9745  color: #111827;
9746}
9747
9748.review-modal .modal-close {
9749  background: none;
9750  border: none;
9751  cursor: pointer;
9752  padding: 4px;
9753  color: #6b7280;
9754  transition: color 0.2s;
9755  display: flex;
9756  align-items: center;
9757  justify-content: center;
9758}
9759
9760.review-modal .modal-close:hover {
9761  color: #111827;
9762}
9763
9764.review-modal .modal-body {
9765  padding: 24px;
9766  max-height: calc(90vh - 140px);
9767  overflow-y: auto;
9768}
9769
9770/* Custom Scrollbar for Modal */
9771.review-modal .modal-body::-webkit-scrollbar {
9772  width: 8px;
9773}
9774
9775.review-modal .modal-body::-webkit-scrollbar-track {
9776  background: #f1f5f9;
9777  border-radius: 4px;
9778}
9779
9780.review-modal .modal-body::-webkit-scrollbar-thumb {
9781  background: linear-gradient(180deg, #3b82f6 0%, #2563eb 100%);
9782  border-radius: 4px;
9783}
9784
9785.review-modal .modal-body::-webkit-scrollbar-thumb:hover {
9786  background: linear-gradient(180deg, #2563eb 0%, #1d4ed8 100%);
9787}
9788
9789/* Firefox Scrollbar */
9790.review-modal .modal-body {
9791  scrollbar-width: thin;
9792  scrollbar-color: #3b82f6 #f1f5f9;
9793}
9794
9795.review-modal .modal-footer {
9796  display: flex;
9797  gap: 12px;
9798  margin-top: 24px;
9799  padding-top: 20px;
9800  border-top: 1px solid #f1f5f9;
9801}
9802
9803.review-product-info {
9804  display: flex;
9805  gap: 16px;
9806  padding: 16px;
9807  background: #f9fafb;
9808  border-radius: 8px;
9809  margin-bottom: 24px;
9810}
9811
9812.review-product-thumb {
9813  width: 80px;
9814  height: 80px;
9815  object-fit: cover;
9816  border-radius: 6px;
9817  border: 1px solid #e5e7eb;
9818}
9819
9820.review-product-details h4 {
9821  margin: 0 0 8px 0;
9822  font-size: 16px;
9823  font-weight: 600;
9824  color: #111827;
9825}
9826
9827.review-product-details p {
9828  margin: 0;
9829  font-size: 13px;
9830  color: #6b7280;
9831}
9832
9833/* Star Rating Input */
9834.star-rating-input {
9835  display: inline-flex;
9836  gap: 8px;
9837  margin-right: 12px;
9838  vertical-align: middle;
9839}
9840
9841.star-btn {
9842  background: none;
9843  border: none;
9844  font-size: 32px;
9845  color: #d1d5db;
9846  cursor: pointer;
9847  transition: all 0.2s;
9848  padding: 0;
9849  line-height: 1;
9850}
9851
9852.star-btn:hover,
9853.star-btn.active {
9854  color: #C10DFF;
9855  transform: scale(1.1);
9856}
9857
9858.rating-label {
9859  display: inline-block;
9860  font-size: 15px;
9861  color: #6b7280;
9862  margin: 0;
9863  vertical-align: middle;
9864  min-width: 150px;
9865}
9866
9867/* Form Elements */
9868.review-form .form-group {
9869  margin-bottom: 20px;
9870}
9871
9872.review-form .form-label {
9873  display: block;
9874  font-size: 14px;
9875  font-weight: 600;
9876  color: #374151;
9877  margin-bottom: 8px;
9878}
9879
9880.review-form .form-control {
9881  width: 100%;
9882  padding: 12px;
9883  border: 1px solid #d1d5db;
9884  border-radius: 8px;
9885  font-size: 14px;
9886  font-family: inherit;
9887  transition: border-color 0.2s;
9888}
9889
9890.review-form .form-control:focus {
9891  outline: none;
9892  border-color: #2563eb;
9893  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
9894}
9895
9896.review-form .form-hint {
9897  font-size: 13px;
9898  color: #6b7280;
9899  margin-top: 8px;
9900}
9901
9902/* Image Upload */
9903.btn-upload-trigger {
9904  display: inline-flex;
9905  align-items: center;
9906  gap: 8px;
9907  padding: 10px 16px;
9908  background: #f9fafb;
9909  border: 2px dashed #d1d5db;
9910  border-radius: 8px;
9911  color: #374151;
9912  cursor: pointer;
9913  font-size: 14px;
9914  font-weight: 500;
9915  transition: all 0.2s;
9916}
9917
9918.btn-upload-trigger:hover {
9919  background: #f3f4f6;
9920  border-color: #2563eb;
9921  color: #2563eb;
9922}
9923
9924.image-preview-grid {
9925  display: grid;
9926  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
9927  gap: 12px;
9928  margin-top: 16px;
9929}
9930
9931.image-preview-item {
9932  position: relative;
9933  aspect-ratio: 1;
9934  border-radius: 8px;
9935  overflow: hidden;
9936  border: 1px solid #e5e7eb;
9937}
9938
9939.image-preview-item img {
9940  width: 100%;
9941  height: 100%;
9942  object-fit: cover;
9943}
9944
9945.remove-image {
9946  position: absolute;
9947  top: 4px;
9948  right: 4px;
9949  background: rgba(0, 0, 0, 0.7);
9950  border: none;
9951  color: white;
9952  width: 24px;
9953  height: 24px;
9954  border-radius: 50%;
9955  cursor: pointer;
9956  display: flex;
9957  align-items: center;
9958  justify-content: center;
9959  transition: background 0.2s;
9960}
9961
9962.remove-image:hover {
9963  background: rgba(0, 0, 0, 0.9);
9964}
9965
9966/* Point Reward Info */
9967.point-reward-info {
9968  background: linear-gradient(135deg, #fef3c7 0%, #C10DFF 100%);
9969  border: 2px solid #C10DFF;
9970  border-radius: 12px;
9971  padding: 16px;
9972  margin: 20px 0;
9973}
9974
9975.pri-header {
9976  display: flex;
9977  align-items: center;
9978  gap: 8px;
9979  font-size: 16px;
9980  font-weight: 700;
9981  color: #ae7e21;
9982  margin-bottom: 12px;
9983}
9984
9985.pri-list {
9986  list-style: none;
9987  padding: 0;
9988  margin: 0 0 12px 0;
9989}
9990
9991.pri-list li {
9992  padding: 6px 0;
9993  font-size: 14px;
9994  color: #765212;
9995  transition: opacity 0.2s;
9996}
9997
9998.pri-total {
9999  padding-top: 12px;
10000  border-top: 2px solid rgba(245,202,86,0.3);
10001  font-size: 16px;
10002  color: #ae7e21;
10003  font-weight: 600;
10004}
10005
10006.form-error {
10007  background: #fee2e2;
10008  border: 1px solid #e2a642;
10009  color: #a36422;
10010  padding: 12px;
10011  border-radius: 8px;
10012  font-size: 14px;
10013  margin-top: 16px;
10014}
10015
10016/* Buttons */
10017.review-form .btn {
10018  padding: 12px 24px;
10019  border: none;
10020  border-radius: 8px;
10021  font-size: 15px;
10022  font-weight: 600;
10023  cursor: pointer;
10024  transition: all 0.2s;
10025}
10026
10027.review-form .btn-primary {
10028  background: #2563eb;
10029  color: white;
10030}
10031
10032.review-form .btn-primary:hover {
10033  background: #1d4ed8;
10034}
10035
10036.review-form .btn-primary:disabled {
10037  background: #94a3b8;
10038  cursor: not-allowed;
10039}
10040
10041.review-form .btn-secondary {
10042  background: #f1f5f9;
10043  color: #64748b;
10044}
10045
10046.review-form .btn-secondary:hover {
10047  background: #e2e8f0;
10048  color: #475569;
10049}
10050
10051@media (max-width: 768px) {
10052    #review-modal-overlay { padding: 0; }
10053    .review-modal { max-width: 100%; width: 100% !important; }
10054}
10055
10056/* ===================================
10057   POINT SUMMARY GRID (Profile Page)
10058   =================================== */
10059
10060.point-summary-grid {
10061  display: grid;
10062  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
10063  gap: 12px;
10064  margin-bottom: 32px;
10065}
10066
10067.point-summary-card {
10068  background: linear-gradient(135deg, #f9fafb 0%, #ffffff 100%);
10069  border: 1px solid #e5e7eb;
10070  border-radius: 12px;
10071  padding: 16px 20px;
10072  display: flex;
10073  align-items: center;
10074  justify-content: space-between;
10075  gap: 12px;
10076  transition: all 0.2s;
10077}
10078
10079.point-summary-card:hover {
10080  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
10081  transform: translateY(-2px);
10082}
10083
10084.point-summary-card .psc-left {
10085  display: flex;
10086  flex-direction: column;
10087  gap: 2px;
10088}
10089
10090.point-summary-card .psc-icon {
10091  font-size: 24px;
10092  margin-bottom: 4px;
10093}
10094
10095.point-summary-card .psc-label {
10096  font-size: 13px;
10097  color: #6b7280;
10098  font-weight: 500;
10099  text-transform: uppercase;
10100  letter-spacing: 0.5px;
10101}
10102
10103.point-summary-card .psc-value {
10104  font-size: 26px;
10105  font-weight: 700;
10106  color: #111827;
10107  flex-shrink: 0;
10108}
10109
10110/* Point History Section */
10111.point-history-section {
10112  margin-top: 32px;
10113}
10114
10115.point-history-section h3 {
10116  font-size: 18px;
10117  font-weight: 600;
10118  color: #111827;
10119  margin-bottom: 16px;
10120}
10121
10122.point-history-list {
10123  display: flex;
10124  flex-direction: column;
10125  gap: 12px;
10126}
10127
10128.point-history-item {
10129  display: grid;
10130  grid-template-columns: 40px 1fr auto;
10131  gap: 16px;
10132  padding: 16px;
10133  background: white;
10134  border: 1px solid #e5e7eb;
10135  border-radius: 10px;
10136  align-items: center;
10137  transition: all 0.2s;
10138}
10139
10140.point-history-item:hover {
10141  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
10142}
10143
10144.point-history-item.credit {
10145  border-left: 3px solid #10b981;
10146}
10147
10148.point-history-item.debit {
10149  border-left: 3px solid #C10DFF;
10150}
10151
10152.point-history-item .phi-icon {
10153  width: 40px;
10154  height: 40px;
10155  display: flex;
10156  align-items: center;
10157  justify-content: center;
10158  font-size: 20px;
10159  border-radius: 50%;
10160  background: #f9fafb;
10161}
10162
10163.point-history-item.credit .phi-icon {
10164  background: #d1fae5;
10165}
10166
10167.point-history-item.debit .phi-icon {
10168  background: #fef3c7;
10169}
10170
10171.point-history-item .phi-details {
10172  display: flex;
10173  flex-direction: column;
10174  gap: 4px;
10175}
10176
10177.point-history-item .phi-desc {
10178  font-size: 15px;
10179  font-weight: 500;
10180  color: #111827;
10181}
10182
10183.point-history-item .phi-date {
10184  font-size: 13px;
10185  color: #9ca3af;
10186}
10187
10188.point-history-item .phi-amount {
10189  font-size: 18px;
10190  font-weight: 700;
10191}
10192
10193.point-history-item .phi-amount.credit {
10194  color: #10b981;
10195}
10196
10197.point-history-item .phi-amount.debit {
10198  color: #C10DFF;
10199}
10200
10201@media (max-width: 640px) {
10202  .point-summary-grid {
10203    grid-template-columns: 1fr;
10204  }
10205  
10206  .point-summary-card .psc-value {
10207    font-size: 22px;
10208  }
10209
10210  .point-history-item {
10211    grid-template-columns: 36px 1fr;
10212    gap: 12px;
10213  }
10214  
10215  .point-history-item .phi-amount {
10216    grid-column: 2;
10217    text-align: right;
10218    margin-top: 8px;
10219  }
10220}
10221
10222/* ── Pending points: compact widget indicator ── */
10223.pbc-pending {
10224  font-size: 12px;
10225  opacity: 0.85;
10226  margin-top: 4px;
10227  color: #C10DFF;
10228}
10229
10230/* ── Pending points: summary card ── */
10231.point-summary-card.psc-pending-card {
10232  background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
10233  border-color: #C10DFF;
10234}
10235
10236.point-summary-card.psc-pending-card .psc-value {
10237  color: #daa837;
10238}
10239
10240.point-summary-card .psc-sub {
10241  font-size: 11px;
10242  color: #9ca3af;
10243  margin-top: 4px;
10244}
10245
10246/* ── Pending points: history item ── */
10247.point-history-item.phi-pending {
10248  opacity: 0.75;
10249  border-left-color: #C10DFF;
10250  background: #fffbeb;
10251}
10252
10253.point-history-item.phi-pending .phi-icon {
10254  background: #fef3c7;
10255}
10256
10257.phi-pending-badge {
10258  display: inline-block;
10259  font-size: 11px;
10260  font-weight: 600;
10261  padding: 2px 7px;
10262  border-radius: 10px;
10263  background: #fef3c7;
10264  color: #ae7e21;
10265  border: 1px solid #C10DFF;
10266  margin-left: 8px;
10267  vertical-align: middle;
10268}
10269
10270/* ── Reversed points: history item ── */
10271.point-history-item.phi-reversed {
10272  opacity: 0.55;
10273  border-left-color: #d1d5db;
10274  background: #f9fafb;
10275}
10276
10277.point-history-item.phi-reversed .phi-icon {
10278  background: #f3f4f6;
10279}
10280
10281.point-history-item.phi-reversed .phi-amount {
10282  text-decoration: line-through;
10283  color: #9ca3af !important;
10284}
10285
10286.phi-reversed-badge {
10287  display: inline-block;
10288  font-size: 11px;
10289  font-weight: 600;
10290  padding: 2px 7px;
10291  border-radius: 10px;
10292  background: #fee2e2;
10293  color: #a36422;
10294  border: 1px solid #e2a642;
10295  margin-left: 8px;
10296  vertical-align: middle;
10297}
10298
10299</style>
10300            
10301</head>
10302<body>
10303 
10304<header class="topbar">
10305  <div class="container topbar-inner">
10306    <div class="topbar-left">
10307      NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif
10308    </div>
10309    <div class="topbar-right">
10310      <a href="https://sunnypork.com/">NAGARP</a>
10311      <a href="https://sunnypork.com/">NAGARP SITUS</a>
10312      <a href="https://sunnypork.com/">Perubahan Teknologi Digital</a>
10313      <a href="https://sunnypork.com/">Pengalaman Platform Interaktif</a>
10314    </div>
10315  </div>
10316</header>
10317<nav class="navbar">
10318  <nav class="navbar-logo" style="width: 100%;">
10319    <div class="container nav-inner" style="justify-items: center; justify-content: center;">
10320        <a href="https://sunnypork.com/" class="logo-mobile" title="NAGARP">
10321          <img src="https://sunnypork.com/assets/images/logo-nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.png" alt="NAGARP" height="40">
10322        </a>
10323    </div>
10324</nav>
10325  <div class="container nav-inner">
10326    <a href="https://sunnypork.com/" class="logo" title="NAGARP">
10327      <img src="https://sunnypork.com/assets/images/logo-nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.png" alt="NAGARP" height="40">
10328    </a>
10329
10330    
10331    <form class="search" method="get" action="#">
10332      <input type="hidden" name="page" value="produk">
10333      
10334      <!-- Search Icon (Desktop) -->
10335      <button type="submit" class="search-icon-btn" id="searchBtn" aria-label="Cari">
10336        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
10337          <circle cx="11" cy="11" r="8"/>
10338          <path d="m21 21-4.35-4.35"/>
10339        </svg>
10340      </button>
10341      
10342      <input type="text" name="q" id="searchInput" placeholder="PENCARIAN NAGARP"
10343             value="" autocomplete="off"> 
10344      
10345      <div class="search-suggest" id="searchSuggest" hidden></div>
10346    </form>
10347
10348    <div class="nav-actions">
10349      <!--<button class="icon-btn" id="notifBtn" title="Notifikasi">🔔</button>-->
10350      <button class="icon-btn cart-indicator" id="cartBtn" title="Keranjang">
10351        🛒 <span id="cartCount" class="badge" data-role="cart-badge" aria-live="polite">0</span>
10352      </button>
10353
10354      <button class="icon-btn compare-indicator" id="compareBtn" title="Perbandingan">
10355        ⚖️ <span id="compareCount" class="badge" data-role="compare-badge" aria-live="polite">0</span>
10356      </button>
10357      
10357<script src="/https://www.mothercare.co/etc.clientlibs/samsung/clientlibs/consumer/global/clientlib-common/global/js/crypto-js.min.6fa2276cf659f30cabde72a6fc4521e4.js"></script>
10357
10358      <!-- Dynamic login/logout button -->
10359            <button class="login-btn" id="loginBtn" aria-label="Masuk">
10360        <span class="login-text">Masuk</span>
10361        <span class="login-ico" aria-hidden="true">😊</span>
10362      </button>
10363          </div>
10364  </div>
10365
10366  <div class="container" style="display: flex; align-items: center;">
10367      
10368
10369      <div class="mega-cats-wrapper">
10370        <div class="mega-cats container" id="megaCatsScroll" style="padding-left: 5px !important;">
10371                      <a class="" href="https://sunnypork.com/">
10372              NAGARP           </a>
10373                      <a class="" href="https://sunnypork.com/">
10374              NAGARP SLOT           </a>
10375                      <a class="" href="https://sunnypork.com/">
10376              NAGARP Pengalaman Platform Interaktif            </a>
10377                      <a class="" href="https://sunnypork.com/">
10378              NAGARP SLOT88           </a>
10379                      <a class="" href="https://sunnypork.com/">
10380              SLOT           </a>
10381                      <a class="" href="https://sunnypork.com/">
10382              Platform Interaktif          </a>
10383                  </div>
10384        <div class="mega-cats-fade" id="megaCatsFade"></div>
10385      </div>
10386  </div>
10387</nav>
10388
10389<!-- Panel Mega Menu - Outside navbar for full-screen -->
10390<div class="mm2-panel" data-mm2="panel" role="dialog" aria-label="Kategori">
10391  <div class="mm2-view is-active" data-mm2="top"></div>
10392  <div class="mm2-view" data-mm2="detail"></div>
10393</div>
10394
10395<!-- Backdrop - Outside navbar for full-screen -->
10396<div class="mm2-backdrop" data-mm2="backdrop"></div>
10397
10398<!-- Mobile Bottom Navigation -->
10399<nav class="bottom-nav">
10400  <div class="bottom-nav-inner">
10401    <!-- Home -->
10402    <a href="/" class="bottom-nav-item" id="bottomHomeBtn" data-page="home">
10403      <div class="bottom-nav-item-icon">
10404        <!-- Outlined icon (default) -->
10405        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10406          <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
10407          <polyline points="9 22 9 12 15 12 15 22"/>
10408        </svg>
10409        <!-- Filled icon (active) -->
10410        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10411          <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"/>
10412          <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"/>
10413        </svg>
10414      </div>
10415      <span class="bottom-nav-item-label">Home</span>
10416    </a>
10417
10418    <!-- Kategori -->
10419    <button class="bottom-nav-item" id="bottomCategoryBtn" data-mm2="trigger" aria-label="Kategori">
10420      <div class="bottom-nav-item-icon">
10421        <!-- Outlined icon (default) -->
10422        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10423          <line x1="3" y1="12" x2="21" y2="12"/>
10424          <line x1="3" y1="6" x2="21" y2="6"/>
10425          <line x1="3" y1="18" x2="21" y2="18"/>
10426        </svg>
10427        <!-- Filled icon (active) -->
10428        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10429          <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"/>
10430        </svg>
10431      </div>
10432      <span class="bottom-nav-item-label">Kategori</span>
10433    </button>
10434
10435    <!-- Keranjang -->
10436    <button class="bottom-nav-item" id="bottomCartBtn">
10437      <div class="bottom-nav-item-icon">
10438        <!-- Outlined icon (default) -->
10439        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10440          <circle cx="9" cy="21" r="1"/>
10441          <circle cx="20" cy="21" r="1"/>
10442          <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/>
10443        </svg>
10444        <!-- Filled icon (active) -->
10445        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10446          <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"/>
10447        </svg>
10448        <span class="bottom-nav-item-badge" id="bottomCartCount">0</span>
10449      </div>
10450      <span class="bottom-nav-item-label">Keranjang</span>
10451    </button>
10452
10453    <!-- Compare -->
10454    <button class="bottom-nav-item" id="bottomCompareBtn">
10455      <div class="bottom-nav-item-icon">
10456        <!-- Outlined icon (default) -->
10457        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10458          <polyline points="17 11 21 7 17 3"/>
10459          <path d="M21 7H9"/>
10460          <polyline points="7 21 3 17 7 13"/>
10461          <path d="M15 17H3"/>
10462        </svg>
10463        <!-- Filled icon (active) -->
10464        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10465          <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"/>
10466        </svg>
10467        <span class="bottom-nav-item-badge" id="bottomCompareCount" style="display:none;">0</span>
10468      </div>
10469      <span class="bottom-nav-item-label">Compare</span>
10470    </button>
10471
10472    <!-- Chat - Always show, check login in JavaScript -->
10473    <button class="bottom-nav-item" id="bottomChatBtn" data-page="chat">
10474      <div class="bottom-nav-item-icon">
10475        <!-- Outlined icon (default) -->
10476        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" opacity="0.85">
10477          <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
10478        </svg>
10479        <!-- Filled icon (active) -->
10480        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10481          <path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/>
10482        </svg>
10483        <span class="bottom-nav-item-badge" id="bottomChatCount" style="display:none;">0</span>
10484      </div>
10485      <span class="bottom-nav-item-label">Chat</span>
10486    </button>
10487
10488    <!-- Akun -->
10489        <button class="bottom-nav-item" id="bottomLoginBtn">
10490      <div class="bottom-nav-item-icon">
10491        <!-- Outlined icon (default) -->
10492        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10493          <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
10494          <circle cx="12" cy="7" r="4"/>
10495        </svg>
10496        <!-- Filled icon (active) -->
10497        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10498          <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"/>
10499        </svg>
10500      </div>
10501      <span class="bottom-nav-item-label">Login</span>
10502    </button>
10503      </div>
10504</nav>
10505
10506<!-- Mobile User Menu Bottom Sheet -->
10507
10508<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
10509(function(){
10510  const btn = document.getElementById('megaMoreBtn');
10511  const dd  = document.getElementById('megaMoreDropdown');
10512  if(!btn || !dd) return;
10513
10514  function openDD(){
10515    dd.hidden = false;
10516    btn.setAttribute('aria-expanded','true');
10517    document.addEventListener('click', outside, {capture:true});
10518    document.addEventListener('keydown', esc);
10519  }
10520  function closeDD(){
10521    if(dd.hidden) return;
10522    dd.hidden = true;
10523    btn.setAttribute('aria-expanded','false');
10524    document.removeEventListener('click', outside, {capture:true});
10525    document.removeEventListener('keydown', esc);
10526  }
10527  function toggle(){ dd.hidden ? openDD() : closeDD(); }
10528  function outside(e){
10529    if(!dd.contains(e.target) && e.target !== btn){
10530      closeDD();
10531    }
10532  }
10533  function esc(e){
10534    if(e.key === 'Escape'){
10535      closeDD();
10536      btn.focus();
10537    }
10538  }
10539  btn.addEventListener('click', toggle);
10540})();
10541
10542// ========== HORIZONTAL SCROLL FADE INDICATOR ==========
10543(function(){
10544  const megaCats = document.getElementById('megaCatsScroll');
10545  const fade = document.getElementById('megaCatsFade');
10546  if (!megaCats || !fade) return;
10547
10548  function updateFade() {
10549    const scrollLeft = megaCats.scrollLeft;
10550    const scrollWidth = megaCats.scrollWidth;
10551    const clientWidth = megaCats.clientWidth;
10552    const maxScroll = scrollWidth - clientWidth;
10553    
10554    // Hide fade when scrolled to end (within 10px tolerance)
10555    if (maxScroll - scrollLeft <= 10) {
10556      fade.style.opacity = '0';
10557    } else {
10558      fade.style.opacity = '1';
10559    }
10560  }
10561
10562  // Update on scroll
10563  megaCats.addEventListener('scroll', updateFade, { passive: true });
10564  
10565  // Update on resize
10566  window.addEventListener('resize', updateFade, { passive: true });
10567  
10568  // Initial check
10569  setTimeout(updateFade, 100);
10570})();
10571
10572document.addEventListener('DOMContentLoaded', function () {
10573  var input = document.getElementById('searchInput');
10574  if (!input) return;
10575  input.addEventListener('keydown', function (e) {
10576    if (e.key === 'Enter') {
10577      e.preventDefault();
10578      var form = input.form;
10579      if (!form) return;
10580      if (typeof form.requestSubmit === 'function') form.requestSubmit();
10581      else form.submit();
10582    }
10583  });
10584});
10585
10586
10587(function(){
10588  const cartBtn = document.getElementById('cartBtn');
10589  if (cartBtn) {
10590    cartBtn.addEventListener('click', function(e) {
10591      e.preventDefault();
10592      
10593      const checkLogin = () => {
10594        return window.isUserLoggedIn ? window.isUserLoggedIn() : false;
10595      };
10596      
10597      if (checkLogin()) {
10598        window.location.href = '/keranjang';
10599      } else {
10600        showCartLoginModal();
10601      }
10602    });
10603  }
10604
10605  const loginBtn = document.getElementById('loginBtn');
10606  if (loginBtn) {
10607    loginBtn.addEventListener('click', function(e) {
10608      e.preventDefault();
10609      window.location.href = 'https://sunnypork.com/';
10610    });
10611  }
10612
10613  const userMenuBtn = document.getElementById('userMenuBtn');
10614  const userDropdown = document.getElementById('userDropdown');
10615  
10616  if (userMenuBtn && userDropdown) {
10617    function openDropdown() {
10618      userDropdown.hidden = false;
10619      userMenuBtn.setAttribute('aria-expanded', 'true');
10620      document.addEventListener('click', closeOnOutside, { capture: true });
10621      document.addEventListener('keydown', closeOnEscape);
10622    }
10623    
10624    function closeDropdown() {
10625      if (userDropdown.hidden) return;
10626      userDropdown.hidden = true;
10627      userMenuBtn.setAttribute('aria-expanded', 'false');
10628      document.removeEventListener('click', closeOnOutside, { capture: true });
10629      document.removeEventListener('keydown', closeOnEscape);
10630    }
10631    
10632    function closeOnOutside(e) {
10633      if (!userDropdown.contains(e.target) && e.target !== userMenuBtn) {
10634        closeDropdown();
10635      }
10636    }
10637    
10638    function closeOnEscape(e) {
10639      if (e.key === 'Escape') {
10640        closeDropdown();
10641        userMenuBtn.focus();
10642      }
10643    }
10644    
10645    userMenuBtn.addEventListener('click', function(e) {
10646      e.preventDefault();
10647      userDropdown.hidden ? openDropdown() : closeDropdown();
10648    });
10649    
10650    // Close dropdown when clicking profile links
10651    userDropdown.querySelectorAll('[data-close-dropdown]').forEach(link => {
10652      link.addEventListener('click', function() {
10653        closeDropdown();
10654      });
10655    });
10656  }
10657
10658  // Bottom nav profile button - toggle mobile bottom sheet
10659  const bottomProfileBtn = document.getElementById('bottomProfileBtn');
10660  const mobileUserSheet = document.getElementById('mobileUserSheet');
10661  const mobileUserSheetBackdrop = document.getElementById('mobileUserSheetBackdrop');
10662  
10663  if (bottomProfileBtn && mobileUserSheet) {
10664    function openMobileSheet() {
10665      mobileUserSheet.hidden = false;
10666      document.body.style.overflow = 'hidden';
10667      // Trigger animation
10668      requestAnimationFrame(() => {
10669        mobileUserSheet.classList.add('active');
10670      });
10671    }
10672    
10673    function closeMobileSheet() {
10674      mobileUserSheet.classList.remove('active');
10675      document.body.style.overflow = '';
10676      setTimeout(() => {
10677        mobileUserSheet.hidden = true;
10678      }, 300);
10679    }
10680    
10681    bottomProfileBtn.addEventListener('click', function(e) {
10682      e.preventDefault();
10683      openMobileSheet();
10684    });
10685    
10686    if (mobileUserSheetBackdrop) {
10687      mobileUserSheetBackdrop.addEventListener('click', closeMobileSheet);
10688    }
10689    
10690    // Close on escape key
10691    document.addEventListener('keydown', function(e) {
10692      if (e.key === 'Escape' && !mobileUserSheet.hidden) {
10693        closeMobileSheet();
10694      }
10695    });
10696    
10697    // Close sheet when clicking profile links
10698    mobileUserSheet.querySelectorAll('[data-close-sheet]').forEach(link => {
10699      link.addEventListener('click', function() {
10700        closeMobileSheet();
10701      });
10702    });
10703  }
10704  
10705  // Handle Google avatar error in bottom-nav (fallback to initials)
10706  const bottomNavAvatar = document.querySelector('.bottom-nav-avatar-img');
10707  if (bottomNavAvatar) {
10708    bottomNavAvatar.addEventListener('error', function() {
10709      const initials = this.getAttribute('data-fallback-initials') || 'U';
10710      const initialsDiv = document.createElement('div');
10711      initialsDiv.className = 'bottom-nav-avatar-initials';
10712      initialsDiv.textContent = initials;
10713      this.parentNode.replaceChild(initialsDiv, this);
10714    });
10715  }
10716
10717  // Mobile logout button
10718  const mobileLogoutBtn = document.getElementById('mobileLogoutBtn');
10719  if (mobileLogoutBtn) {
10720    mobileLogoutBtn.addEventListener('click', async function(e) {
10721      e.preventDefault();
10722      
10723      // Check push notification before logout
10724      if (window.ChatNotification && typeof window.ChatNotification.confirmLogoutWithPushNotification === 'function') {
10725        const confirmLogout = await window.ChatNotification.confirmLogoutWithPushNotification();
10726        if (!confirmLogout) {
10727          return; // User cancelled logout
10728        }
10729      }
10730      
10731      const getCsrfToken = () => document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
10732      
10733      try {
10734        const response = await fetch('/auth/logout', {
10735          method: 'POST',
10736          credentials: 'same-origin',
10737          headers: {
10738            'Content-Type': 'application/json',
10739            'X-CSRF-Token': getCsrfToken()
10740          },
10741          body: JSON.stringify({
10742            csrf_token: getCsrfToken()
10743          })
10744        });
10745        
10746        if (response.ok) {
10747          if (window.guestCart && typeof window.guestCart.clear === 'function') {
10748            window.guestCart.clear();
10749          }
10750          window.location.href = '/login?logged_out=1';
10751        } else {
10752          console.error('Logout failed');
10753          window.location.href = '/logout';
10754        }
10755      } catch (error) {
10756        console.error('Logout error:', error);
10757        window.location.href = '/logout';
10758      }
10759    });
10760  }
10761
10762  const logoutBtn = document.getElementById('logoutBtn');
10763  if (logoutBtn) {
10764    logoutBtn.addEventListener('click', async function(e) {
10765      e.preventDefault();
10766      
10767      // Check push notification before logout
10768      if (window.ChatNotification && typeof window.ChatNotification.confirmLogoutWithPushNotification === 'function') {
10769        const confirmLogout = await window.ChatNotification.confirmLogoutWithPushNotification();
10770        if (!confirmLogout) {
10771          return; // User cancelled logout
10772        }
10773      }
10774      
10775      const getCsrfToken = () => document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
10776      
10777      try {
10778        const response = await fetch('/auth/logout', {
10779          method: 'POST',
10780          credentials: 'same-origin',
10781          headers: {
10782            'Content-Type': 'application/json',
10783            'X-CSRF-Token': getCsrfToken()
10784          },
10785          body: JSON.stringify({
10786            csrf_token: getCsrfToken()
10787          })
10788        });
10789        
10790        if (response.ok) {
10791          if (window.guestCart && typeof window.guestCart.clear === 'function') {
10792            window.guestCart.clear();
10793          }
10794          
10795          window.location.href = '/login?logged_out=1';
10796        } else {
10797          console.error('Logout failed');
10798          window.location.href = '/logout';
10799        }
10800      } catch (error) {
10801        console.error('Logout error:', error);
10802        window.location.href = '/logout';
10803      }
10804    });
10805  }
10806
10807  const wishlistBtn = document.getElementById('wishlistBtn');
10808  const notifBtn = document.getElementById('notifBtn');
10809  
10810  if (wishlistBtn) {
10811    wishlistBtn.addEventListener('click', function(e) {
10812      e.preventDefault();
10813      alert('Fitur wishlist akan segera tersedia!');
10814    });
10815  }
10816  
10817  if (notifBtn) {
10818    notifBtn.addEventListener('click', function(e) {
10819      e.preventDefault();
10820      alert('Fitur notifikasi akan segera tersedia!');
10821    });
10822  }
10823})();
10824
10825// ========== MOBILE BOTTOM NAVIGATION ==========
10826(function initBottomNav() {
10827  // Sync cart badge with bottom nav
10828  function syncBottomCartBadge() {
10829    const mainBadge = document.getElementById('cartCount');
10830    const bottomBadge = document.getElementById('bottomCartCount');
10831    if (mainBadge && bottomBadge) {
10832      const count = mainBadge.textContent;
10833      bottomBadge.textContent = count;
10834      bottomBadge.style.display = parseInt(count) > 0 ? 'flex' : 'none';
10835    }
10836  }
10837
10838  // Sync compare badge with bottom nav
10839  function syncBottomCompareBadge() {
10840    const mainBadge = document.getElementById('compareCount');
10841    const bottomBadge = document.getElementById('bottomCompareCount');
10842    if (mainBadge && bottomBadge) {
10843      const count = mainBadge.textContent;
10844      bottomBadge.textContent = count;
10845      bottomBadge.style.display = parseInt(count) > 0 ? 'flex' : 'none';
10846    }
10847  }
10848
10849  // Initial sync
10850  syncBottomCartBadge();
10851  syncBottomCompareBadge();
10852
10853  // Observe badge changes
10854  const observer = new MutationObserver(function() {
10855    syncBottomCartBadge();
10856    syncBottomCompareBadge();
10857  });
10858
10859  const cartBadge = document.getElementById('cartCount');
10860  const compareBadge = document.getElementById('compareCount');
10861  
10862  if (cartBadge) observer.observe(cartBadge, { childList: true, characterData: true, subtree: true });
10863  if (compareBadge) observer.observe(compareBadge, { childList: true, characterData: true, subtree: true });
10864
10865  // Bottom Cart Button
10866  const bottomCartBtn = document.getElementById('bottomCartBtn');
10867  if (bottomCartBtn) {
10868    bottomCartBtn.addEventListener('click', function(e) {
10869      e.preventDefault();
10870      
10871      const checkLogin = () => {
10872        return window.isUserLoggedIn ? window.isUserLoggedIn() : false;
10873      };
10874      
10875      if (checkLogin()) {
10876        window.location.href = '/keranjang';
10877      } else {
10878        showCartLoginModal();
10879      }
10880    });
10881  }
10882
10883  // Bottom Compare Button
10884  const bottomCompareBtn = document.getElementById('bottomCompareBtn');
10885  if (bottomCompareBtn) {
10886    bottomCompareBtn.addEventListener('click', function(e) {
10887      e.preventDefault();
10888      
10889      // Close chat widget if open
10890      if (window.plazaChat && window.plazaChat.isWidgetOpen) {
10891        window.plazaChat.closeWidget();
10892      }
10893      
10894      const mainCompareBtn = document.getElementById('compareBtn');
10895      if (mainCompareBtn) {
10896        mainCompareBtn.click();
10897      }
10898    });
10899  }
10900
10901  // Bottom Category Button - triggers mega menu
10902  const bottomCategoryBtn = document.getElementById('bottomCategoryBtn');
10903  if (bottomCategoryBtn) {
10904    bottomCategoryBtn.addEventListener('click', function(e) {
10905      e.preventDefault();
10906      // Trigger desktop mega menu
10907      const desktopTrigger = document.querySelector('.mm2-trigger:not(#bottomCategoryBtn)');
10908      if (desktopTrigger) {
10909        desktopTrigger.click();
10910      }
10911    });
10912  }
10913
10914  // Bottom Login Button
10915  const bottomLoginBtn = document.getElementById('bottomLoginBtn');
10916  if (bottomLoginBtn) {
10917    bottomLoginBtn.addEventListener('click', function(e) {
10918      e.preventDefault();
10919      window.location.href = '/login';
10920    });
10921  }
10922
10923  // Bottom Chat Button
10924  const bottomChatBtn = document.getElementById('bottomChatBtn');
10925  if (bottomChatBtn) {
10926    bottomChatBtn.addEventListener('click', function(e) {
10927      e.preventDefault();
10928      
10929      // Trigger chat widget (try multiple times if needed)
10930      const triggerChat = () => {
10931        const chatToggle = document.getElementById('chat-toggle-btn');
10932        if (chatToggle) {
10933          chatToggle.click();
10934          return true;
10935        }
10936        return false;
10937      };
10938      
10939      // Immediate attempt
10940      if (!triggerChat()) {
10941        // Retry after 100ms if not found
10942        setTimeout(() => {
10943          if (!triggerChat()) {
10944            // Last resort: manually show popup
10945            const popup = document.getElementById('chat-popup');
10946            if (popup) {
10947              popup.style.display = 'block';
10948            }
10949          }
10950        }, 100);
10951      }
10952    });
10953  }
10954
10955  // Sync chat unread badge
10956  function syncBottomChatBadge() {
10957    const chatBadge = document.getElementById('chat-unread-badge');
10958    const bottomChatBadge = document.getElementById('bottomChatCount');
10959    if (chatBadge && bottomChatBadge) {
10960      const count = chatBadge.textContent;
10961      bottomChatBadge.textContent = count;
10962      bottomChatBadge.style.display = chatBadge.style.display;
10963    }
10964  }
10965
10966  // Observe chat badge changes
10967  const chatBadge = document.getElementById('chat-unread-badge');
10968  if (chatBadge) {
10969    observer.observe(chatBadge, { attributes: true, childList: true, characterData: true, subtree: true });
10970    syncBottomChatBadge();
10971  }
10972
10973  // Active state management based on current page
10974  function updateActiveState() {
10975    const currentPath = window.location.pathname;
10976    const bottomNavItems = document.querySelectorAll('.bottom-nav-item');
10977    
10978    bottomNavItems.forEach(item => {
10979      item.classList.remove('active');
10980      
10981      const itemId = item.id;
10982      const page = item.getAttribute('data-page');
10983      const href = item.getAttribute('href');
10984      
10985      // Check by ID for specific pages
10986      if (itemId === 'bottomHomeBtn' && currentPath === '/') {
10987        item.classList.add('active');
10988      } else if (itemId === 'bottomCartBtn' && (currentPath === '/keranjang' || currentPath === '/cart')) {
10989        item.classList.add('active');
10990      } else if (itemId === 'bottomProfileBtn' && currentPath.startsWith('/profile')) {
10991        item.classList.add('active');
10992      } else if (itemId === 'bottomCategoryBtn' && currentPath.startsWith('/kategori')) {
10993        item.classList.add('active');
10994      } else if (itemId === 'bottomCompareBtn' && currentPath === '/compare') {
10995        item.classList.add('active');
10996      } else if (itemId === 'bottomChatBtn' && currentPath === '/chat') {
10997        item.classList.add('active');
10998      }
10999      // Fallback: check by data-page attribute
11000      else if (page === 'home' && currentPath === '/') {
11001        item.classList.add('active');
11002      } else if (page === 'profile' && currentPath.startsWith('/profile')) {
11003        item.classList.add('active');
11004      }
11005      // Fallback: check by href
11006      else if (href && currentPath === href) {
11007        item.classList.add('active');
11008      }
11009    });
11010  }
11011
11012  // Update active state on load
11013  updateActiveState();
11014
11015  // Bottom nav always visible - auto-hide disabled for better UX
11016  // const bottomNav = document.querySelector('.bottom-nav');
11017  // if (bottomNav && window.innerWidth <= 640) {
11018  //   // Auto-hide feature disabled
11019  // }
11020})();
11021// Cart Login Modal Handler (Global scope)
11022function showCartLoginModal() {
11023  const modal = document.getElementById('cartLoginModal');
11024  if (!modal) return;
11025  
11026  modal.classList.add('show');
11027  
11028  const confirmBtn = document.getElementById('cartLoginConfirm');
11029  const registerBtn = document.getElementById('cartRegisterBtn');
11030  const cancelBtn = document.getElementById('cartLoginCancel');
11031  
11032  const handleLogin = () => {
11033    window.location.href = '/login?return=' + encodeURIComponent('/keranjang');
11034  };
11035  
11036  const handleRegister = () => {
11037    window.location.href = '/register?return=' + encodeURIComponent('/keranjang');
11038  };
11039  
11040  const handleCancel = () => {
11041    modal.classList.remove('show');
11042    confirmBtn.removeEventListener('click', handleLogin);
11043    registerBtn.removeEventListener('click', handleRegister);
11044    cancelBtn.removeEventListener('click', handleCancel);
11045  };
11046  
11047  confirmBtn.removeEventListener('click', handleLogin);
11048  registerBtn.removeEventListener('click', handleRegister);
11049  cancelBtn.removeEventListener('click', handleCancel);
11050  
11051  confirmBtn.addEventListener('click', handleLogin);
11052  registerBtn.addEventListener('click', handleRegister);
11053  cancelBtn.addEventListener('click', handleCancel);
11054  
11055  modal.addEventListener('click', (e) => {
11056    if (e.target === modal) {
11057      handleCancel();
11058    }
11059  });
11060}
11061</script>
vendor: 2 bytes, line 11061
11061
11062<script data-inlined="cart-badge.js">
11063(function(){
11064  function selBadge(){
11065    return document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
11066  }
11067  function setBadge(n){
11068    const el = selBadge();
11069    if (!el) return;
11070    const count = Math.max(0, parseInt(n||0,10) || 0);
11071    el.textContent = String(count);
11072    // Sembunyikan jika 0? Jika ingin selalu terlihat, hapus 2 baris di bawah
11073    if (count <= 0) { el.classList.add('is-empty'); }
11074    else { el.classList.remove('is-empty'); }
11075  }
11076  async function refresh(){
11077    try{
11078      // Primary: Use server-side endpoint (handles both logged and guest users)
11079      const res = await fetch('/api/cart/get-simple.php', { credentials:'same-origin' });
11080      if (res.ok) {
11081        const data = await res.json();
11082        if (data && data.ok) {
11083          setBadge(data.items_count || 0);
11084          return;
11085        }
11086      }
11087      
11088      // Fallback hanya untuk guest user: check localStorage
11089      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
11090                        document.body.classList.contains('logged-in') ||
11091                        window.userLoggedIn === true;
11092      
11093      if (!isLoggedIn && window.guestCart && typeof window.guestCart.getItemsCount === 'function') {
11094        setBadge(window.guestCart.getItemsCount());
11095      }
11096    }catch(e){ 
11097      // Final fallback hanya untuk guest
11098      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true';
11099      if (!isLoggedIn) {
11100        try {
11101          const guestCartData = JSON.parse(localStorage.getItem('plazait_guest_cart') || '{"items":{}}');
11102          const items = guestCartData.items || {};
11103          const count = Object.values(items).reduce((total, item) => total + (item.qty || 0), 0);
11104          setBadge(count);
11105        } catch(e2) {
11106          setBadge(0);
11107        }
11108      } else {
11109        setBadge(0);
11110      }
11111    }
11112  }
11113
11114  // Ekspos untuk dipanggil dari skrip lain (opsional)
11115  window.CartBadge = { set: setBadge, refresh };
11116  window.updateCartCounter = refresh; // Alias for compatibility
11117
11118  // Update saat halaman load
11119  if (document.readyState === 'loading') {
11120    document.addEventListener('DOMContentLoaded', refresh);
11121  } else {
11122    refresh();
11123  }
11124
11125  // Dengarkan event global dari add/remove/update
11126  document.addEventListener('cart:updated', function(ev){
11127    const c = ev && ev.detail && typeof ev.detail.items_count === 'number'
11128      ? ev.detail.items_count : null;
11129    if (c !== null) setBadge(c);
11130    else refresh();
11131  });
11132
11133  // Dengarkan event khusus cart remove untuk update badge
11134  document.addEventListener('cart:removed', function(ev){
11135    // Langsung refresh untuk memastikan data terbaru
11136    refresh();
11137  });
11138
11139  // Jika addToCart sudah ada sebelum file ini, coba wrap agar badge ter-update.
11140  const prevAdd = window.addToCart;
11141  if (typeof prevAdd === 'function'){
11142    window.addToCart = async function(pid, qty, vid, meta){
11143      const res = await prevAdd(pid, qty, vid, meta);
11144      try{
11145        const cnt = res && res.cart ? res.cart.items_count : null;
11146        if (cnt !== null) setBadge(cnt);
11147        // tetap siarkan event agar listener lain ikut sync
11148        document.dispatchEvent(new CustomEvent('cart:updated', { detail: res.cart || {} }));
11149      }catch(_){}
11150      return res;
11151    };
11152  }
11153
11154  // Helper function to check if user is logged in
11155  function isUserLoggedIn() {
11156    const metaTag = document.querySelector('meta[name="user-logged-in"]');
11157    return metaTag ? metaTag.getAttribute('content') === 'true' : false;
11158  }
11159
11160  // Make login check globally accessible
11161  window.isUserLoggedIn = isUserLoggedIn;
11162})();
11163</script>
11163
11164
11165<!-- Product Compare Sidebar -->
11166<div class="compare-sidebar" id="compareSidebar">
11167  <div class="compare-header">
11168    <h4 class="compare-title">
11169      <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
11170        <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"/>
11171      </svg>
11172      Bandingkan Produk
11173      <span class="compare-count" id="compareCountSidebar">(0)</span>
11174    </h4>
11175    <button class="compare-close" id="compareClose" aria-label="Tutup">
11176      <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
11177        <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"/>
11178      </svg>
11179    </button>
11180  </div>
11181  
11182  <div class="compare-content">
11183    <div class="compare-empty" id="compareEmpty">
11184      <div class="empty-icon">
11185        <svg width="48" height="48" viewBox="0 0 24 24" fill="currentColor" opacity="0.3">
11186          <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"/>
11187        </svg>
11188      </div>
11189      <h5>Belum ada produk dipilih</h5>
11190      <p>Pilih produk yang ingin dibandingkan dengan klik tombol "Bandingkan Produk" di halaman produk.</p>
11191    </div>
11192    
11193    <div class="compare-list" id="compareList">
11194      <!-- Products will be populated here -->
11195    </div>
11196  </div>
11197  
11198  <div class="compare-actions" id="compareActions">
11199    <button class="btn btn-outline-danger btn-sm" id="compareClearAll">
11200      <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
11201        <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"/>
11202      </svg>
11203      Hapus Semua
11204    </button>
11205    <button class="btn btn-primary btn-sm" id="compareAnalyze" disabled>
11206      <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
11207        <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"/>
11208      </svg>
11209      Analisis AI
11210    </button>
11211  </div>
11212  
11213  <!-- AI Disclaimer in Sidebar -->
11214  <div class="compare-disclaimer">
11215    <div class="ai-disclaimer">
11216      <div class="disclaimer-text">
11217        <strong>💡 Catatan:</strong> Analisis ini dihasilkan secara otomatis dan dapat memiliki keterbatasan.
11218        Gunakan sebagai referensi tambahan dengan tetap mempertimbangkan kebutuhan Anda.
11219        Untuk informasi lebih lanjut, silakan hubungi tim NAGARP.
11220      </div>
11221    </div>
11222  </div>
11223</div><style data-inlined="pd-lightbox.css">
11224/* PlazaIT Product Lightbox (vanilla, no CDN) — v4 */
11225.pd-lb {
11226  position: fixed; inset: 0; z-index: 99999;
11227  background: rgba(0,0,0,.95);
11228  display: none;
11229  color: #fff;
11230  /* Transition untuk swipe-down feedback */
11231  transition: transform .18s ease, opacity .18s ease;
11232}
11233.pd-lb.show { display: block; }
11234
11235/* Canvas di bawah (z:1), Controls di atas (z:2) */
11236.pd-lb-canvas {
11237  position: absolute; inset: 56px 0 56px 0;
11238  display: flex; align-items: center; justify-content: center;
11239  overflow: hidden;
11240  touch-action: none;
11241  z-index: 1;
11242}
11243
11244.pd-lb-img {
11245  max-width: 96vw; max-height: 86vh;
11246  will-change: transform;
11247  user-select: none;
11248  pointer-events: none;
11249  transform-origin: 50% 50%;
11250}
11251
11252.pd-lb-close, .pd-lb-prev, .pd-lb-next, .pd-lb-counter {
11253  position: absolute;
11254  z-index: 2;
11255}
11256
11257.pd-lb-close, .pd-lb-prev, .pd-lb-next {
11258  background: rgba(17,24,39,.7);
11259  color: #fff; border: 0; cursor: pointer;
11260  width: 40px; height: 40px; border-radius: 999px;
11261  display: inline-flex; align-items: center; justify-content: center;
11262  transition: background-color .15s ease, transform .05s ease;
11263  -webkit-tap-highlight-color: transparent;
11264}
11265.pd-lb-close:hover, .pd-lb-prev:hover, .pd-lb-next:hover { background: rgba(31,41,55,.85); }
11266.pd-lb-close:active, .pd-lb-prev:active, .pd-lb-next:active { transform: translateY(.5px); }
11267
11268.pd-lb-close { top: max(12px, env(safe-area-inset-top, 12px)); right: max(12px, env(safe-area-inset-right, 12px)); }
11269.pd-lb-prev  { top: 50%; left:  10px; transform: translateY(-50%); }
11270.pd-lb-next  { top: 50%; right: 10px; transform: translateY(-50%); }
11271
11272.pd-lb-counter {
11273  top: max(16px, env(safe-area-inset-top, 16px)); left: 50%;
11274  transform: translateX(-50%);
11275  font-weight: 700; font-size: 13px; letter-spacing: .4px;
11276  background: rgba(17,24,39,.65);
11277  padding: 6px 10px; border-radius: 999px; border: 1px solid rgba(255,255,255,.1);
11278}
11279
11280@media (max-width: 640px){
11281  .pd-lb-img { max-width: 98vw; max-height: 80vh; }
11282  .pd-lb-prev, .pd-lb-next { width: 36px; height: 36px; }
11283  .pd-lb-close { width: 38px; height: 38px; }
11284}
11285</style>
11286<style>.paylater-promo{display:inline-flex;align-items:center;gap:8px;background:linear-gradient(135deg,#f0fdf4 0,#dcfce7 100%);border:1px solid #86efac;border-radius:8px;padding:8px 12px;font-size:13px;line-height:1.4;margin:8px 0}.paylater-promo-sm{padding:6px 10px;font-size:11px;gap:6px}.paylater-promo-md{padding:8px 12px;font-size:13px;gap:8px}.paylater-promo-lg{padding:10px 16px;font-size:14px;gap:10px}.paylater-icon{font-size:1.2em;flex-shrink:0}.paylater-text{display:flex;flex-direction:column;gap:2px}.paylater-text strong{color:#15803d;font-weight:600}.paylater-text small{color:#16a34a;font-size:.85em;opacity:.9}.paylater-desktop-only{display:none}.paylater-mobile-only{display:flex}@media (min-width:641px){.paylater-desktop-only{display:flex}.paylater-mobile-only{display:none}}@media (max-width:640px){.paylater-promo{font-size:12px;padding:7px 10px}.paylater-promo-lg{font-size:13px;padding:8px 12px}}@media print{.paylater-promo{display:none!important}}.paylater-compact{display:inline-block;font-size:11px;font-weight:600;color:#059669;background:#d1fae5;padding:3px 8px;border-radius:4px;margin:6px 0 4px;line-height:1.3}@media (max-width:640px){.paylater-compact{font-size:10px;padding:2px 6px;margin:5px 0 3px}}@media print{.paylater-compact{display:none!important}}</style><style nonce="se3iyzEHMc9FQEB4wSyd0w==">.pd-voucher-minimal{margin:10px 0;display:flex;flex-direction:column;gap:6px}.pdv-min-item{display:flex;align-items:center;gap:8px;padding:0;height:32px;transition:all .2s ease}.pdv-min-hidden{display:none}.pdv-min-disabled{opacity:.5}.pdv-badge-btn{display:inline-flex;align-items:center;gap:8px;background:linear-gradient(135deg,#667eea 0,#764ba2 100%);border:none;border-radius:8px;padding:3px;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:#667eea;cursor:pointer;transition:all .2s ease;white-space:nowrap}.pdv-min-copy:hover:not(:disabled),.pdv-min-more:hover{background:#667eea;color:#fff;border-color:#667eea}.pdv-min-copy:disabled{opacity:.4;cursor:not-allowed}.pdv-min-copy.copied{background:#22c55e;color:#fff;border-color:#22c55e}.pdv-min-more{margin-left:auto}.modal-close{position:absolute;top:20px;right:20px;background:none;border:none;cursor:pointer;color:#64748b;padding:4px;transition:color .2s ease}.modal-close:hover{color:#1e293b}.modal-body{margin-bottom:8px}.voucher-modal-content{text-align:center}.voucher-modal-badge{display:inline-flex;flex-direction:row;align-items:center;gap:6px;background:linear-gradient(135deg,#667eea 0,#764ba2 100%);border-radius:10px;padding:8px 16px;margin-bottom:12px}.vmb-value{font-size:18px;font-weight:700;color:#fff;line-height:1}.vmb-label{font-size:18px;font-weight:700;color:#fff;text-transform:uppercase;letter-spacing:.5px}.voucher-modal-code{font-family:'Courier New',monospace;font-size:14px;font-weight:700;color:#667eea;background:rgba(102,126,234,.1);padding:6px 12px;border-radius:6px;border:2px dashed rgba(102,126,234,.3);display:inline-block;margin-bottom:10px}.voucher-modal-name{font-size:14px;font-weight:600;color:#1e293b;margin-bottom:16px}.voucher-modal-details{text-align:left;background:#f8fafc;border-radius:8px;padding:12px;margin-bottom:16px}.vmd-item{display:flex;align-items:flex-start;gap:8px;padding:6px 0;font-size:13px;color:#64748b;border-bottom:1px solid #e2e8f0}.vmd-item:last-child{border-bottom:none}.vmd-item svg{color:#667eea;flex-shrink:0;margin-top:2px}.vmd-item strong{color:#1e293b}.modal-actions{padding:20px}.modal-btn-primary{background:linear-gradient(135deg,#667eea 0,#764ba2 100%);color:#fff}.modal-btn-primary:hover{opacity:.9;transform:translateY(-1px)}@media (max-width:640px){.pdv-min-item{gap:6px}.pdv-badge-btn{padding:3px;gap:6px}.pdv-badge-text{font-size:13px}.pdv-badge-code{font-size:12px;padding:3px 6px}.pdv-min-copy,.pdv-min-more{padding:3px 10px;font-size:11px}.modal-dialog{max-width:95%;margin:20px}.vmb-value{font-size:16px}.vmb-label{font-size:16px}.voucher-modal-code{font-size:13px;padding:5px 10px}}</style><style nonce="se3iyzEHMc9FQEB4wSyd0w==">.promo-endyear-mini-banner{display:flex;align-items:center;gap:12px;padding:12px 16px;background:rgba(255,255,255,.8);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:2px solid rgba(245,202,86,.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(245,202,86,.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:#C10DFF;margin-bottom:2px}
11286.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,#C10DFF,#C10DFF);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(245,202,86,.25)}.pey-link:hover{transform:translateX(2px);box-shadow:0 4px 12px rgba(245,202,86,.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>
11287<style nonce="se3iyzEHMc9FQEB4wSyd0w==">
11288.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}
11289
11290.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)}}
11291
11292.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}
11293
11294.pd-share-preview{display:flex;gap:12px;padding:16px;background:#f8fafc;border-radius:12px;margin-bottom:24px}.pd-share-preview-img{width:64px;height:64px;border-radius:8px;object-fit:cover;flex-shrink:0;border:1px solid #e2e8f0}.pd-share-preview-info{flex:1;min-width:0}.pd-share-preview-name{font-size:14px;font-weight:600;color:#0f172a;margin:0 0 4px 0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.pd-share-preview-price{font-size:16px;font-weight:700;color:#0ea5e9;margin:0}
11295
11296.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)}
11297
11298.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,#e2a642 0%,#c9832a 100%);color:#fff}.pd-share-icon-copy{background:linear-gradient(135deg,#C10DFF 0%,#C10DFF 100%);color:#fff}.pd-share-icon-pdf{background:linear-gradient(135deg,#e2a642 0%,#e2a642 100%);color:#fff}.pd-share-name{font-size:12px;font-weight:600;color:#475569;text-align:center}
11299
11300.pd-share-copy-section{background:#f8fafc;padding:16px;border-radius:12px}.pd-share-copy-input-wrap{display:flex;gap:8px}.pd-share-copy-input{flex:1;padding:12px 14px;border:1px solid #e2e8f0;border-radius:10px;font-size:14px;color:#0f172a;background:#fff;font-family:'Segoe UI',system-ui,sans-serif}.pd-share-copy-input:focus{outline:0;border-color:#0ea5e9;box-shadow:0 0 0 3px rgba(14,165,233,.1)}.pd-share-copy-btn{padding:12px 20px;border:0;border-radius:10px;background:linear-gradient(135deg,#0ea5e9 0%,#0284c7 100%);color:#fff;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s ease;white-space:nowrap;display:inline-flex;align-items:center;gap:6px}.pd-share-copy-btn:hover{background:linear-gradient(135deg,#0284c7 0%,#0369a1 100%);transform:translateY(-1px);box-shadow:0 4px 12px rgba(14,165,233,.3)}
11300.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}
11301
11302@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}}
11303
11304.mf-share{background:#fff;color:#C10DFF;border:2px solid #C10DFF;display:inline-flex;align-items:center;justify-content:center}.mf-share:active{transform:scale(.95)}.mf-share svg{display:block}
11305
11306@media(max-width:640px){.pd-share-btn{display:none!important}}
11307
11308.stock-badge.in{background:#e6f9ec;color:#15803d;border-radius:999px;padding:4px 10px;font-weight:700}.stock-badge.out{background:#e2a642;color:#14b1d7;border-radius:999px;padding:4px 10px;font-weight:700}
11309
11310.stock-info-btn{--c:#0ea5e9;--b:#bae6fd;--bg:#f0f9ff;--bg-h:#e0f2fe;--b-h:#0ea5e9;margin-left:0;display:inline-flex;align-items:center;justify-content:center;gap:0.5rem;padding:4px 10px;border-radius:999px;border:1.5px solid var(--b);background:var(--bg);color:var(--c);cursor:pointer;font-size:12px;font-weight:600;transition:all .2s ease;-webkit-tap-highlight-color:transparent}.stock-info-btn:hover{background:var(--bg-h);border-color:var(--b-h);color:#0284c7;transform:translateY(-1px);box-shadow:0 2px 8px rgba(14,165,233,.15)}.stock-info-btn:active{transform:translateY(0)}.stock-info-btn:focus-visible{outline:0;box-shadow:0 0 0 3px rgba(14,165,233,.2)}.stock-info-btn svg{width:14px;height:14px;flex-shrink:0}.stock-info-btn .btn-text{white-space:nowrap}
11311
11312.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}
11313
11314.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,#60a5fa);display:inline-block}.sp-label{font-weight:600;font-size:13px}
11315
11316
11317.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}
11318
11319.sp-wa{
11320  grid-column:3; justify-self:end;
11321  width:32px; height:32px; min-width:32px; min-height:32px;
11322  box-sizing:border-box;
11323  padding:0; line-height:0; text-decoration:none; outline:0;
11324  display:inline-flex; align-items:center; justify-content:center;
11325  border-radius:999px; border:1px solid rgba(37,211,102,.60);  /* 1px untuk hasil “crisp” */
11326  background:rgba(37,211,102,.08); color:#25D366;
11327  transition:background-color .15s ease, border-color .15s ease, transform .05s ease;
11328  -webkit-tap-highlight-color:transparent;
11329}
11330.sp-wa:hover{background:rgba(37,211,102,.16);border-color:rgba(37,211,102,.75)}
11331.sp-wa:active{transform:translateY(.5px) scale(.98)}
11332
11333.sp-wa svg{width:18px;height:18px;display:block;vertical-align:middle}
11334.stock-popover a{text-decoration:none}
11335
11336#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}}
11337
11338.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:#C10DFF}.stock-dot.out{background:#e2a642}.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}}
11339
11340.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}}
11341
11342.pwp-trigger-badge{display:inline-flex;align-items:center;gap:4px;padding:3px 8px 4px;background:linear-gradient(135deg,#C10DFF 0%,#C10DFF 100%);color:#765212;border-radius:6px;font-size:.65rem;font-weight:600;border:0}.pwp-trigger-badge svg{flex-shrink:0;width:12px;height:12px}
11343.pwp-deals-section{margin:0;padding:12px;background:linear-gradient(135deg,#fef3c7 0%,#C10DFF 100%);border-radius:12px;border:2px solid #C10DFF}.pwp-deals-header{display:flex;gap:10px;margin-bottom:10px}.pwp-icon{flex-shrink:0;color:#C10DFF;width:18px;height:18px}.pwp-text .pwp-title{margin:0 0 2px 0;font-size:14px;font-weight:700;color:#765212}
11343.pwp-text .pwp-subtitle{margin:0;font-size:11px;color:#ae7e21;opacity:0.9}.pwp-deals-loading{text-align:center;padding:40px 0;color:#ae7e21}.pwp-spinner{width:40px;height:40px;border:3px solid #C10DFF;border-top-color:#C10DFF;border-radius:50%;animation:spin 0.8s linear infinite;margin:0 auto 12px}@keyframes spin{to{transform:rotate(360deg)}}
11344.pwp-deals-grid{display:flex;flex-direction:column;gap:6px}.pwp-deal-card{background:#fff;border-radius:6px;overflow:hidden;border:1px solid #C10DFF;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:#e2a642;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:#e2a642;white-space:nowrap}.pwp-deal-original{font-size:11px;color:#64748b;text-decoration:line-through;white-space:nowrap}.pwp-cart-note{margin-top:8px;padding:6px 10px;background:#e0f2fe;border-left:2px solid#0ea5e9;font-size:11px;color:#0c4a6e;border-radius:4px}
11345@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}}
11346
11347.preorder-badge{display:inline-flex;align-items:center;gap:5px;padding:6px 12px;background:linear-gradient(135deg,#8b5cf6 0%,#7c3aed 100%);color:#fff;border-radius:8px;font-size:13px;font-weight:700;border:0;box-shadow:0 2px 8px rgba(139,92,246,.25)}.preorder-badge svg{width:16px;height:16px;flex-shrink:0}.preorder-info{margin:12px 0;padding:14px;background:linear-gradient(135deg,#f5f3ff 0%,#ede9fe 100%);border:1.5px solid #c4b5fd;border-radius:12px}.preorder-info-header{display:flex;align-items:center;gap:8px;margin-bottom:10px}.preorder-info-icon{width:20px;height:20px;color:#7c3aed;flex-shrink:0}.preorder-info-title{font-size:14px;font-weight:700;color:#5b21b6;margin:0}.preorder-info-items{display:flex;flex-direction:column;gap:8px}.preorder-info-row{display:flex;align-items:center;justify-content:space-between;font-size:13px}.preorder-info-label{color:#6b21a8;font-weight:600}.preorder-info-value{color:#5b21b6;font-weight:700}.preorder-info-value.highlight{color:#e2a642;font-size:14px}.preorder-info-note{margin-top:10px;padding:8px 10px;background:#fef3c7;border-left:2px solid #C10DFF;font-size:12px;color:#765212;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}}
11348</style>
11349
11350<div class="pd-sticky-header" id="pd-sticky-header"><div class="pd-sticky-header-container"><div class="pd-sticky-thumb"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP" id="pd-sticky-thumb-img"></div><div class="pd-sticky-info"><h2 class="pd-sticky-title" id="pd-sticky-title">NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif</h2><div class="pd-sticky-price-row"><span class="pd-sticky-price" id="pd-sticky-price">Rp 20.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">
11350<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>
11351
11352<main class="product-single product-single--v3"><nav class="pd-breadcrumb" aria-label="breadcrumb"><a href="https://sunnypork.com/">NAGARP</a><span>-</span><a href="https://sunnypork.com/">NAGARP ONLINE</a><span>-</span><a href="https://sunnypork.com/">NAGARP SITUS</a><span>-</span><a href="https://sunnypork.com/">NAGARP SLOT</a><span>-</span><a href="https://sunnypork.com/">NAGARP SLOT88</a><span>-</span><span class="current">NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif
11353
11354</span></nav>
11355
11356<div class="pd-top-layout"><section class="pd-media-card"><figure class="pd-main-image" id="pd-main-figure"><img id="pd-main-img" src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="Teknologi Digital NAGARP" data-slug="" loading="eager">
11357</figure>
11358
11359<div class="pd-mobile-slider-wrapper">
11360<div class="pd-mobile-slider" id="pd-mobile-slider" aria-label="Galeri gambar (geser)"><div class="pd-mslide"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE" loading="eager"></div><div class="pd-mslide"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE" loading="lazy"></div><div class="pd-mslide"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE" loading="lazy"></div><div class="pd-mslide"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE" loading="lazy"></div><div class="pd-mslide"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE" loading="lazy"></div><div class="pd-mslide"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE" loading="lazy"></div></div><div class="pd-gallery-counter" id="pd-gallery-counter">1/6</div></div>
11361<div class="pd-thumb-gallery" id="pd-thumb-gallery" aria-label="Galeri gambar produk"><button type="button" class="pd-thumb-btn active" data-full="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" data-alt="NAGARP ONLINE" data-slug="" aria-label="Gambar 1"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" data-alt
11361="NAGARP ONLINE" data-slug="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" aria-label="Gambar 2"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" data-alt="NAGARP ONLINE" data-slug="lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-223.webp" aria-label="Gambar 3"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" data-alt="NAGARP ONLINE" data-slug="lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-776.webp" aria-label="Gambar 4"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" data-alt="NAGARP ONLINE" data-slug="lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-634.webp" aria-label="Gambar 5"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" data-alt="NAGARP ONLINE" data-slug="lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-892.webp" aria-label="Gambar 6"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE" loading="lazy"></button></div></section><section class="pd-info-card"><div id="pd-variant-combined" class="pd-variant-combined" hidden></div><div class="pd-title-row">
11362        <h1 class="pd-title" id="pd-title">NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif</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>
11363<div id="pd-price-anchor">
11364</div>
11365<div class="pd-price-qr-container">
11366        <div class="pd-price-block" id="pd-price-block">
11367                <div class="pd-price-row">
11368                        <span class="pd-price-current" id="pd-price-current" >Rp 20.000</span>
11369                        <span class="pd-price-original" id="pd-price-original">Rp 50.000</span>
11370                        <span class="pd-discount-badge" id="pd-discount-badge">-90%</span></div>
11371                        <div class="pd-meta-row">
11372                                <span class="sold-inline" id="pd-sold-inline" data-role="sold-chip">Terjual Terjual 5</span>
11373                                <span class="pwp-trigger-badge" id="pwp-trigger-badge" hidden>
11374                                        <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
11375                                                <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
11375-.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>
11376                                                <div class="pd-meta-inline"><span class="stock-badge out" id="pd-stock-badge">NAGARP</span>
11377                                                        <span class="sku">SKU: <span id="pd-sku">NAGARP SITUS</span></span></div>
11378<div class="paylater-promo paylater-promo-sm paylater-mobile-only">
11379    <span class="paylater-icon">💳</span>
11380    <span class="paylater-text">
11381        <strong>NAGARP</strong>
11382        <small>ONLINE</small>
11383    </span>
11384</div>
11385
11386<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">
11387<li class="sp-item"><div class="sp-left"><span class="sp-dot" style="--dot:#60a5fa;"></span><span class="sp-label">Asia Mega Mas</span></div><span class="sp-qty" id="sp-qty-meg">0</span><a class="sp-wa" href="https://wa.me/6281380009898?text=Halo%2C%20saya%20ingin%20tanya%20stok%20di%20Asia%20Mega%20Mas%20untuk%20produk%20https%3A%2F%2Fplazait.co.id%2Fproduct%2Flenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-black" target="_blank" rel="noopener" aria-label="WhatsApp Asia Mega Mas"><svg class="ico-wa" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path fill="currentColor" d="M17.472 14.382c-.297-.149-1.758-.867-2.029-.967-.273-.099-.471-.148-.669.149-.198.297-.767.966-.94 1.164-.173.198-.347.223-.644.074-.297-.149-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.297-.347.446-.52.149-.173.198-.297.298-.495.099-.198.05-.372-.025-.521-.075-.149-.669-1.612-.916-2.206-.242-.579-.487-.5-.669-.51l-.57-.01c-.198 0-.52.074-.792.372s-1.04 1.016-1.04 2.479 1.065 2.876 1.213 3.074c.149.198 2.095 3.2 5.076 4.487.709.306 1.262.489 1.694.626.712.226 1.36.194 1.872.118.571-.085 1.758-.718 2.006-1.411.248-.694.248-1.29.173-1.411-.074-.123-.272-.198-.57-.347m-5.421 7.403h-.002a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.263c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.887 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.158 11.892c0 2.096.547 4.142 1.588 5.945L0 24l6.305-1.654a11.86 11.86 0 005.67 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.485-8.413"/></svg></a></li>
11388                
11389                    <li class="sp-item">
11390                      <div class="sp-left">
11391                        <span class="sp-dot" style="--dot:#34d399;"></span>
11392                        <span class="sp-label">Medan Fair</span>
11393                      </div>
11394                      <span class="sp-qty" id="sp-qty-mdf">0</span>
11395                      <a class="sp-wa"
11396                         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"
11397                         target="_blank" rel="noopener" aria-label="WhatsApp Medan Fair">
11398                        <svg class="ico-wa" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
11399                            <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"/>
11400                          </svg>
11401                      </a>
11402                    </li>
11403                
11404                    <li class="sp-item">
11405                      <div class="sp-left">
11406                        <span class="sp-dot" style="--dot:#C10DFF;"></span>
11407                        <span class="sp-label">Banda Aceh</span>
11408                      </div>
11409                      <span class="sp-qty" id="sp-qty-bda">0</span>
11410                      <a class="sp-wa"
11411                         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"
11412                         target="_blank" rel="noopener" aria-label="WhatsApp Banda Aceh">
11413                        <svg class="ico-wa" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
11414                            <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"/>
11415                          </svg>
11416                      </a>
11417                    </li>
11418                  </ul>
11419                </div>
11420
11421                            </div>
11422            <div class="pd-qr-print" style="display:none;"><div class="pd-qr-label">Scan untuk lihat produk:</div><img src="https://api.qrserver.com/v1/create-qr-code/?size=120x120&data=https%3A%2F%2Fplazait.co.id%2Fproduct%2Flenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-black" alt="QR Code" class="pd-qr-image"></div>
11423            </div>
11424                                    <div id="pd-variants-anchor"></div>
11425
11426                        
11427
11428            <div class="pd-compare-section">
11429                <button type="button" class="pd-compare-btn" id="pd-compare-btn" data-product-id="325">
11430                    <svg class="compare-icon" width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
11431                        <path d="M21,9L17,5V8H10V10H17V13M7,11L3,15L7,19V16H14V14H7V11Z"/>
11432                    </svg>
11433                    <span class="compare-text">Bandingkan Produk</span>
11434                    <span class="compare-remove" hidden>
11435                        <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
11436                            <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"/>
11437                        </svg>
11438                        Hapus dari Perbandingan
11439                    </span>
11440                </button>
11441                <div class="pd-compare-info">
11442                    <small class="compare-note" id="compare-note">Bandingkan hingga 5 produk dengan bantuan AI</small>
11443                </div>
11444            </div>
11445            
11446            <div class="pd-short-desc"><p><strong>Informasi NAGARP:</strong></p><ul><li>Minimal Deposit: Rp.10.000,-</li><li>Jenis Permainan: NAGARP Pengalaman Platform Interaktif</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: 399.999.999</li><li>Rating: ⭐⭐⭐⭐⭐</li><li>Daftar Buruan: <a href="https://sunny-401.sunnypork.com/">Klik Disini</a></li></ul></div><div class="Perubahan-Teknologi-Digital-v1 mini">
11447<div class="Perubahan-Teknologi-Digital">
11448
11449  <!-- HEADER -->
11450<div class="Perubahan-Teknologi-Digital-v3 mini">
11451                    <div class="Pengalaman-Platform-Interaktif-v3">
11452                        <h2 class="NAGARP-title-v3">🎯 PREDIKSI NAGARP RESMI (SIMULASI) 🎯</h2>
11453
11454                        <div class="NAGARP-controls-v3">
11455                            <label>
11456                                PASARAN
11457                                <select id="marketFilter">
11458                                    <option value="ALL">ALL PASARAN</option>
11459                                </select>
11460                            </label>
11461
11462                            <label>
11463                                TIPE
11464                                <select id="typeFilter">
11465                                    <option value="4D">4D</option>
11466                                    <option value="3D">3D</option>
11467                                    <option value="2D">2D</option>
11468                                </select>
11469                            </label>
11470
11471                            <label>
11472                                MIN CONF
11473                                <input id="minConf" type="number" min="50" max="99" step="1" value="70">
11474                            </label>
11475
11476                            <button id="btnNAGARPRandom" type="button">
11477                                <span class="icon">🎲</span> GENERATE
11478                            </button>
11479                        </div>
11480
11481                        <div class="NAGARP-main-result-v3" id="NAGARPResult">
11482                            <div class="NAGARP-status-v3" id="NAGARPStatus">— READY —</div>
11483
11484                            <div class="NAGARP-highlight-box">
11485                                <div class="NAGARP-col-left">
11486                                    <div class="k">Pasaran Rekomendasi</div>
11487                                    <div class="v-game" id="rMarket">-</div>
11488
11489                                    <div class="provider-info">
11490                                        <span class="k-small">Tipe:</span>
11491                                        <span class="v-small" id="rType">-</span>
11492                                        <span class="k-small" style="margin-left:10px;">Conf:</span>
11493                                        <span class="v-small" id="rConf">-</span>
11494                                    </div>
11495                                </div>
11496
11497                                <div class="NAGARP-col-right">
11498                                    <div class="k-rtp">Angka Utama</div>
11499                                    <div class="v-rtp-huge" id="rAngka">-</div>
11500                                </div>
11501                            </div>
11502
11503                            <div class="NAGARP-details-v3">
11504                                <div class="NAGARP-detail-item">
11505                                    <div class="k-detail">BBFS</div>
11506                                    <div class="v-detail" id="rBBFS">-</div>
11507                                </div>
11508                                <div class="NAGARP-detail-item">
11509                                    <div class="k-detail">Angka Ikut</div>
11510                                    <div class="v-detail" id="rIkut">-</div>
11511                                </div>
11512                                <div class="NAGARP-detail-item">
11513                                    <div class="k-detail">Shio</div>
11514                                    <div class="v-detail" id="rShio">-</div>
11515                                </div>
11516                            </div>
11517
11518                            <div class="NAGARP-tip-v3" id="rTip">
11519                                Tips: Ini simulasi hiburan. Tetap bijak & disiplin.
11520                            </div>
11521                        </div>
11522
11523                        <div class="NAGARP-alts-v3">
11524                            <div class="NAGARP-alts-title-v3">3 Alternatif Angka</div>
11525                            <div class="NAGARP-alt-list-v3" id="NAGARPAltList"></div>
11526                        </div>
11527
11528                        <div class="NAGARP-note-v3">
11529                            Disclaimer: Hasil di halaman ini random (simulasi), bukan acuan hasil resmi.
11530                        </div>
11531                    </div>
11532                </div>
11533
11534                <style>
11535                    @import url("https://fonts.googleapis.com/css2?family=Orbitron:wght@600;700;800&display=swap");
11536
11537                    /* ===============================
11538   WRAPPER
11539================================ */
11540                    .Perubahan-Teknologi-Digital-v3 {
11541                        display: flex;
11542                        justify-content: center;
11543                        padding: 16px 10px;
11544                        font-family: "Orbitron", "Poppins", system-ui, sans-serif;
11545                    }
11546
11547                    /* ===============================
11548   MACHINE CORE (#c9832a ELEGANT)
11549================================ */
11550                    .Pengalaman-Platform-Interaktif-v3 {
11551                        position: relative;
11552                        width: 100%;
11553                        max-width: 520px;
11554                        padding: 16px;
11555                        border-radius: 16px;
11556                        overflow: hidden;
11557                        isolation: isolate;
11558                        background: radial-gradient(circle at 15% 0%, rgba(163,100,34,1), transparent 48%), radial-gradient(circle at 85% 100%, rgba(201,131,42,0.18), transparent 55%), linear-gradient(123deg, #000000, #000000);
11559                        border: 1px solid rgba(201,131,42,1);
11560                        box-shadow: inset 0 0 18px rgba(201,131,42,1), 0 0 18px rgba(201,131,42,1);
11561                        color: #dbdbdb;
11562                    }
11563
11564                    /* HUD SWEEP */
11565                    .Pengalaman-Platform-Interaktif-v3::before {
11566                        content: "";
11567                        position: absolute;
11568                        inset: 0;
11569                        background: linear-gradient(120deg, transparent 40%, rgba(226,166,66,.10), transparent 65%);
11570                        animation: hudSweep 7s linear infinite;
11571                        pointer-events: none;
11572                        opacity: .8;
11573                    }
11574
11575                    /* ===============================
11576   TITLE
11577================================ */
11578                    .NAGARP-title-v3 {
11579                        text-align: center;
11580                        margin: 0 0 12px;
11581                        font-size: 15px;
11582                        font-weight: 900;
11583                        letter-spacing: 1px;
11584                        text-shadow: 0 0 6px rgba(163,100,34,90%);
11585                    }
11586
11587                    /* ===============================
11588   CONTROLS
11589================================ */
11590                    .NAGARP-controls-v3 {
11591                        display: grid;
11592                        grid-template-columns: 1fr 1fr;
11593                        gap: 10px;
11594                        padding: 12px;
11595                        border-radius: 14px;
11596
11597                        background: rgba(0, 0, 0, 0.4);
11598                        border: 1px solid rgba(226,166,66,0.45);
11599                        box-shadow: inset 0 0 14px rgba(201,131,42,22%);
11600                    }
11601
11602                    .NAGARP-controls-v3 label {
11603                        display: grid;
11604                        gap: 6px;
11605                        font-size: 10px;
11606                        letter-spacing: .8px;
11607                        text-transform: uppercase;
11608                        color: #c9832a;
11609                    }
11610
11611                    .NAGARP-controls-v3 select,
11612                    .NAGARP-controls-v3 input {
11613                        background: rgba(0 0 0 / 92%);
11614                        color: #c5c5c5;
11615                        border: 1px solid rgba(201,131,42,1);
11616                        border-radius: 10px;
11617                        padding: 7px 9px;
11618                        font-weight: 700;
11619                        font-size: 12px;
11620                        outline: none;
11621                        box-shadow: inset 0 0 10px rgba(201,131,42,1);
11622                    }
11623
11624                    /* BUTTON */
11625                    #btnNAGARPRandom {
11626                        grid-column: span 2;
11627                        display: flex;
11628                        align-items: center;
11629                        justify-content: center;
11630                        gap: 8px;
11631
11632                        background: linear-gradient(145deg, #c9832a, #c9832a);
11633                        border: none;
11634                        color: #000000;
11635                        font-weight: 900;
11636                        letter-spacing: .8px;
11637                        border-radius: 14px;
11638                        cursor: pointer;
11639                        padding: 10px 12px;
11640                        font-size: 12px;
11641
11642                        box-shadow: 0 0 16px rgba(226,166,66,0.75);
11643                        transition: .2s ease;
11644                    }
11645
11646                    #btnNAGARPRandom:active {
11647                        transform: scale(.98);
11648                    }
11649
11650                    #btnNAGARPRandom:disabled {
11651                        opacity: .75;
11652                        cursor: not-allowed;
11653                    }
11654
11655                    /* ===============================
11656   MAIN RESULT
11657================================ */
11658                    .NAGARP-main-result-v3 {
11659                        margin-top: 12px;
11660                        padding: 12px;
11661                        border-radius: 14px;
11662
11663                        background: rgba(18,12,8,0.78);
11664                        border: 1px solid rgba(226,166,66,1);
11665                        box-shadow: inset 0 0 16px rgba(226,166,66,1);
11666                    }
11667
11668                    .NAGARP-status-v3 {
11669                        text-align: center;
11670                        font-weight: 800;
11671                        letter-spacing: .8px;
11672                        color: #c9832a;
11673                        margin-bottom: 10px;
11674                        font-size: 11px;
11675                    }
11676
11677                    .NAGARP-highlight-box {
11678                        display: flex;
11679                        justify-content: space-between;
11680                        gap: 12px;
11681                        padding-bottom: 10px;
11682                        margin-bottom: 10px;
11683                        border-bottom: 1px dashed rgba(226,166,66,0.3);
11684                    }
11685
11686                    .k,
11687                    .k-rtp {
11688                        font-size: 10px;
11689                        color: #c7c7c7;
11690                        text-transform: uppercase;
11691                    }
11692
11693                    .v-game {
11694                        font-size: 14px;
11695                        font-weight: 900;
11696                        color: #ffffff;
11697                        margin-top: 6px;
11698                        line-height: 1.2;
11699                    }
11700
11701                    .provider-info {
11702                        margin-top: 8px;
11703                        font-size: 11px;
11704                        color: #d9d9d9;
11705                        opacity: .95;
11706                    }
11707
11708                    .k-small {
11709                        color: #c9832a;
11710                        font-size: 10px;
11711                    }
11712
11713                    .v-small {
11714                        color: #fff0f0;
11715                        font-weight: 900;
11716                    }
11717
11718                    .v-rtp-huge {
11719                        font-size: 2.4em;
11720                        font-weight: 900;
11721                        color: #e2a642;
11722                        text-shadow: 0 0 10px rgba(0 0 0);
11723                        line-height: 1;
11724                        letter-spacing: 1px;
11725                    }
11726
11727                    /* DETAILS */
11728                    .NAGARP-details-v3 {
11729                        display: grid;
11730                        grid-template-columns: repeat(3, 1fr);
11731                        gap: 8px;
11732                        background: rgba(0, 0, 0, .30);
11733                        padding: 10px;
11734                        border-radius: 12px;
11735                    }
11736
11737                    .k-detail {
11738                        font-size: 9px;
11739                        color: #c9832a;
11740                        text-transform: uppercase;
11741                    }
11742
11743                    .v-detail {
11744                        font-size: 12px;
11745                        font-weight: 800;
11746                        color: #ffffff;
11747                        margin-top: 4px;
11748                        letter-spacing: .8px;
11749                    }
11750
11751                    .NAGARP-tip-v3 {
11752                        margin-top: 10px;
11753                        font-size: 10px;
11754                        color: #d9d9d9;
11755                        opacity: .9;
11756                        text-align: center;
11757                    }
11758
11759                    /* ALTS */
11760                    .NAGARP-alts-v3 {
11761                        margin-top: 12px;
11762                        padding: 12px;
11763                        border-radius: 14px;
11764                        background: rgba(0, 0, 0, .40);
11765                        border: 1px solid rgba(226,166,66,1);
11766                    }
11767
11768                    .NAGARP-alts-title-v3 {
11769                        font-weight: 900;
11770                        color: #c9832a;
11771                        margin-bottom: 10px;
11772                        font-size: 12px;
11773                    }
11774
11775                    .NAGARP-alt-list-v3 {
11776                        display: grid;
11777                        grid-template-columns: 1fr;
11778                        gap: 8px;
11779                    }
11780
11781                    .NAGARP-alt-item {
11782                        background: rgba(18,12,8,.86);
11783                        border-radius: 12px;
11784                        padding: 10px;
11785                        border-left: 3px solid rgba(163,100,34,90%);
11786                    }
11787
11788                    .NAGARP-alt-item .g {
11789                        font-weight: 900;
11790                        color: #c5c5c5;
11791                        font-size: 12px;
11792                    }
11793
11794                    .NAGARP-alt-item .p {
11795                        font-size: 10px;
11796                        color: #c9832a;
11797                        margin-top: 4px;
11798                    }
11799
11800                    .NAGARP-alt-item .r {
11801                        margin-top: 6px;
11802                        font-weight: 900;
11803                        color: #e2a642;
11804                        letter-spacing: .8px;
11805                        font-size: 12px;
11806                    }
11807
11808                    .NAGARP-note-v3 {
11809                        margin-top: 10px;
11810                        font-size: 10px;
11811                        color: #d9d9d9;
11812                        opacity: .85;
11813                        text-align: center;
11814                    }
11815
11816                    /* MOBILE */
11817                    @media(max-width:420px) {
11818                        .NAGARP-highlight-box {
11819                            flex-direction: column
11820                        }
11821
11822                        .NAGARP-details-v3 {
11823                            grid-template-columns: 1fr
11824                        }
11825                    }
11826
11827                    /* ANIM */
11828                    @keyframes hudSweep {
11829                        0% {
11830                            transform: translateX(-110%)
11831                        }
11832
11833                        100% {
11834                            transform: translateX(110%)
11835                        }
11836                    }
11837
11838                    @keyframes pulse {
11839                        0% {
11840                            opacity: .55
11841                        }
11842
11843                        50% {
11844                            opacity: 1
11845                        }
11846
11847                        100% {
11848                            opacity: .55
11849                        }
11850                    }
11851                </style>
11852
11853                
11853<script>
11854                    /* ===============================
11855                       DATASET NAGARP (SIMULASI)
11856                    ================================ */
11857                    const NAGARP_MARKETS = [
11858                        { market: "Singapore", code: "SGP", open: "17:45", zone: "WIB" },
11859                        { market: "Hongkong", code: "HK", open: "22:45", zone: "WIB" },
11860                        { market: "Sydney", code: "SDY", open: "13:45", zone: "WIB" },
11861                        { market: "Macau", code: "MC", open: "16:00", zone: "WIB" },
11862                        { market: "Cambodia", code: "KHM", open: "10:45", zone: "WIB" },
11863                        { market: "Japan", code: "JP", open: "11:30", zone: "WIB" }
11864                    ];
11865
11866                    const SHIO = ["Tikus", "Kerbau", "Macan", "Kelinci", "Naga", "Ular", "Kuda", "Kambing", "Monyet", "Ayam", "Anjing", "Babi"];
11867
11868                    const $ = (q) => document.querySelector(q);
11869                    const randInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
11870
11871                    function uniqueMarkets() {
11872                        return NAGARP_MARKETS.map(m => m.market);
11873                    }
11874
11875                    function genDigits(len) {
11876                        let s = "";
11877                        for (let i = 0; i < len; i++) s += String(randInt(0, 9));
11878                        return s;
11879                    }
11880
11881                    function genBBFS() {
11882                        const pool = [...Array(10)].map((_, i) => i);
11883                        const out = [];
11884                        for (let i = 0; i < 7; i++) {
11885                            const idx = randInt(0, pool.length - 1);
11886                            out.push(pool.splice(idx, 1)[0]);
11887                        }
11888                        return out.join("");
11889                    }
11890
11891                    function genIkut() {
11892                        const pool = [...Array(10)].map((_, i) => i);
11893                        const out = [];
11894                        for (let i = 0; i < 4; i++) {
11895                            const idx = randInt(0, pool.length - 1);
11896                            out.push(pool.splice(idx, 1)[0]);
11897                        }
11898                        return out.join(" • ");
11899                    }
11900
11901                    function simulateConfidence() {
11902                        return randInt(60, 96);
11903                    }
11904
11905                    function applyFilters() {
11906                        const m = $("#marketFilter").value;
11907                        const minC = parseFloat($("#minConf").value || "0");
11908
11909                        return NAGARP_MARKETS
11910                            .filter(x => (m === "ALL" || x.market === m))
11911                            .map(x => ({ ...x, conf: simulateConfidence() }))
11912                            .filter(x => x.conf >= minC);
11913                    }
11914
11915                    function pickRandom(arr, n = 1) {
11916                        const copy = [...arr];
11917                        const out = [];
11918                        for (let i = 0; i < n && copy.length; i++) {
11919                            const idx = randInt(0, copy.length - 1);
11920                            out.push(copy.splice(idx, 1)[0]);
11921                        }
11922                        return out;
11923                    }
11924
11925                    function formatAngka(type) {
11926                        if (type === "4D") return genDigits(4);
11927                        if (type === "3D") return genDigits(3);
11928                        return genDigits(2);
11929                    }
11930
11931                    function renderMain(item, type) {
11932                        $("#rMarket").textContent = `${item.market} (${item.code}) • Open ${item.open} ${item.zone}`;
11933                        $("#rType").textContent = type;
11934                        $("#rConf").textContent = `${item.conf}%`;
11935
11936                        $("#rAngka").textContent = formatAngka(type);
11937                        $("#rBBFS").textContent = genBBFS();
11938                        $("#rIkut").textContent = genIkut();
11939                        $("#rShio").textContent = SHIO[randInt(0, SHIO.length - 1)];
11940                        $("#rTip").textContent = "Tips: Ini simulasi hiburan. Tetap bijak & disiplin.";
11941                    }
11942
11943                    function renderAlts(list, type) {
11944                        const wrap = $("#NAGARPAltList");
11945                        wrap.innerHTML = "";
11946                        list.forEach(x => {
11947                            const el = document.createElement("div");
11948                            el.className = "NAGARP-alt-item";
11949                            el.innerHTML = `
11950      <div class="g">${x.market} (${x.code})</div>
11951      <div class="p">Tipe ${type} • Conf <strong>${x.conf}%</strong></div>
11952      <div class="r">Angka: <strong>${formatAngka(type)}</strong></div>
11953    `;
11954                            wrap.appendChild(el);
11955                        });
11956                    }
11957
11958                    function spinButton(btn, on) {
11959                        if (on) {
11960                            btn.disabled = true;
11961                            btn.dataset.text = btn.innerHTML;
11962                            btn.innerHTML = `<span class="icon">🔄</span> PROCESS...`;
11963                        } else {
11964                            btn.disabled = false;
11965                            btn.innerHTML = btn.dataset.text || `<span class="icon">🎲</span> GENERATE`;
11966                        }
11967                    }
11968
11969                    /* ===============================
11970                       INIT
11971                    ================================ */
11972                    (function init() {
11973                        const sel = $("#marketFilter");
11974                        uniqueMarkets().forEach(m => {
11975                            const o = document.createElement("option");
11976                            o.value = m; o.textContent = m;
11977                            sel.appendChild(o);
11978                        });
11979
11980                        $("#btnNAGARPRandom").addEventListener("click", (e) => {
11981                            const btn = e.currentTarget;
11982                            const type = $("#typeFilter").value;
11983                            const filtered = applyFilters();
11984                            const st = $("#NAGARPStatus");
11985
11986                            if (!filtered.length) {
11987                                st.textContent = "— NO MATCH —";
11988                                st.style.color = "white";
11989                                renderAlts([], type);
11990                                $("#rMarket").textContent = "-";
11991                                $("#rType").textContent = type;
11992                                $("#rConf").textContent = "-";
11993                                $("#rAngka").textContent = "-";
11994                                $("#rBBFS").textContent = "-";
11995                                $("#rIkut").textContent = "-";
11996                                $("#rShio").textContent = "-";
11997                                return;
11998                            }
11999
12000                            spinButton(btn, true);
12001                            st.textContent = "— GENERATING —";
12002                            st.style.animation = "pulse .6s linear infinite";
12003                            st.style.color = "white";
12004
12005                            const t0 = setInterval(() => {
12006                                const temp = pickRandom(filtered, 1)[0];
12007                                $("#rMarket").textContent = `${temp.market} (${temp.code}) • Open ${temp.open} ${temp.zone}`;
12008                                $("#rType").textContent = type;
12009                                $("#rConf").textContent = `${temp.conf}%`;
12010                                $("#rAngka").textContent = formatAngka(type);
12011                                $("#rBBFS").textContent = genBBFS();
12012                                $("#rIkut").textContent = genIkut();
12013                                $("#rShio").textContent = SHIO[randInt(0, SHIO.length - 1)];
12014                            }, 85);
12015
12016                            setTimeout(() => {
12017                                clearInterval(t0);
12018
12019                                const picks = pickRandom(filtered, 4);
12020                                renderMain(picks[0], type);
12021                                renderAlts(picks.slice(1), type);
12022
12023                                st.textContent = "— READY —";
12024                                st.style.animation = "";
12025                                st.style.color = "gold";
12026                                spinButton(btn, false);
12027                            }, 1200);
12028                        });
12029
12030                        renderAlts([], "4D");
12031                    })();
12032                </script>
12032
12033
12034<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 12034
12034
12035<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 12035
12035
12036<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 12036
12036
12037<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v833ccba57c9e4d2798f2e76cebdd09a11778172276447" integrity="sha512-57MDmcccJXYtNnH+ZiBwzC4jb2rvgVCEokYN+L/nLlmO8rfYT/gIpW2A569iJ/3b+0UEasghjuZH/ma3wIs/EQ==" data-cf-beacon='{"version":"2024.11.0","token":"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 12037
12037
12038<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v833ccba57c9e4d2798f2e76cebdd09a11778172276447" integrity="sha512-57MDmcccJXYtNnH+ZiBwzC4jb2rvgVCEokYN+L/nLlmO8rfYT/gIpW2A569iJ/3b+0UEasghjuZH/ma3wIs/EQ==" data-cf-beacon='{"version":"2024.11.0","token":"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 12038
12038
12039<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v833ccba57c9e4d2798f2e76cebdd09a11778172276447" integrity="sha512-57MDmcccJXYtNnH+ZiBwzC4jb2rvgVCEokYN+L/nLlmO8rfYT/gIpW2A569iJ/3b+0UEasghjuZH/ma3wIs/EQ==" data-cf-beacon='{"version":"2024.11.0","token":"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 12039
12039
12040<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v833ccba57c9e4d2798f2e76cebdd09a11778172276447" integrity="sha512-57MDmcccJXYtNnH+ZiBwzC4jb2rvgVCEokYN+L/nLlmO8rfYT/gIpW2A569iJ/3b+0UEasghjuZH/ma3wIs/EQ==" data-cf-beacon='{"version":"2024.11.0","token":"51df3b17a746455f940c0b561e8c0c0b","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
12040
12041
12042<style>
12043body{background:#ebf2fc; font-family:Poppins,sans-serif; color:white;}
12044.Perubahan-Teknologi-Digital{
12045max-width: 520px;
12046    margin: auto;
12047    padding: 15px;
12048    border-radius: 12px;
12049    background: #a36422;
12050    border: 2px solid #c9832a;
12051    box-shadow: 0 0 20px #e2a642;
12052}
12053
12054.NAGARP-header{
12055  font-weight:900;
12056  text-align:center;
12057  color:#e2a642;
12058  text-shadow:0 0 12px #5f90d9;
12059  margin-bottom:12px;
12060}
12061
12062.NAGARP-controls{
12063  display:grid;
12064  grid-template-columns:1fr 1fr;
12065  gap:10px;
12066  margin-bottom:12px;
12067}
12068.NAGARP-controls label{
12069  display:flex; flex-direction:column;
12070  font-size:11px; color:#e2a642;
12071}
12072.NAGARP-controls select,
12073.NAGARP-controls input{
12074  padding:8px;
12075  border-radius:8px;
12076  border:1px solid #e2a642;
12077  background:#120809;
12078  color:white;
12079}
12080#btnGenerate{
12081  grid-column:span 2;
12082  padding:12px;
12083  background:#5f90d9;
12084  border:none;
12085  color:black;
12086  font-weight:900;
12087  cursor:pointer;
12088  margin-top:5px;
12089  box-shadow:0 0 15px #e2a642;
12090}
12091
12092.NAGARP-result{
12093  margin-top:12px;
12094  padding:12px;
12095  border-radius:10px;
12096  background:rgba(226,166,66,0.05);
12097  border:1px solid #e2a642;
12098}
12099
12100.NAGARP-main div, .NAGARP-details div{
12101  margin:5px 0;
12102}
12103
12104#rPrediction{
12105  font-size:24px;
12106  font-weight:900;
12107  color:#5f90d9;
12108}
12109
12110.NAGARP-alts{
12111  margin-top:10px;
12112}
12113.NAGARP-alts h4{margin-bottom:6px;}
12114#NAGARPAltList div{
12115  background:#120809;
12116  padding:6px 10px;
12117  margin:3px 0;
12118  border-radius:8px;
12119  border-left:3px solid #5f90d9;
12120}
12121  .bank-tab.active, .bank-tab:hover {
12122    background: #e2a642;
12123    color: #fff;
12124    border-color: #e2a642;
12125    box-shadow: 0 3px 10px -4px rgb(226 166 66 / 60%);
12126}
12127
12128.NAGARP-disclaimer{
12129  font-size:10px;
12130  margin-top:8px;
12131  text-align:center;
12132  color:#e2a642;
12133}
12134</style>
12135
12136<script>
12137const leagues = ["Premier League","La Liga","Serie A"];
12138const tips = ["Analisis statistik tim","Performa terakhir","Cek head-to-head"];
12139function rand(arr){return arr[Math.floor(Math.random()*arr.length)];}
12140
12141var _bgBtn=document.getElementById("btnGenerate");if(_bgBtn)_bgBtn.onclick=function(){
12142  const type = document.getElementById("betTypeFilter").value;
12143  const league = rand(leagues);
12144
12145  let pred;
12146  if(type==="1X2") pred=rand(["1","X","2"]);
12147  else if(type==="OVER_UNDER") pred=rand(["Over","Under"]);
12148  else pred=rand(["Home -1","Away +1","Draw"]);
12149
12150  document.getElementById("rLeague").innerText=league;
12151  document.getElementById("rPrediction").innerText=pred;
12152  document.getElementById("rBetType").innerText=type;
12153  document.getElementById("rConf").innerText=Math.floor(Math.random()*40+60)+"%";
12154  document.getElementById("rTip").innerText=rand(tips);
12155  document.getElementById("rOdds").innerText=(Math.random()*3+1).toFixed(2);
12156
12157  // alternatif
12158  const alt=document.getElementById("NAGARPAltList"); alt.innerHTML="";
12159  for(let i=0;i<3;i++){let d=document.createElement("div"); d.innerText=rand(["1","X","2"]); alt.appendChild(d);}
12160  
12161  document.getElementById("NAGARPStatus").innerText="— READY —";
12162}
12163</script>
12163
12164
12165
12166                        
12167            <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>
12168        </div>
12169        </div>
12170        </section>
12171
12172        <aside class="pd-sidebar">
12173            <div class="pd-side-card pd-buy-card">
12174                <form class="pd-buy-form" id="pd-buy-form" data-pid="325">
12175                    <input type="hidden" name="variant_id" id="pd-variant-id" value="0">
12176                    <div>
12177                        <label for="qty" class="qty-label">Qty</label>
12178                        <input type="number" id="qty" name="qty" value="0" min="1"
12179                               max="0"
12180                               disabled>
12181                    </div>
12182                    <div class="pd-actions">
12183                        <button type="submit" class="btn-add-cart" id="btn-add-cart" disabled>+ Keranjang</button>
12184                        <button type="button" class="btn-buy" id="btn-buy" data-id="278426" disabled >Beli</button>
12185                        <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>
12186                                            </div>
12187                </form>
12188                <div class="paylater-promo paylater-promo-md paylater-desktop-only">
12189    <span class="paylater-icon">💳</span>
12190    <span class="paylater-text">
12191        <strong>Autorized NAGARP</strong>
12192        <small>Situs Resmi NAGARP </small>
12193    </span>
12194</div>            </div>
12195
12196                        
12197                        <div class="pd-side-card qoala-banner-card">
12198                <div class="qoala-banner">
12199                    <div class="qoala-banner-content">
12200                        <div class="qoala-icon">
12201                            <center><img src="https://sunnypork.com/assets/images/icon-nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.png" alt="NAGARP" class="qoala-shield-img"></center>
12202                        </div>
12203                        <div class="qoala-text">
12204                            <div class="qoala-title">Login NAGARP</div>
12205                            <div class="qoala-subtitle">Verified</div>
12206                        </div>
12207                    </div>
12208                    <a href="https://sunny-401.sunnypork.com/" class="qoala-cta"
12209                       data-ga-event="qoala_banner_click"
12210                       data-product-id="325"
12211                       data-product-name="NAGARP"
12212                       data-category-path="Komputer &amp; Laptop/Laptop/Gaming" target="_blank">Info</a>
12213                </div>
12214<style>
12215.qoala-banner-card{padding:0;overflow:hidden}
12216
12217.qoala-banner{
12218  background:linear-gradient(135deg, #c9832a 0%, #c9832a 100%);
12219  padding:12px 16px;
12220  display:flex;
12221  align-items:center;
12222  justify-content:space-between;
12223  color:white;
12224  text-decoration:none;
12225  min-height:60px;
12226}
12227
12228.qoala-banner-content{
12229  display:flex;
12230  align-items:center;
12231  gap:12px;
12232}
12233
12234.qoala-icon{
12235  display:flex;
12236  align-items:center;
12237  justify-content:center;
12238}
12239
12240.qoala-shield-img{
12241  width:36px;
12242  height:36px;
12243  object-fit:contain;
12244  opacity:0.9;
12245}
12246
12247.qoala-title{
12248  font-weight:600;
12249  font-size:14px;
12250  line-height:1.2;
12251}
12252
12253.qoala-subtitle{
12254  font-size:12px;
12255  opacity:0.9;
12256  line-height:1.2;
12257}
12258
12259.qoala-cta{
12260  background:rgba(255,255,255,0.15);
12261  color:white;
12262  padding:6px 12px;
12263  border-radius:6px;
12264  font-size:12px;
12265  font-weight:600;
12266  text-decoration:none;
12267  transition:background 0.2s ease;
12268  border:1px solid rgba(226,166,66,0.4);
12269}
12270
12271.qoala-cta:hover{
12272  background:rgba(226,166,66,0.25);
12273  color:white;
12274}
12275
12276@media(max-width:768px){
12277  .qoala-banner{padding:10px 12px}
12278  .qoala-title{font-size:17px}
12279  .qoala-subtitle{font-size:11px}
12280}
12281</style>
12282            </div>
12283            
12284                        <div class="pd-side-card pd-installment-card2"
12285                 id="inst-card"
12286                 data-base-price="20789000"
12287                 data-zero-smart="0">
12288                <div class="inst-head">
12289                    <h3>Promo & Fitur NAGARP</h3>
12290                                    </div>
12291
12292                <div class="inst-bank-tabs" role="tablist" aria-label="Bank">
12293                    <button type="button" class="bank-tab active" data-bank="Lucky Draw" aria-selected="true">Lucky Draw</button>
12294                    <button type="button" class="bank-tab" data-bank="Lomba Panjat Tur
12294nover" aria-selected="false">Lomba Panjat Turnover</button>
12295                    <button type="button" class="bank-tab" data-bank="Prediksi Jitu" aria-selected="false">Prediksi Jitu</button>
12296                </div>
12297
12298                <ul class="inst-month-list" id="inst-month-list" aria-label="Daftar cicilan per bulan"></ul>
12299                <div class="inst-note" id="inst-zero-note" style="display:none;">0% Cicilan khusus bank terpilih*</div>
12300            </div>
12301            
12302            <!-- MECHA BLUE FINAL BOSS REVIEW SLIDER -->
12303<div class="tnt-review-mecha">
12304
12305<div class="tnt-header">
12306<h2>REVIEW PEMAIN NAGARP</h2>
12307
12308</div>
12309
12310<div class="tnt-slider" id="tntSlider">
12311
12312<div class="tnt-slide active">
12313  <div class="tnt-name">Perubahan Teknologi Digital — Batam</div>
12314  <div class="tnt-comment">"NAGARP X Sunny Pork berhasil menghadirkan nuansa baru lewat integrasi teknologi digital yang membuat platform interaktif terasa lebih hidup dan mudah digunakan.</div>
12315</div>
12316
12317<div class="tnt-slide">
12318  <div class="tnt-name">Pengalaman Platform Interaktif — Pekanbaru</div>
12319  <div class="tnt-comment">"Saya cukup impressed dengan cara NAGARP X Sunny Pork membentuk pengalaman platform interaktif menggunakan teknologi digital terkini tanpa ribet."</div>
12320</div>
12321
12322<div class="tnt-slide">
12323  <div class="tnt-name">Teknologi Digital — Jakarta</div>
12324  <div class="tnt-comment">"Platform NAGARP X Sunny Pork terasa modern berkat perubahan teknologi digital yang diterapkan secara konsisten dan user-friendly."</div>
12325</div>
12326<div class="tnt-slide">
12327  <div class="tnt-name">Platform Interaktif — Medan</div>
12328  <div class="tnt-comment">"Review saya tentang NAGARP X Sunny Pork: pengalaman platform interaktifnya meningkat signifikan berkat pemanfaatan teknologi digital yang tepat."</div>
12329</div>
12330<div class="tnt-slide">
12331  <div class="tnt-name">Membentuk Pengalaman Platform — Medan</div>
12332  <div class="tnt-comment">"NAGARP X Sunny Pork memberikan kesan positif karena teknologi digitalnya mampu membentuk pengalaman platform interaktif yang engaging sehari-hari."</div>
12333</div>
12334
12335
12336
12337</div>
12338</div>
12339
12340</aside>
12341</div>
12342
12343<style>
12344.tnt-review-mecha{
12345  background: radial-gradient(circle at center, #e2a642, #2b1b0e);
12346  border: 2px solid #c9832a;
12347  border-radius: 14px;
12348  padding: 20px;
12349  color: #dbdbdb;
12350  font-family: 'Orbitron', sans-serif;
12351  position: relative;
12352  overflow: hidden;
12353  box-shadow: 0 0 25px #a36422, inset 0 0 20px rgba(226,166,66,0.2);
12354}
12355
12356/* scanline */
12357.tnt-review-mecha::before{
12358  content:"";
12359  position:absolute;
12360  inset:0;
12361  background: repeating-linear-gradient(
12362    to bottom,
12363    rgba(226,166,66,0.08),
12364    rgba(226,166,66,0.08) 1px,
12365    transparent 2px,
12366    transparent 4px
12367  );
12368  pointer-events:none;
12369}.bank-tab.active, .bank-tab:hover
12370  
12371
12372.tnt-header h2{
12373  font-size: 18px;
12374  color:#000000;
12375  text-shadow:0 0 10px #e2a642, 0 0 20px #c9832a;
12376  margin-bottom:5px;
12377}
12378
12379.tnt-header p{
12380  font-size:12px;
12381  opacity:0.85;
12382  margin-bottom:15px;
12383}
12384
12385.tnt-slider{
12386  position:relative;
12387  height:90px;
12388}
12389
12390.tnt-slide{
12391  position:absolute;
12392  width:100%;
12393  opacity:0;
12394  transform:translateX(40px);
12395  transition:all .6s ease;
12396}
12397  .tenor-badge {
12398    background: #e2a642;
12399    color: #fff;
12400    font-weight: 700;
12401    min-width: 42px;
12402    text-align: center;
12403    padding: .38rem .4rem;
12404    font-size: .8rem;
12405    border-radius: 10px;
12406    box-shadow: 0 3px 10px -4px rgb(226,166,66);
12407}
12408
12409.tnt-slide.active{
12410  opacity:1;
12411  transform:translateX(0);
12412}
12413
12414.tnt-name{
12415font-size: 14px;
12416    color: #000000;
12417    margin-bottom: 5px;
12418    text-shadow: 0 0 6px #120809;
12419}
12420
12421.tnt-comment{
12422  font-size:13px;
12423  line-height:1.5;
12424}
12425
12426/* glowing border animation */
12427@keyframes pulseGlow{
12428  0%{box-shadow:0 0 10px #e2a642;}
12429  50%{box-shadow:0 0 25px #e2a642;}
12430  100%{box-shadow:0 0 10px #e2a642;}
12431}
12432
12433.tnt-review-mecha{
12434  animation:pulseGlow 2.5s infinite;
12435}
12436</style>
12437
12438<script>
12439let slides = document.querySelectorAll('.tnt-slide');
12440let index = 0;
12441
12442setInterval(()=>{
12443  slides[index].classList.remove('active');
12444  index = (index + 1) % slides.length;
12445  slides[index].classList.add('active');
12446}, 3000);
12447</script>
12447
12448
12449        <div class="artikel-glow-KT">
12450
12451  <div class="artikel-shine"></div>
12452
12453  <h1>NAGARP X Sunny Pork: Perubahan Teknologi Digital Membentuk Pengalaman Platform Interaktif</h1>
12454 Perubahan Teknologi Digital pada <a href="https://sunnypork.com/">NAGARP</a> X Sunny Pork membentuk pengalaman platform interaktif yang lebih responsif. Pengguna dapat merasakan evolusi layanan digital yang dinamis dan sesuai kebutuhan modern.
12455</div>
12456
12457<style>
12458.artikel-glow-KT{
12459      max-width: 820px;
12460    margin: 40px auto;
12461    padding: 36px;
12462    box-sizing: border-box;
12463    position: relative;
12464    overflow: hidden;
12465    border-radius: 30px;
12466    font-family: Arial, sans-serif;
12467    color: #ffffff;
12468    background: radial-gradient(circle at top left, rgba(201,131,42,1), transparent 20%), radial-gradient(circle at bottom right, rgb(43,27,14), transparent 28%), linear-gradient(135deg, #c9832a, #c9832a, #c9832a);
12469    border: 2px solid rgba(255, 255, 255, .55);
12470    box-shadow: 0 0 25px rgb(201,131,42), 0 0 55px rgb(226,166,66), 0 20px 45px rgba(0, 0, 0, .25), inset 0 0 35px rgba(163,100,34,1);
12471}
12472
12473.artikel-glow-KT:before{
12474  content:"";
12475  position:absolute;
12476  inset:10px;
12477  border-radius:24px;
12478  border:1px solid rgba(255,255,255,.32);
12479  pointer-events:none;
12480}
12481
12482.artikel-shine{
12483  position:absolute;
12484  top:-120px;
12485  left:-180px;
12486  width:280px;
12487  height:650px;
12488  background:linear-gradient(120deg,transparent,rgba(255,255,255,.55),transparent);
12489  transform:rotate(25deg);
12490  animation:artikelShine 4.5s infinite;
12491  pointer-events:none;
12492}
12493
12494@keyframes artikelShine{
12495  0%{left:-220px;}
12496  55%{left:110%;}
12497  100%{left:110%;}
12498}
12499
12500.artikel-glow-KT h1,
12501.artikel-glow-KT h2{
12502  position:relative;
12503  color:#000000;
12504  text-align:center;
12505  line-height:1.35;
12506  text-shadow:
12507    0 0 8px rgba(201,131,42,95%),
12508    0 0 18px rgba(201,131,42,1),
12509    0 2px 8px rgba(163,100,34,1);
12510}
12511
12512.artikel-glow-KT h1{
12513  font-size:31px;
12514  margin:0 0 20px;
12515}
12516
12517.artikel-glow-KT h2{
12518  font-size:24px;
12519  margin:28px 0 14px;
12520}
12521
12522.artikel-glow-KT p{
12523  position:relative;
12524  margin:0 0 16px;
12525  color:#f8ffff;
12526  font-size:15px;
12527  line-height:1.85;
12528  text-align:justify;
12529  text-shadow:0 1px 4px rgba(0,0,0,.28);
12530}
12531
12532.artikel-glow-KT a{
12533  color:#000000;
12534  font-weight:800;
12535  text-shadow:0 0 8px rgba(163,100,34,1);
12536}
12537
12538.artikel-glow-KT img{
12539  position:relative;
12540  display:block;
12541  max-width:100%;
12542  margin:16px auto 24px;
12543  border-radius:16px;
12544  border:1px solid rgba(255,255,255,.55);
12545  box-shadow:
12546    0 0 18px rgba(255,255,255,.55),
12547    0 12px 28px rgba(0,0,0,.28);
12548}
12549
12550.artikel-highlight{
12551  position:relative;
12552  margin:24px 0;
12553  padding:22px;
12554  border-radius:22px;
12555  background:rgba(255,255,255,.20);
12556  border:1px solid rgba(255,255,255,.45);
12557  box-shadow:
12558    inset 0 1px 0 rgba(255,255,255,.65),
12559    inset 0 0 22px rgba(255,255,255,.14),
12560    0 10px 25px rgba(0,0,0,.18);
12561  backdrop-filter:blur(8px);
12562}
12563
12564.artikel-highlight:before{
12565  content:"";
12566  position:absolute;
12567  left:0;
12568  right:0;
12569  top:0;
12570  height:45%;
12571  border-radius:22px 22px 0 0;
12572  background:linear-gradient(rgba(255,255,255,.35),rgba(255,255,255,.04));
12573  pointer-events:none;
12574}
12575
12576.artikel-glow-KT ul{
12577  position:relative;
12578  margin:15px 0 0 20px;
12579  padding:0;
12580}
12581
12582.artikel-glow-KT li{
12583  margin-bottom:11px;
12584  padding:10px 12px;
12585  border-radius:12px;
12586  background:rgba(255,255,255,.16);
12587  color:#ffffff;
12588  line-height:1.6;
12589  box-shadow:inset 0 0 10px rgba(255,255,255,.08);
12590}
12591
12592@media(max-width:600px){
12593  .artikel-glow-KT{
12594    margin:25px 12px;
12595    padding:25px;
12596    border-radius:24px;
12597  }
12598
12599  .artikel-glow-KT h1{
12600    font-size:24px;
12601  }
12602
12603  .artikel-glow-KT h2{
12604    font-size:21px;
12605  }
12606}
12607</style>
12608
12609<div class="KT-faq-rainbow">
12610  <div class="KT-shine"></div>
12611
12612  <div class="KT-faq-top">
12613    <span>★ PREMIUM FAQ ★</span>
12614    <h2>Pertanyaan Umum NAGARP</h2>
12615    
12616  </div>
12617
12618  <div class="KT-rainbow-item">
12619    <button type="button">Apa dampak perubahan teknologi digital pada platform interaktif NAGARP X Sunny Pork?</button>
12620    <div class="KT-rainbow-answer">
12621      <p>Perubahan teknologi digital membantu NAGARP X Sunny Pork menciptakan pengalaman platform interaktif yang lebih responsif dan engaging bagi pengguna.</p>
12622    </div>
12623  </div>
12624
12625  <div class="KT-rainbow-item">
12626    <button type="button">Bagaimana NAGARP X Sunny Pork memanfaatkan teknologi digital untuk platformnya?</button>
12627    <div class="KT-rainbow-answer">
12628      <p>NAGARP X Sunny Pork mengintegrasikan teknologi digital terkini untuk membentuk pengalaman platform interaktif yang dinamis dan mudah diakses.</p>
12629    </div>
12630  </div>
12631
12632  <div class="KT-rainbow-item">
12633    <button type="button">Apakah NAGARP X Sunny Pork menawarkan fitur interaktif berbasis teknologi digital?</button>
12634    <div class="KT-rainbow-answer">
12635      <p>Ya, NAGARP X Sunny Pork fokus pada perubahan teknologi digital yang membentuk pengalaman platform interaktif dengan elemen visual dan navigasi canggih.</p>
12636    </div>
12637  </div>
12638
12639  <div class="KT-rainbow-item">
12640    <button type="button">Mengapa pengalaman platform interaktif penting di NAGARP X Sunny Pork?</button>
12641    <div class="KT-rainbow-answer">
12642      <p>Pengalaman platform interaktif di NAGARP X Sunny Pork menjadi prioritas karena perubahan teknologi digital memungkinkan interaksi yang lebih personal dan efisien.</p>
12643    </div>
12644  </div>
12645  <div class="KT-rainbow-item">
12646    <button type="button">Apa keunggulan NAGARP X Sunny Pork dalam hal teknologi digital?</button>
12647    <div class="KT-rainbow-answer">
12648      <p>Keunggulan NAGARP X Sunny Pork terletak pada kemampuannya memanfaatkan perubahan teknologi digital untuk menghadirkan pengalaman platform interaktif yang seamless.</p>
12649    </div>
12650  </div>
12651</div>
12652
12653<style>
12654.KT-faq-rainbow{
12655      max-width: 760px;
12656    margin: 40px auto;
12657    padding: 30px;
12658    box-sizing: border-box;
12659    position: relative;
12660    overflow: hidden;
12661    border-radius: 32px;
12662    font-family: Arial, sans-serif;
12663    background: radial-gradient(circle at 15% 10%, rgba(201,131,42,1), transparent 18%), radial-gradient(circle at 85% 15%, rgb(201,131,42), transparent 25%), radial-gradient(circle at 20% 90%, rgb(201,131,42), transparent 25%), linear-gradient(135deg, #c9832a, #a36422, #a36422, #c9832a);
12664    border: 2px solid rgba(201,131,42,1);
12665    box-shadow: 0 0 25px rgb(201,131,42), 0 0 55px rgba(226,166,66,1), inset 0 0 35px rgba(163,100,34,1);
12666}
12667
12668.KT-shine{
12669  position:absolute;
12670  top:-90px;
12671  left:-120px;
12672  width:260px;
12673  height:520px;
12674  background:linear-gradient(120deg,transparent,rgba(255,255,255,.55),transparent);
12675  transform:rotate(25deg);
12676  animation:KTShine 4s infinite;
12677  pointer-events:none;
12678}
12679
12680@keyframes KTShine{
12681  0%{left:-180px;}
12682  55%{left:110%;}
12683  100%{left:110%;}
12684}
12685
12686.KT-faq-top{
12687  position:relative;
12688  text-align:center;
12689  margin-bottom:24px;
12690}
12691
12692.KT-faq-top span{
12693  display:inline-block;
12694  padding:8px 22px;
12695  border-radius:999px;
12696  background:rgba(201,131,42,0%);
12697  color:#dbdbdb;
12698  font-size:12px;
12699  font-weight:900;
12700  letter-spacing:2px;
12701  border:1px solid rgba(215 215 215 / 62%);
12702  box-shadow:inset 0 0 14px rgba(255,255,255,.25);
12703}
12704
12705.KT-faq-top h2{
12706  margin:16px 0 8px;
12707  color:#000000;
12708  font-size:34px;
12709  line-height:1.15;
12710  text-shadow:
12711    0 0 10px rgba(201,131,42,1),
12712    0 0 22px rgba(201,131,42,1);
12713}
12714
12715.KT-faq-top p{
12716  margin:0;
12717  color:#f3ffff;
12718  font-size:14px;
12719}
12720
12721.KT-rainbow-item{
12722  position:relative;
12723  overflow:hidden;
12724  margin-bottom:14px;
12725  border-radius:20px;
12726  background:rgba(255,255,255,.20);
12727  border:1px solid rgba(255,255,255,.48);
12728  box-shadow:
12729    inset 0 1px 0 rgba(255,255,255,.6),
12730    0 10px 25px rgba(0,0,0,.18);
12731  backdrop-filter:blur(10px);
12732}
12733
12734.KT-rainbow-item:before{
12735  content:"";
12736  position:absolute;
12737  top:0;
12738  left:0;
12739  right:0;
12740  height:45%;
12741  background:linear-gradient(rgba(255,255,255,.32),rgba(255,255,255,.04));
12742  pointer-events:none;
12743}
12744
12745.KT-rainbow-item button{
12746  width:100%;
12747  padding:18px 58px 18px 20px;
12748  border:0;
12749  background: #c9832a;
12750  color:#dbdbdb;
12751  text-align:left;
12752  cursor:pointer;
12753  font-size:15px;
12754  font-weight:900;
12755  position:relative;
12756  z-index:2;
12757  text-shadow:0 2px 6px rgba(0,0,0,.35);
12758}
12759
12760.KT-rainbow-item button:after{
12761  content:"+";
12762  position:absolute;
12763  right:18px;
12764  top:50%;
12765  transform:translateY(-50%);
12766  width:34px;
12767  height:34px;
12768  border-radius:50%;
12769  background:linear-gradient(135deg,#c9832a, #a36422);
12770  color:#d1d1d1;
12771  text-align:center;
12772  line-height:34px;
12773  font-size:24px;
12774  font-weight:900;
12775  box-shadow:
12776    0 0 14px rgba(255,255,255,.8),
12777    inset 0 -4px 8px rgba(0,0,0,.18);
12778}
12779
12780.KT-rainbow-item.active button:after{
12781  content:"−";
12782  background:linear-gradient(135deg,#e2a642, #4d2e14);
12783}
12784
12785.KT-rainbow-answer{
12786  max-height:0;
12787  overflow:hidden;
12788  transition:max-height .35s ease;
12789  position:relative;
12790  z-index:2;
12791}
12792
12793.KT-rainbow-item.active .KT-rainbow-answer{
12794  max-height:170px;
12795}
12796
12797.KT-rainbow-answer p{
12798  margin:0;
12799  padding:0 20px 18px;
12800  color:#dbdbdb;
12801  font-size:14px;
12802  line-height:1.7;
12803  text-shadow:0 2px 5px rgba(0 0 0);
12804}
12805
12806@media(max-width:600px){
12807  .KT-faq-rainbow{
12808    margin:25px 12px;
12809    padding:22px;
12810    border-radius:24px;
12811  }
12812
12813  .KT-faq-top h2{
12814    font-size:26px;
12815  }
12816}
12817</style>
12818
12819<script>
12820document.querySelectorAll(".KT-rainbow-item button").forEach(function(btn){
12821  btn.addEventListener("click", function(){
12822    this.parentElement.classList.toggle("active");
12823  });
12824});
12825</script>
12825
12826<style>
12827@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;900&display=swap');
12828
12829.cyber-mecha {
12830  max-width: 800px;
12831  margin: 40px auto;
12832
12833  /* deep luxury #c9832a */
12834  background: radial-gradient(circle at top, #14b1d7, #14b1d7 70%);
12835
12836  border-radius: 16px;
12837  border: 1px solid rgba(226,166,66,0.35);
12838
12839  box-shadow:
12840    0 0 25px rgba(163,100,34,0.35),
12841    inset 0 0 20px rgba(226,166,66,0.10);
12842
12843  padding: 25px;
12844  font-family: 'Orbitron', sans-serif;
12845  color: #f7e6e6;
12846  position: relative;
12847  overflow: hidden;
12848}
12849
12850/* scanline overlay */
12851.cyber-mecha::before {
12852  content: "";
12853  position: absolute;
12854  inset: 0;
12855  background: repeating-linear-gradient(
12856    to bottom,
12857    rgba(226,166,66,0.03),
12858    rgba(226,166,66,0.03) 1px,
12859    transparent 1px,
12860    transparent 3px
12861  );
12862  pointer-events: none;
12863}
12864
12865.pd-full-desc-title {
12866  text-align: center;
12867  font-size: 22px;
12868
12869  /* elegant muted #c9832a */
12870  color: #e2a642;
12871
12872  letter-spacing: 2px;
12873  text-shadow: 0 0 10px rgba(226,166,66,0.4);
12874  margin-bottom: 20px;
12875}
12876
12877.mecha-header h3 {
12878  text-align: center;
12879  font-size: 18px;
12880  color: #e2a642;
12881  text-shadow: 0 0 8px rgba(226,166,66,0.5);
12882  margin-bottom: 15px;
12883}
12884
12885.mecha-image img {
12886  width: 100%;
12887  border-radius: 10px;
12888  border: 1px solid rgba(226,166,66,0.4);
12889  box-shadow: 0 0 20px rgba(226,166,66,0.25);
12890  margin-bottom: 20px;
12891}
12892
12893.mecha-content p {
12894  font-size: 14px;
12895  line-height: 1.8;
12896  margin-bottom: 14px;
12897  color: #f3dede;
12898}
12899
12900.glow {
12901  color: #e2a642;
12902  text-shadow: 0 0 6px rgba(226,166,66,0.5);
12903  font-weight: 600;
12904}
12905
12906.glow-strong {
12907  color: #e2a642;
12908  text-shadow:
12909    0 0 10px rgba(226,166,66,0.6),
12910    0 0 20px rgba(226,166,66,0.4);
12911  font-weight: 700;
12912}
12913
12914.final-line {
12915  margin-top: 18px;
12916  padding: 10px;
12917  border: 1px solid rgba(226,166,66,0.35);
12918  background: rgba(43,27,14,0.25);
12919  border-radius: 8px;
12920  text-align: center;
12921  color: #ffd0d0;
12922}
12923</style>
12924    
12925    
12926    
12927        <section class="pd-related-full">
12928        <h2 class="pd-related-title">Produk Terkait</h2>
12929        <div class="pd-related-grid pd-related-grid--full">
12930                        <a class="pd-related-card" href="https://sunnypork.com/">
12931                <div class="thumb">
12932                    <img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP">
12933                    <span class="mini-disc">-2%</span>                </div>
12934                <div class="info">
12935                    <h3>NAGARP</h3>
12936                    <div class="spec-chips">I5 <span class="spec-divider">|</span> 16GB <span class="spec-divider">|</span> 512GB <span class="spec-divider">|</span> RTX3050</div>                    <span class="price">
12937                        <span class="final">Rp 11.500.000</span>
12938                                                  <span class="ori">Rp 12.799.000</span>
12939                                            </span>
12940                    <span class="sold">Terjual 200+</span>                    <span class="stock-dot low" title="Stok Terbatas"></span>
12941                </div>
12942            </a>
12943                        <a class="pd-related-card" href="https://sunnypork.com/">
12944                <div class="thumb">
12945                    <img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="Teknologi Digital NAGARP">
12946                    <span class="mini-disc">-1%</span>                </div>
12947                <div class="info">
12948                    <h3>NAGARP</h3>
12949                    <div class="spec-chips">I7 <span class="spec-divider">|</span> 16GB <span class="spec-divider">|</span> 512GB <span class="spec-divider">|</span> RTX5050</div>                    <span class="price">
12950                        <span class="final">Rp 22.887.000</span>
12951                                                  <span class="ori">Rp 21.999.000</span>
12952                                            </span>
12953                    <span class="sold">Terjual 20+</span>                    <span class="stock-dot out" title="Habis"></span>
12954                </div>
12955            </a>
12956                        <a class="pd-related-card" href="https://sunnypork.com/">
12957                <div class="thumb">
12958                    <img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE">
12959                    <span class="mini-disc">-1%</span>                </div>
12960                <div class="info">
12961                    <h3>NAGARP ONLINE</h3>
12962                    <div class="spec-chips">R7AI <span class="spec-divider">|</span> 16GB <span class="spec-divider">|</span> 1TB <span class="spec-divider">|</span> RTX5060</div>                    <span class="price">
12963                        <span class="final">Rp 23.600.000</span>
12964                                                  <span class="ori">Rp 21.399.000</span>
12965                                            </span>
12966                    <span class="sold">Terjual 6</span>                    <span class="stock-dot ready" title="Ready"></span>
12967                </div>
12968            </a>
12969                        <a class="pd-related-card" href="https://sunnypork.com/">
12970                <div class="thumb">
12971                    <img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP 4D">
12972                    <span class="mini-disc">-1%</span>                </div>
12973                <div class="info">
12974                    <h3>NAGARP SITUS</h3>
12975                    <div class="spec-chips">R5AI <span class="spec-divider">|</span> 16GB <span class="spec-divider">|</span> 1TB <span class="spec-divider">|</span> RTX5060</div>                    <span class="price">
12976                        <span class="final">Rp 18.800.000</span>
12977                                                  <span class="ori">Rp 19.799.000</span>
12978                                            </span>
12979                    <span class="sold">Terjual 5</span>                    <span class="stock-dot ready" title="Ready"></span>
12980                </div>
12981            </a>
12982                        <a class="pd-related-card" href="https://sunnypork.com/">
12983                <div class="thumb">
12984                    <img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="Teknologi Digital NAGARP 4D">
12985                    <span class="mini-disc">-1%</span>                </div>
12986                <div class="info">
12987                    <h3>NAGARP LINK</h3>
12988                    <div class="spec-chips">I7 <span class="spec-divider">|</span> 16GB <span class="spec-divider">|</span> 512GB <span class="spec-divider">|</span> RTX5050</div>                    <span class="price">
12989                        <span class="final">Rp 28.600.080</span>
12990                                                  <span class="ori">Rp 27.999.000</span>
12991                                            </span>
12992                    <span class="sold">Terjual 6</span>                    <span class="stock-dot low" title="Stok Terbatas"></span>
12993                </div>
12994            </a>
12995                        <a class="pd-related-card" href="https://sunnypork.com/">
12996                <div class="thumb">
12997                    <img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP ONLINE">
12998                    <span class="mini-disc">-1%</span>                </div>
12999                <div class="info">
13000                    <h3>NAGARP SITUS ONLINE</h3>
13001                    <div class="spec-chips">CORE5 <span class="spec-divider">|</span> 16GB <span class="spec-divider">|</span> 512GB <span class="spec-divider">|</span> RTX4050</div>                    <span class="price">
13002                        <span class="final">Rp 17.888.000</span>
13003                                                  <span class="ori">Rp 18.499.000</span>
13004                                            </span>
13005                                        <span class="stock-dot low" title="Stok Terbatas"></span>
13006                </div>
13007            </a>
13008                        
13009                    </div>
13010    </section>
13011    
13012    <div class="pd-mobile-fab" id="pd-mobile-fab" data-pid="325" data-price="20789000" data-voucher-code="" data-voucher-value="0"><div class="mf-actions"><button type="button" class="mf-btn mf-share" id="mf-share" aria-label="Bagikan"><svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z"/></svg></button><button type="button" class="mf-btn mf-chat" id="mf-chat" aria-label="Tanya Produk"><svg viewBox="0 0 640 640" fill="currentColor"><path d="M320 128C241 128 175.3 185.3 162.3 260.7C171.6 257.7 181.6 256 192 256L208 256C234.5 256 256 277.5 256 304L256 400C256 426.5 234.5 448 208 448L192 448C139 448 96 405 96 352L96 288C96 164.3 196.3 64 320 64C443.7 64 544 164.3 544 288L544 456.1C544 522.4 490.2 576.1 423.9 576.1L336 576L304 576C277.5 576 256 554.5 256 528C256 501.5 277.5 480 304 480L336 480C362.5 480 384 501.5 384 528L384 528L424 528C463.8 528 496 495.8 496 456L496 435.1C481.9 443.3 465.5 447.9 448 447.9L432 447.9C405.5 447.9 384 426.4 384 399.9L384 303.9C384 277.4 405.5 255.9 432 255.9L448 255.9C458.4 255.9 468.3 257.5 477.7 260.6C464.7 185.3 399.1 127.9 320 127.9z"/></svg></button><button type="button" class="mf-btn mf-cart" id="mf-cart" disabled aria-label="Tambah ke keranjang"><svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/></svg></button><button type="button" class="mf-btn mf-buy" id="mf-buy" disabled><span class="mf-buy-text">Beli Sekarang</span><span class="mf-buy-price">Rp 20.789.000</span></button></div></div>
13013
13014    <button type="button"
13015            class="pd-scroll-top"
13016            id="pd-scroll-top"
13017            aria-label="Kembali ke atas"
13018            title="Kembali ke atas">
13019        <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
13020            <path fill="currentColor" d="M7.41 14.59 12 10l4.59 4.59L18 13.17l-6-6-6 6z"/>
13021        </svg>
13022    </button>
13023</main>
13024
13025<div class="pd-share-modal-overlay" id="pd-share-modal-overlay"><div class="pd-share-modal"><div class="pd-share-header"><h2 class="pd-share-title">Bagikan Produk</h2><button type="button" class="pd-share-close" id="pd-share-close" aria-label="Tutup"><svg width="20" height="20" viewBox="0 0 24 24"><path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></button></div><div class="pd-share-body"><div class="pd-share-preview"><img src="https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg" alt="NAGARP" class="pd-share-preview-img"><div class="pd-share-preview-info"><h3 class="pd-share-preview-name">NAGARP</h3><p class="pd-share-preview-price">Rp 20.789.000</p></div></div><p class="pd-share-label">Bagikan ke</p><div class="pd-share-grid"><a href="#" class="pd-share-option" data-share="whatsapp" target="_blank" rel="noopener"><span class="pd-share-icon pd-share-icon-whatsapp"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M17.472 14.382c-.297-.149-1.758-.867-2.029-.967-.273-.099-.471-.148-.669.149-.198.297-.767.966-.94 1.164-.173.198-.347.223-.644.074-.297-.149-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.297-.347.446-.52.149-.173.198-.297.298-.495.099-.198.05-.372-.025-.521-.075-.149-.669-1.612-.916-2.206-.242-.579-.487-.5-.669-.51l-.57-.01c-.198 0-.52.074-.792.372s-1.04 1.016-1.04 2.479 1.065 2.876 1.213 3.074c.149.198 2.095 3.2 5.076 4.487.709.306 1.262.489 1.694.626.712.226 1.36.194 1.872.118.571-.085 1.758-.718 2.006-1.411.248-.694.248-1.29.173-1.411-.074-.123-.272-.198-.57-.347m-5.421 7.403h-.002a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.263c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.887 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.158 11.892c0 2.096.547 4.142 1.588 5.945L0 24l6.305-1.654a11.86 11.86 0 005.67 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.485-8.413"/></svg></span><span class="pd-share-name">WhatsApp</span></a><a href="#" class="pd-share-option" data-share="facebook" target="_blank" rel="noopener"><span class="pd-share-icon pd-share-icon-facebook"><svg viewBox="0 0 24 24">
13025<path fill="currentColor" d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg></span><span class="pd-share-name">Facebook</span></a><a href="#" class="pd-share-option" data-share="twitter" target="_blank" rel="noopener"><span class="pd-share-icon pd-share-icon-twitter"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg></span><span class="pd-share-name">Twitter</span></a><a href="#" class="pd-share-option" data-share="telegram" target="_blank" rel="noopener"><span class="pd-share-icon pd-share-icon-telegram"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg></span><span class="pd-share-name">Telegram</span></a><a href="#" class="pd-share-option" data-share="email"><span class="pd-share-icon pd-share-icon-email"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg></span><span class="pd-share-name">Email</span></a><button type="button" class="pd-share-option" data-share="copy"><span class="pd-share-icon pd-share-icon-copy"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg></span><span class="pd-share-name">Salin Link</span></button><button type="button" class="pd-share-option" data-share="pdf"><span class="pd-share-icon pd-share-icon-pdf"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11zM8 15.5h1.5c.83 0 1.5-.67 1.5-1.5v-1c0-.83-.67-1.5-1.5-1.5H8v4zm6 0h1c1.1 0 2-.9 2-2v-2c0-1.1-.9-2-2-2h-1v6zm-4.5-1.5H8v-1h1.5v1zM14 13h1v1h-1v-1z"/></svg></span><span class="pd-share-name">Export PDF</span></button></div><div class="pd-share-copy-section"><div class="pd-share-copy-input-wrap"><input type="text" class="pd-share-copy-input" id="pd-share-url-input" value="https://sunnypork.com/" readonly><button type="button" class="pd-share-copy-btn" id="pd-share-copy-url-btn"><svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg><span class="pd-share-copy-text">Salin</span></button></div></div></div></div></div>
13026
13027<style>.toast-root{position:fixed;left:50%;bottom:18px;transform:translateX(-50%);display:flex;flex-direction:column;gap:10px;z-index:99999;pointer-events:none;max-width:min(92vw,540px)}.toast{display:grid;grid-template-columns:28px 1fr auto;gap:12px;background:#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:#0ea5e9;color:#fff;border:0;border-radius:999px;padding:8px 12px;font-size:12px;font-weight:700;cursor:pointer}.toast .btn.alt{background:transparent;color:#8fb5ff;border:1px solid rgba(143,181,255,.35)}.toast .close{background:transparent;border:0;color:#98a2b3;cursor:pointer;padding:4px;margin-left:6px}.toast.success{border-left:4px solid #22c55e}.toast.info{border-left:4px solid #0ea5e9}.toast.warn{border-left:4px solid #C10DFF}.toast .progress{position:absolute;
13027left:0;bottom:0;height:3px;background:linear-gradient(90deg,#60a5fa,#22c55e);width:100%;transform-origin:left;animation:toastBar linear forwards}@keyframes toastBar{from{transform:scaleX(1)}to{transform:scaleX(0)}}@media(max-width:640px){.toast{grid-template-columns:24px 1fr auto;padding:12px}.toast .btn{padding:7px 10px}}</style>
13028<style>
13029.toast .msg { word-break: break-word; overflow-wrap: anywhere; }
13030
13031.toast-root { bottom: max(12px, env(safe-area-inset-bottom, 12px)); }
13032
13033@media (max-width: 540px) {
13034  .toast-root {
13035    left: 0; right: 0; transform: none;
13036    padding: 0 12px;
13037    bottom: calc(max(12px, env(safe-area-inset-bottom, 12px)) + var(--fab-offset, 72px));
13038    max-width: none;
13039  }
13040  .toast {
13041    grid-template-columns: 1fr; /* 1 kolom */
13042    position: relative;
13043    padding: 14px 14px 12px 14px;
13044    border-radius: 16px;
13045  }
13046  .toast .ico {
13047    position: absolute; left: 14px; top: 14px;
13048    margin: 0; transform: translateY(1px);
13049  }
13050  .toast .cnt { padding-left: 34px; }
13051  .toast .ttl { font-size: 15px; margin-bottom: 6px; }
13052  .toast .msg { font-size: 13px; opacity: .96; }
13053  .toast .actions {
13054    margin-top: 12px; gap: 10px; width: 100%;
13055    display: grid; grid-template-columns: 1fr 1fr;
13056  }
13057  .toast .btn { width: 100%; min-width: 0; padding: 10px 0; }
13058  .toast .close { position: absolute; right: 6px; top: 6px; }
13059}
13060</style>
13061
13062<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
13063(function adjustToastFabOffset(){
13064  const root = document.getElementById('toastRoot');
13065  if (!root) return;
13066
13067  function isVisible(el){
13068    if (!el) return false;
13069    const cs = getComputedStyle(el);
13070    if (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0') return false;
13071    const r = el.getBoundingClientRect();
13072    return r.width > 0 && r.height > 0;
13073  }
13074
13075  function update(){
13076    const fab = document.getElementById('pd-mobile-fab');
13077    let extra = 16;
13078    if (isVisible(fab)) {
13079      const h = Math.round(fab.getBoundingClientRect().height) || 56;
13080      extra = h + 16; // FAB height + margin
13081    }
13082    root.style.setProperty('--fab-offset', extra + 'px');
13083  }
13084
13085  window.addEventListener('resize', update, { passive: true });
13086  const fab = document.getElementById('pd-mobile-fab');
13087  if (window.ResizeObserver && fab) {
13088    const ro = new ResizeObserver(update);
13089    ro.observe(fab);
13090  }
13091  
13092  update();
13093})();
13094</script>
13094
13095<div class="toast-root" id="toastRoot" aria-live="polite" aria-atomic="true"></div>
13096
13097<script nonce="se3iyzEHMc9FQEB4wSyd0w==">(function(){const root=document.getElementById('toastRoot');function svgIcon(kind){if(kind==='success')return '<svg width="24" height="24" viewBox="0 0 24 24" fill="#22c55e"><path d="M9 16.2l-3.5-3.5 1.4-1.4L9 13.4l7.7-7.7 1.4 1.4z"/></svg>';if(kind==='warn')return '<svg width="24" height="24" viewBox="0 0 24 24" fill="#f59e0b"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>';return '<svg width="24" height="24" viewBox="0 0 24 24" fill="#0ea5e9"><path d="M11 7h2v2h-2zm0 4h2v6h-2z"/><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" fill="none" stroke="#0ea5e9"/></svg>'}
13098window.showToast=function(opts){const o=Object.assign({title:'',message:'',type:'info',duration:4000,actions:[]},opts||{});const el=document.createElement('div');el.className='toast '+(o.type||'info');el.setAttribute('role','status');el.style.position='relative';const acts=(o.actions||[]).map(a=>{const lab=a.label||'OK';const cls='btn'+(a.variant==='alt'?' alt':'');return `<button type="button" class="${cls}" data-act="${lab}">${lab}</button>`}).join('');el.innerHTML=`<div class="ico" aria-hidden="true">${svgIcon(o.type)}</div><div class="cnt">${o.title?`<div class="ttl">${o.title}</div>`:''}${o.message?`<div class="msg">${o.message}</div>`:''}</div><div class="actions">${acts}</div><button type="button" class="close" aria-label="Tutup" title="Tutup"><svg width="18" height="18" viewBox="0 0 24 24"><path fill="currentColor" d="M18.3 5.71L12 12.01l-6.3-6.3-1.4 1.41 6.3 6.3-6.3 6.3 1.4 1.41 6.3-6.3 6.3 6.3 1.41-1.41-6.3-6.3 6.3-6.3z"/></svg></button><div class="progress" style="animation-duration:${Math.max(1500,o.duration)}ms"></div>`;
13099
13100    const close = ()=> {
13101      if (!el.parentNode) return;
13102      el.style.transition = 'transform .2s ease, opacity .2s ease';
13103      el.style.opacity = '0'; el.style.transform = 'translateY(10px)';
13104      setTimeout(()=>{ if (el.parentNode) el.parentNode.removeChild(el); }, 220);
13105    };
13106
13107    el.querySelector('.close').addEventListener('click', close);
13108    el.querySelectorAll('.actions .btn').forEach(btn=>{
13109      btn.addEventListener('click', ()=>{
13110        const lab = btn.getAttribute('data-act');
13111        try {
13112          (o.actions||[]).forEach(a=>{ if (a.label===lab && typeof a.onClick==='function') a.onClick(); });
13113        } catch(_){}
13114        close();
13115      });
13116    });
13117
13118    root.appendChild(el);
13119    setTimeout(close, o.duration);
13120    return el;
13121  };
13122})();
13123
13124const _0x1a2b=[{"url":"https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg","alt":"Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Win 11 Home/Eclipse Black/3Y","slug":""},{"url":"https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg","alt":"Lenovo Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Wi...","slug":"https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg"},{"url":"https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg","alt":"Lenovo Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Wi...","slug":"lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-223.webp"},{"url":"https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg","alt":"Lenovo Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Wi...","slug":"lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-776.webp"},{"url":"https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg","alt":"Lenovo Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Wi...","slug":"lenovo-legion-go-8as
13124p2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-634.webp"},{"url":"https://sunnypork.com/assets/images/nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.jpg","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=>c.textContent.trim()).filter(Boolean);return b.length?b.join(' / '):''}
13125function encodeVariantHash(id){if(!id||id<=0)return '';const xor=id^0x4A5D;return xor.toString(36)}
13126function decodeVariantHash(hash){if(!hash)return 0;const xor=parseInt(hash,36);return isNaN(xor)?0:xor^0x4A5D}
13127
13128function showSuccessToast(productId,qty,variantId){const variantText=getVariantSummaryText();const skuText=(function(){const el=document.getElementById('pd-sku');return el?el.textContent.trim():PD_SKU})();showToast({type:'warn',title:'Fitur Segera Hadir',message:`<strong>${escapeHtml(PD_NAME)}</strong>${variantText?` · <em>${escapeHtml(variantText)}</em>`:''}<br>SKU: ${escapeHtml(skuText)} · Qty: ${qty}`,duration:5000,actions:[{label:'Lihat Keranjang',onClick:()=>{}},{label:'Lanjut Belanja',variant:'alt',onClick:()=>{}}]});try{if(window.ga4Ecomm){const qtySend=qty||1;window.ga4Ecomm.addToCart({currency:'IDR',price:20789000,quantity:qtySend,item_id:"SB0B3T",item_name:"Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Win 11 Home/Eclipse Black/3Y",item_brand:"Lenovo ",item_category:(["Komputer & Laptop","Laptop","Gaming"]||[]).join(' / ')})}}catch(_){}return false}function showErrorToast(){showToast({type:'error',title:'Gagal Menambah ke Keranjang',message:'Terjadi kesalahan saat memproses permintaan. Silakan coba lagi.',duration:5000})}
13129
13130function executeBuyNowLegacy(id,variantId){const a=getVariantSummaryText(),b=(function(){const c=document.getElementById('pd-sku');return c?c.textContent.trim():_0x5e6f})();
13131
13132    let willRedirect = false;
13133    try {
13134        if (window.addToCart) {
13135            window.addToCart(id, 1, variantId);
13136            willRedirect = false;
13137        }
13138    } catch(_){}
13139
13140    if (willRedirect) {
13141        showToast({
13142            type: 'info',
13143            title: 'Menuju Keranjang',
13144            message: `<strong>${escapeHtml(PD_NAME)}</strong>${variantText?` · <em>${escapeHtml(variantText)}</em>`:''}<br>SKU: ${escapeHtml(skuText)} · Qty: 1`,
13145            duration: 1800
13146        });
13147        setTimeout(()=>{ window.location.href='/keranjang'; }, 1200);
13148    } else {
13149        showToast({
13150            type: 'warn',
13151            title: 'Fitur Beli Langsung Segera Hadir',
13152            message: `Sementara ini Anda bisa menambahkan produk ke keranjang terlebih dahulu.<br><strong>${escapeHtml(PD_NAME)}</strong>${variantText?` · <em>${escapeHtml(variantText)}</em>`:''}`,
13153            duration: 6000,
13154            actions: [
13155                { label: '+ Keranjang', onClick: ()=> { try{ if (window.addToCart) window.addToCart(id,1,variantId); }catch(_){} } },
13156                { label: 'Hubungi CS', variant: 'alt', onClick: ()=> { window.open('https://wa.me/6281380009898','_blank'); } }
13157            ]
13158        });
13159    }
13160}
13161
13162document.addEventListener('DOMContentLoaded',function(){var a=document.getElementById('pd-buy-form');if(a){a.addEventListener('submit',function(e){var b=parseInt(a.getAttribute('data-pid'),10)||0;if(window.addToCartSubmit){return window.addToCartSubmit(e,b)}})}});document.addEventListener('DOMContentLoaded',function(){var a=document.getElementById('btn-buy');if(a){a.addEventListener('click',function(){var b=a.getAttribute('data-id');buyNow(b)})}});
13163
13164function _0x2m3n(s){return String(s||'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[m]))}
13165
13166(function initGallery(){const mainImg=document.getElementById('pd-main-img');const gallery=document.getElementById('pd-thumb-gallery');if(!mainImg||!gallery)return;gallery.addEventListener('click',function(ev){const btn=ev.target.closest('.pd-thumb-btn');if(!btn)return;const full=btn.getAttribute('data-full');if(full){mainImg.src=full;const alt=btn.getAttribute('data-alt')||'';if(alt)mainImg.alt=alt;const slug=btn.getAttribute('data-slug')||'';if(slug)mainImg.dataset.slug=slug;gallery.querySelectorAll('.pd-thumb-btn').forEach(b=>b.classList.remove('active'));btn.classList.add('active')}})})();
13167
13168(function initMobileSliderCounter(){const slider=document.getElementById('pd-mobile-slider');const counter=document.getElementById('pd-gallery-counter');if(!slider||!counter)return;let ticking=false;slider.addEventListener('scroll',function(){if(!ticking){window.requestAnimationFrame(function(){const slides=slider.querySelectorAll('.pd-mslide');if(!slides.length)return;const containerRect=slider.getBoundingClientRect();const containerCenter=containerRect.left+containerRect.width/2;let currentIdx=0;let minDist=Infinity;
13168slides.forEach((slide,idx)=>{const slideRect=slide.getBoundingClientRect();const slideCenter=slideRect.left+slideRect.width/2;const dist=Math.abs(slideCenter-containerCenter);if(dist<minDist){minDist=dist;currentIdx=idx}});counter.textContent=(currentIdx+1)+'/'+slides.length;ticking=false});ticking=true}},{passive:true})})();
13169
13170/* ================= Simulasi Cicilan ================= */
13171(function initInstallmentList(){const card=document.getElementById('inst-card');if(!card)return;function getBasePrice(){return parseInt(card.getAttribute('data-base-price')||'0',10)||0}function setBasePrice(v){card.setAttribute('data-base-price',String(parseInt(v||'0',10)||0))}const zeroEligible=card.getAttribute('data-zero-smart')==='1';window.initMobile=zeroEligible;const listEl=document.getElementById('inst-month-list');const zeroNote=document.getElementById('inst-zero-note');const tenors=[3,6,12];const rates={BRI:{3:0.975,6:0.975,12:0.975},Mandiri:{3:0.984,6:0.9725,12:0.96},BCA:{3:0.97,6:0.94,12:0.91}};function formatRupiah(num){return 'Rp '+(Math.round(num)).toString().replace(/\B(?=(\d{3})+(?!\d))/g,'.')}function activeBank(){const tab=card.querySelector('.bank-tab.active');return tab?tab.dataset.bank:'BRI'}
13172
13173function _0x4o5p(bank){const a=getBasePrice();listEl.innerHTML='';const b=rates[bank]||{};let c=false;tenors.forEach(m=>{let d=b[m]??1;if(zeroEligible&&(bank==='BRI'||bank==='Mandiri')){d=1;c=true}const e=a/d;const f=m>0?Math.ceil((e/m)/100)*100:0;const g=document.createElement('li');g.className='inst-month-item';g.innerHTML=`<span class="tenor-badge">${m}x</span><span class="amount">${formatRupiah(f)} / bln</span>${zeroEligible&&(bank==='BRI'||bank==='Mandiri')?'<span class="tag tag-zero">0%</span>':''}`;listEl.appendChild(g)});zeroNote.style.display=c?'block':'none'}
13174
13175card.addEventListener('click',function(ev){const a=ev.target.closest('.bank-tab');if(!a)return;card.querySelectorAll('.bank-tab').forEach(b=>{b.classList.remove('active');b.setAttribute('aria-selected','false')});a.classList.add('active');a.setAttribute('aria-selected','true');_0x4o5p(a.dataset.bank)});
13176
13177window.instSetBasePrice=function(newPrice){setBasePrice(newPrice);_0x4o5p(activeBank())};_0x4o5p(activeBank());
13178})();
13179
13180/* ====== Fokus gambar varian ====== */
13181function _0x6q7r(s){return String(s||'').toLowerCase().replace(/[^a-z0-9]+/g,'')}function _0x8s9t(u){try{return new URL(u,window.location.origin).pathname.split('/').pop().toLowerCase()}catch(e){try{return(u||'').split('/').pop().toLowerCase()}catch(_){return ''}}}
13182function _0xau1v(url,altOpt){const a=document.getElementById('pd-main-img'),b=document.getElementById('pd-thumb-gallery'),c=document.getElementById('pd-mobile-slider');if(a&&url){a.src=url;if(altOpt)a.alt=altOpt}if(b){const d=_0x8s9t(url);let e=null;b.querySelectorAll('.pd-thumb-btn').forEach(btn=>{const f=btn.getAttribute('data-full')||'',g=_0x8s9t(f)===d;if(g)e=btn;btn.classList.toggle('active',g)});if(e){const h=e.getAttribute('data-alt')||'',i=e.getAttribute('data-slug')||'';if(h&&a)a.alt=h;if(i&&a)a.dataset.slug=i}}
13183  if (c){
13184    const targetName = _0x8s9t(url);
13185    const slideImg = Array.from(c.querySelectorAll('img')).find(im => _0x8s9t(im.src) === targetName);
13186    if (slideImg){
13187      
13188      try {
13189        const container = c;
13190        const imgRect = slideImg.getBoundingClientRect();
13191        const contRect = container.getBoundingClientRect();
13192        const offsetLeft = slideImg.offsetLeft - container.offsetLeft;
13193        const center = offsetLeft - (container.clientWidth - slideImg.clientWidth) / 2;
13194        container.scrollTo({ left: Math.max(0, center), behavior: 'smooth' });
13195      } catch(_){
13196        
13197        slideImg.scrollIntoView({ behavior:'smooth', inline:'center', block:'nearest' });
13198      }
13199    }
13200  }
13201}
13202function findImageForVariant(variant){
13203  if (!variant || !variant.attrs) return null;
13204  const tokens = [];
13205  try{
13206    Object.values(variant.attrs).forEach(v=>{
13207      if (!v) return;
13208      const raw = String(v);
13209      tokens.push(raw);
13210      tokens.push(normalize(raw));
13211    });
13212  }catch(_){}
13213  if (variant.sku){
13214    tokens.push(String(variant.sku));
13215    tokens.push(normalize(String(variant.sku)));
13216  }
13217  if (variant.image_url) {
13218      tokens.push(String(variant.image_url));
13219  }
13220  let best = null;
13221  let bestScore = -1;
13222
13223  for (const g of (galleryData||[])){
13224    const slugN = normalize(g.slug||'');
13225    const altN  = normalize(g.alt||'');
13226    const file  = _0x8s9t(g.url||'');
13227    const fileN = normalize(file);
13228    const imgU  = g.url;
13229
13230    for (const t of tokens){
13231      const tN = normalize(t);
13232      if (!tN) continue;
13233
13234      let score = -1;
13235      if (imgU === tN) score = 300;
13236      if (slugN && slugN === tN) score = 300;
13237      else if (fileN && fileN.includes(tN)) score = 200;
13238      else if (altN && altN.includes(tN)) score = 120;
13239
13240      if (score > bestScore){
13241        bestScore = score;
13242        best = g;
13243      }
13244      if (bestScore >= 300) break;
13245    }
13246    if (bestScore >= 300) break;
13247  }
13248  return bestScore >= 120 ? best : null;
13249}
13250function _0xey3z(variant){const a=_0xcw2x(variant);if(a&&a.url){_0xau1v(a.url,a.alt||'')}}
13251
13252/* ====== Layout mobile ====== */
13253(function _0xg04h(){const a=document.querySelector('.pd-media-card'),b=document.getElementById('pd-price-block'),c=document.getElementById('pd-price-anchor'),d=document.querySelector('.pd-info-card'),e=document.getElementById('pd-title'),f=document.getElementById('pd-variant-combined'),g=document.getElementById('pd-variants'),h=document.getElementById('pd-variants-anchor'),i=document.getElementById('pd-variant-summary'),j=document.getElementById('pd-summary-anchor'),titleRow=document.querySelector('.pd-title-row'),promoBanner=document.querySelector('.promo-endyear-mini-banner'),compareSection=document.querySelector('.pd-compare-section');if(!a||!b||!c||!d||!e)return;
13254
13255    let pricePlaced = false;
13256    let varPlaced = false;
13257    let titlePlaced = false;
13258    let promoPlaced = false;
13259    const mq = window.matchMedia('(max-width: 640px)');
13260
13261    function apply(){
13262        if (mq.matches) {
13263            if (!titlePlaced && a && titleRow && d.contains(titleRow)) { try{a.appendChild(titleRow);titlePlaced=true;}catch(_){} }
13264            if (!pricePlaced && a && b) { try{a.appendChild(b);pricePlaced=true;}catch(_){} }
13265            if (!varPlaced && f && d && e) {
13266                try{
13267                    f.hidden = false;
13268                    if (i && !f.contains(i)) f.appendChild(i);
13269                    if (g && !f.contains(g)) f.appendChild(g);
13270                    if(d.contains(e))d.insertBefore(f, e);
13271                    varPlaced = true;
13272                }catch(_){}
13273            }
13274            if (!promoPlaced && promoBanner && compareSection && compareSection.parentNode) {
13275                try{
13276                    compareSection.parentNode.insertBefore(promoBanner, compareSection);
13277                    promoPlaced = true;
13278                }catch(_){}
13279            }
13280        } else {
13281            if (titlePlaced && titleRow && d) {
13282                try{
13283                    const vcombined = document.getElementById('pd-variant-combined');
13284                    if (vcombined && d.contains(vcombined)) {
13285                        d.insertBefore(titleRow, vcombined);
13286                    } else {
13287                        d.insertBefore(titleRow, d.firstChild);
13288                    }
13289                    titlePlaced=false;
13290                }catch(_){}
13291            }
13292            if (pricePlaced && b && c && c.parentNode) {
13293                try{c.parentNode.insertBefore(b, c.nextSibling);pricePlaced=false;}catch(_){}
13294            }
13295            if (varPlaced) {
13296                try{
13297                    if (i && j && j.parentNode && !j.parentNode.contains(i)) j.parentNode.insertBefore(i, j.nextSibling);
13298                    if (g && h && h.parentNode && !h.parentNode.contains(g)) h.parentNode.insertBefore(g, h.nextSibling);
13299                    if (f) { f.hidden = true; f.innerHTML = ''; }
13300                    varPlaced = false;
13301                }catch(_){}
13302            }
13303            if (promoPlaced && promoBanner && b) {
13304                try{
13305                    b.appendChild(promoBanner);
13306                    promoPlaced = false;
13307                }catch(_){}
13308            }
13309        }
13310    }
13311
13312    mq.addEventListener ? mq.addEventListener('change', apply) : mq.addListener(apply);
13313    apply();
13314
13315    const fab = document.getElementById('pd-mobile-fab');
13316    if (fab) {
13317        const pid = parseInt(fab.getAttribute('data-pid')||'0',10) || 325;
13318        const btnCart = document.getElementById('mf-cart');
13319
13320        if (btnCart) btnCart.addEventListener('click', function(){
13321            const varInput = document.getElementById('pd-variant-id');
13322            const variantId = varInput ? parseInt(varInput.value||'0',10) : 0;
13323            try { if (window.addToCart) addToCart(pid, 1, variantId); } catch(_){}
13324            const variantText = getVariantSummaryText();
13325            const skuText = (function(){ const el=document.getElementById('pd-sku'); return el?el.textContent.trim():PD_SKU; })();
13326            showToast({
13327                type: 'success',
13328                title: 'Berhasil ditambahkan ke Keranjang',
13329                message: `<strong>${escapeHtml(PD_NAME)}</strong>${variantText?` · <em>${escapeHtml(variantText)}</em>`:''}<br>SKU: ${escapeHtml(skuText)} · Qty: 1`,
13330                duration: 5000,
13331                actions: [
13332                    { label: 'Lihat Keranjang', onClick: ()=> { window.location.href = '/keranjang'; } },
13333                    { label: 'Lanjut Belanja', variant: 'alt', onClick: ()=>{} }
13334                ]
13335            });
13336        });
13337    }
13338})();
13339
13340/* ====== Auto-hide installment banner if sidebar too tall (Desktop only) ====== */
13341(function(){
13342    function adjustSidebarBanner() {
13343        if (window.innerWidth <= 768) return;
13344        const sidebar = document.querySelector('.pd-sidebar');
13345        const infoCard = document.querySelector('.pd-info-card');
13346        const banner = document.querySelector('.pd-side-card.inst-alt-banner.inst-alt-banner--compact');
13347        if (!sidebar || !infoCard || !banner) return;
13348        const sidebarHeight = sidebar.offsetHeight;
13349        const infoCardHeight = infoCard.offsetHeight;
13350        if (sidebarHeight > infoCardHeight) {
13351            banner.style.display = 'none';
13352        } else {
13353            banner.style.display = '';
13354        }
13355    }
13356    if (document.readyState === 'loading') {
13357        document.addEventListener('DOMContentLoaded', adjustSidebarBanner);
13358    } else {
13359        adjustSidebarBanner();
13360    }
13361    let resizeTimer;
13362    window.addEventListener('resize', function() {
13363        clearTimeout(resizeTimer);
13364        resizeTimer = setTimeout(adjustSidebarBanner, 250);
13365    });
13366    const pwpSection = document.getElementById('pwp-deals-section');
13367    if (pwpSection) {
13368        const observer = new MutationObserver(function(mutations) {
13369            let shouldRecheck = false;
13370            mutations.forEach(function(mutation) {
13371                if (mutation.type === 'attributes' && mutation.attributeName === 'hidden') {
13372                    shouldRecheck = true;
13373                } else if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
13374                    shouldRecheck = true;
13375                }
13376            });
13377            if (shouldRecheck) {
13378                setTimeout(adjustSidebarBanner, 100);
13379            }
13380        });
13381        observer.observe(pwpSection, {
13382            attributes: true,
13383            childList: true,
13384            subtree: true
13385        });
13386    }
13387    window.adjustSidebarBanner = adjustSidebarBanner;
13388})();
13389
13390/* ====== Global constants ====== */
13391const IS_DIGITAL=false;
13392const IS_PREORDER=false;
13393
13394/* ====== Variants state (REVISED) ====== */
13395(function initVariants(){
13396const _0x7g8h=[];window.variants=_0x7g8h;
13397window.isPreorderProduct=false;
13398    const attrOrder = [];
13399
13400    if (!variants.length || !attrOrder.length) return;
13401
13402    function comboKey(sel){
13403        return attrOrder.map(a => a + '=' + (sel[a] ?? '')).join('|');
13404    }
13405
13406    const variantMap = {};
13407    variants.forEach(v=>{
13408        const key = comboKey(v.attrs);
13409        variantMap[key] = v;
13410    });
13411
13412    function readSelection(){
13413        const sel = {};
13414        attrOrder.forEach(a=>{
13415            const r = document.querySelector(`.v-group[data-attr="${CSS.escape(a)}"] input[type=radio]:checked`);
13416            sel[a] = r ? r.value : '';
13417        });
13418        return sel;
13419    }
13420
13421    function findVariant(sel){
13422        return variantMap[comboKey(sel)] || null;
13423    }
13424
13425    function autoFillMissing(sel){
13426        let changed = false;
13427        for (const a of attrOrder) {
13428            if (sel[a]) continue;
13429            const possible = [];
13430            const seen = new Set();
13431            variants.forEach(v=>{
13432                let ok = true;
13433                for (const a2 of attrOrder) {
13434                    if (a2 === a) continue;
13435                    if (sel[a2] && v.attrs[a2] !== sel[a2]) { ok = false; break; }
13436                }
13437                if (ok) {
13438                    const val = v.attrs[a];
13439                    if (!seen.has(val)) { seen.add(val); possible.push(val); }
13440                }
13441            });
13442            if (possible.length === 1) {
13443                const val = possible[0];
13444                const input = document.querySelector(`.v-group[data-attr="${CSS.escape(a)}"] input[type=radio][value="${CSS.escape(val)}"]`);
13445                if (input && !input.checked) { input.checked = true; sel[a] = val; changed = true; }
13446            }
13447        }
13448        return changed;
13449    }
13450
13451    function rupiah(n){
13452        n = parseInt(n||0,10)||0;
13453        return 'Rp ' + n.toString().replace(/\B(?=(\d{3})+(?!\d))/g,'.');
13454    }
13455    
13456    function focusGalleryByUrl(imageUrl) {
13457        const isMobile = window.matchMedia && window.matchMedia('(max-width: 640px)').matches;
13458          if (isMobile) {
13459            _0xau1v(imageUrl);
13460            return;
13461          }
13462        
13463          const gallery = document.getElementById('pd-thumb-gallery');
13464          const targetName = _0x8s9t(imageUrl);
13465        
13466          if (gallery) {
13467            const btns = Array.from(gallery.querySelectorAll('.pd-thumb-btn'));
13468            const btn = btns.find(b => _0x8s9t(b.getAttribute('data-full') || '') === targetName);
13469            if (btn) { btn.click(); return; }
13470          }
13471          
13472          _0xau1v(imageUrl);
13473    }
13474    
13475
13476function formatStockLabel(n){n=parseInt(n||0,10)||0;return(n>10)?'10+':String(n)}
13477function updateBranchPopover(v){try{if(!v)return;const a=document.getElementById('sp-qty-meg'),b=document.getElementById('sp-qty-mdf'),c=document.getElementById('sp-qty-bda');if(a)a.textContent=formatStockLabel(Math.max(0,v.smm|0));if(b)b.textContent=formatStockLabel(Math.max(0,v.smf|0));if(c)c.textContent=formatStockLabel(Math.max(0,v.sba|0))}catch(_){}}
13478
13479    function calcVoucherDisc(type,value,price,maxDisc){if(type==='PERCENTAGE'){let d=(price*value)/100;if(maxDisc>0&&d>maxDisc)d=maxDisc;return Math.round(d)}return Math.round(value)}
13480
13481    function applyVariant(v, focusImage = true){
13482        const elCur  = document.getElementById('pd-price-current');
13483        const elOri  = document.getElementById('pd-price-original');
13484        const elDisc = document.getElementById('pd-discount-badge');
13485        const elSku  = document.getElementById('pd-sku');
13486        const elStock= document.getElementById('pd-stock-badge');
13487        const elQty  = document.getElementById('qty');
13488        const btnAdd = document.getElementById('btn-add-cart');
13489        const btnBuy = document.getElementById('btn-buy');
13490        const hidVar = document.getElementById('pd-variant-id');
13491
13492        const mfCart= document.getElementById('mf-cart');
13493        const mfBuy = document.getElementById('mf-buy');
13494        const fab = document.getElementById('pd-mobile-fab');
13495
13496        if (hidVar) hidVar.value = v ? v.id : 0;
13497        if (elCur) elCur.textContent = rupiah(v ? v.final : 0);
13498        if (elSku && v && v.sku) elSku.textContent = v.sku;
13499
13500        if (v && v.ori > v.final) {
13501            if (elOri) { elOri.style.display='inline'; elOri.textContent = rupiah(v.ori); }
13502            if (elDisc) { elDisc.style.display='inline'; elDisc.textContent = '-' + (v.disc||0) + '%'; }
13503        } else {
13504            if (elOri) elOri.style.display='none';
13505            if (elDisc) elDisc.style.display='none';
13506        }
13507
13508        const stock = v ? (parseInt(v.stock||0,10)||0) : 0;
13509        let effectiveStock = stock;
13510        let isActivePreorder = false;
13511        if (window.isPreorderProduct && v && 'preorder_remaining' in v && v.preorder_remaining !== null) {
13512            effectiveStock = parseInt(v.preorder_remaining, 10);
13513            if (isNaN(effectiveStock)) effectiveStock = 0;
13514            isActivePreorder = true;
13515        }
13516        if (elStock){
13517            elStock.classList.remove('in','out');
13518            if (effectiveStock>0){ 
13519                elStock.classList.add('in'); 
13520                elStock.textContent = IS_DIGITAL ? 'Tersedia' : (isActivePreorder ? 'Sisa Slot: ' + effectiveStock : 'Stok: ' + formatStockLabel(effectiveStock)); 
13521            } else { 
13522                elStock.classList.add('out'); 
13523                elStock.textContent = 'Habis'; 
13524            }
13525        }
13526        if (elQty){
13527            elQty.max = String(effectiveStock>0?effectiveStock:0);
13528            if (effectiveStock<=0) { elQty.value = '0'; elQty.disabled = true; }
13529            else { if (parseInt(elQty.value||'1',10) < 1) elQty.value='1'; elQty.disabled=false; }
13530        }
13531        
13532        const shouldDisable = !(v && effectiveStock>0);
13533        const updateButtons = () => {
13534            const btnAddCart = document.getElementById('btn-add-cart');
13535            const btnBuyNow = document.getElementById('btn-buy');
13536            const mobileCart = document.getElementById('mf-cart');
13537            const mobileBuy = document.getElementById('mf-buy');
13538            [btnAddCart, btnBuyNow, mobileCart, mobileBuy].forEac
13538h(btn => {
13539                if (!btn) return;
13540                if (shouldDisable) {
13541                    btn.disabled = true;
13542                    btn.setAttribute('disabled', 'disabled');
13543                    btn.classList.add('stock-disabled');
13544                    btn.setAttribute('data-stock-blocked', 'true');
13545                    btn.setAttribute('data-stock-reason', window.isPreorderProduct ? 'Pre-order habis' : 'Stok habis');
13546                } else {
13547                    btn.disabled = false;
13548                    btn.removeAttribute('disabled');
13549                    btn.classList.remove('stock-disabled');
13550                    btn.removeAttribute('data-stock-blocked');
13551                    btn.removeAttribute('data-stock-reason');
13552                }
13553            });
13554        };
13555        updateButtons();
13556        setTimeout(updateButtons, 10);
13557        setTimeout(updateButtons, 50);
13558        setTimeout(updateButtons, 100);
13559        if (mfBuy && v) {
13560            const voucherBtn = document.getElementById('btn-buy-voucher');
13561            const voucherCode = fab ? fab.dataset.voucherCode : '';
13562            let voucherDiscount = 0;
13563            
13564            if (voucherBtn && voucherCode) {
13565                const vType = voucherBtn.dataset.voucherType;
13566                const vValue = parseFloat(voucherBtn.dataset.voucherValue) || 0;
13567                const vMax = parseFloat(voucherBtn.dataset.voucherMax) || 0;
13568                const finalPrice = v.final || 0;
13569                if (vType && vValue > 0) {
13570                    voucherDiscount = calcVoucherDisc(vType, vValue, finalPrice, vMax);
13571                    if (fab) fab.dataset.voucherValue = voucherDiscount.toString();
13572                }
13573            }
13574            
13575            const finalPrice = v.final || 0;
13576            const textEl = mfBuy.querySelector('.mf-buy-text');
13577            const priceEl = mfBuy.querySelector('.mf-buy-price');
13578            
13579            if (effectiveStock > 0) {
13580                    if (IS_PREORDER) {
13581                        if (textEl) textEl.textContent = 'Pre Order Sekarang';
13582                        if (priceEl) priceEl.textContent = rupiah(finalPrice);
13583                    } else if (voucherCode && voucherDiscount > 0) {
13584                        if (textEl) textEl.textContent = 'Beli dengan Voucher';
13585                        if (priceEl) priceEl.textContent = rupiah(Math.max(0, finalPrice - voucherDiscount));
13586                    } else {
13587                        if (textEl) textEl.textContent = 'Beli Sekarang';
13588                        if (priceEl) priceEl.textContent = rupiah(finalPrice);
13589                    }
13590                } else {
13591                    if (textEl) textEl.textContent = 'Stok Habis';
13592                    if (priceEl) priceEl.textContent = rupiah(finalPrice);
13593                }
13594            }
13595        
13596        const isMobile = window.initMobile ?? 0;
13597        if (window.instSetBasePrice) window.instSetBasePrice(v ? (isMobile == '1' ? v.ori : v.final) : 0);
13598        if (window.updateSoldBySku) window.updateSoldBySku(v.sku);
13599        updateBranchPopover(v);
13600        if (v && v.image_url && focusImage) focusGalleryByUrl(v.image_url);
13601        if (v) {
13602            window.dispatchEvent(new CustomEvent('pd-variant-changed', {
13603                detail: {
13604                    price: v.final,
13605                    originalPrice: v.ori,
13606                    discount: v.disc,
13607                    stock: effectiveStock,
13608                    sku: v.sku,
13609                    variant_id: v.id
13610                }
13611            }));
13612            
13613            const encodedHash = encodeVariantHash(v.id);
13614            if (encodedHash && window.history && window.history.replaceState) {
13615                const newHash = '#v=' + encodedHash;
13616                const newUrl = window.location.pathname + window.location.search + newHash;
13617                window.history.replaceState(null, '', newUrl);
13618            }
13619        }
13620    }
13621
13622    let sel = readSelection();
13623    if (autoFillMissing(sel)) sel = readSelection();
13624
13625    let v = findVariant(sel);
13626    if (!v) {
13627        const candidates = variants.filter(variant=>{
13628            return attrOrder.every(a=>{
13629                if (!sel[a]) return true;
13630                return variant.attrs[a] === sel[a];
13631            });
13632        });
13633        if (candidates.length >= 1) {
13634            v = candidates[0];
13635            attrOrder.forEach(a=> sel[a] = v.attrs[a]);
13636            attrOrder.forEach(a=>{
13637                const val = sel[a];
13638                const input = document.querySelector(`.v-group[data-attr="${CSS.escape(a)}"] input[type=radio][value="${CSS.escape(val)}"]`);
13639                if (input) input.checked = true;
13640            });
13641        }
13642    }
13643
13644    const initSel = readSelection();
13645    const initVar = findVariant(initSel) || variants[0] || null;
13646    if (document.readyState === 'loading') {
13647        document.addEventListener('DOMContentLoaded', () => applyVariant(initVar, false));
13648    } else {
13649        applyVariant(initVar, false);
13650    }
13651    
13652function updateSummary(sel){Object.entries(sel).forEach(([a,val])=>{const el=document.getElementById('vs-'+a);if(el)el.textContent=val||'-'})}
13653    function refreshOptions(sel){
13654        attrOrder.forEach(a=>{
13655            const group = document.querySelector(`.v-group[data-attr="${CSS.escape(a)}"]`);
13656            if (!group) return;
13657            const chips = group.querySelectorAll('.v-chip');
13658            chips.forEach(ch=>{
13659                const val = ch.getAttribute('data-value');
13660                const possible = variants.some(v=>{
13661                    if (v.attrs[a] !== val) return false;
13662                    for (const a2 of attrOrder) {
13663                        if (a2 === a) continue;
13664                        if (sel[a2] && v.attrs[a2] !== sel[a2]) return false;
13665                    }
13666                    return true;
13667                });
13668                const input = ch.querySelector('input[type=radio]');
13669                const disabled = !possible;
13670                if (input) input.disabled = disabled;
13671                ch.classList.toggle('disabled', disabled);
13672
13673                let candidateVariant = null;
13674                if (possible) {
13675                    candidateVariant = variants.find(v=>{
13676                        if (v.attrs[a] !== val) return false;
13677                        for (const a2 of attrOrder) {
13678                            if (a2 === a) continue;
13679                            if (sel[a2] && v.attrs[a2] !== sel[a2]) return false;
13680                        }
13681                        return true;
13682                    });
13683                }
13684                const outOfStock = candidateVariant ? (window.isPreorderProduct && candidateVariant.preorder_remaining !== null ? candidateVariant.preorder_remaining <= 0 : candidateVariant.stock <= 0) : false;
13685                ch.classList.toggle('out', !disabled && outOfStock);
13686                const badge = ch.querySelector('.chip-badge');
13687                if (badge) badge.hidden = !(candidateVariant && outOfStock);
13688            });
13689            group.querySelectorAll('.v-chip').forEach(ch=>{
13690                const input = ch.querySelector('input[type=radio]');
13691                ch.classList.toggle('selected', input && input.checked);
13692            });
13693        });
13694    }
13695    updateSummary(sel);
13696    refreshOptions(sel);
13697    document.querySelectorAll('.v-chip').forEach(chip=>{const input=chip.querySelector('input[type=radio]');if(input&&input.checked){chip.classList.add('selected');}});
13698
13699    const container = document.getElementById('pd-variants');
13700    container && container.addEventListener('change', function(ev){
13701        const radio = ev.target.closest('input[type=radio]');
13702        if (!radio) return;
13703        sel = readSelection();
13704        if (autoFillMissing(sel)) sel = readSelection();
13705        const chosen = findVariant(sel);
13706        applyVariant(chosen || null, true);
13707        
13708        updateSummary(sel);
13709        refreshOptions(sel);
13710    });
13711    
13712window.applyVariant=applyVariant;
13713})();
13714
13715(function cleanBrokenDescImages(){
13716    const containers = [
13717        document.querySelector('.pd-full-desc-body'),
13718        document.querySelector('.pd-short-desc')
13719    ].filter(Boolean);
13720
13721    containers.forEach(root => {
13722        const imgs = root.querySelectorAll('img');
13723        imgs.forEach(img => {
13724            if (img.complete && img.naturalWidth === 0) {
13725                img.remove();
13726                return;
13727            }
13728            img.addEventListener('error', () => {
13729                img.remove();
13730            }, { once:true });
13731        });
13732    });
13733})();
13734
13735/* ====== Scroll To Top ====== */
13736(function _0xi25j(){const a=document.getElementById('pd-scroll-top');if(!a)return;function b(){if(window.matchMedia('(max-width:640px)').matches){const d=document.querySelector('.bottom-nav'),e=document.querySelector('.pd-mobile-fab');if(!d&&!e){setTimeout(b,100);return}const f=d&&window.getComputedStyle(d).display!=='none'&&window.getComputedStyle(d).visibility!=='hidden',g=e&&window.getComputedStyle(e).display!=='none'&&window.getComputedStyle(e).visibility!=='hidden';let h;if(f&&g){h='calc(68px + 54px + env(safe-area-inset-bottom))'}else if(g){h='calc(68px + 8px + env(safe-area-inset-bottom))'}else if(f){h='calc(60px + env(safe-area-inset-bottom))'}else{h='calc(20px + env(safe-area-inset-bottom))'}a.style.setProperty('--scroll-top-bottom',h)}else{a.style.removeProperty('--scroll-top-bottom')}}function c(){const d=window.pageYOffset||document.documentElement.scrollTop||0,e=d>300;a.classList.toggle('visible',e);a.setAttribute('aria-hidden',e?'false':'true');b()}window.addEventListener('scroll',c,{passive:!0});window.addEventListener('resize',b,{passive:!0});setTimeout(b,100);c();a.addEventListener('click',function(){window.scrollTo({top:0,behavior:'smooth'})})})();
13737
13738/* Format stok 10+ */
13739function _0xk67l(n){n=parseInt(n||0,10)||0;return n>10?'10+':String(n)}
13740
13741/* Toggle popover stok per cabang */
13742(function _0xm89n(){const a=document.getElementById('pd-stock-info-btn'),b=document.getElementById('pd-stock-popover'),c=document.getElementById('pd-stock-popover-close');if(!a||!b)return;function d(){if(window.matchMedia('(min-width: 641px)').matches){const e=document.getElementById('pd-stock-badge');if(!e)return;const f=e.getBoundingClientRect(),g=window.pageXOffset||document.documentElement.scrollLeft||0,h=window.pageYOffset||document.documentElement.scrollTop||0;b.style.left=(f.left+g-10)+'px';b.style.top=(f.bottom+h+10)+'px'}else{b.style.left='';b.style.top=''}}
13743function e(){d();b.classList.add('show');a.setAttribute('aria-expanded','true');b.setAttribute('aria-hidden','false');document.addEventListener('click',g,{capture:true});window.addEventListener('resize',d,{passive:true});window.addEventListener('scroll',d,{passive:true})}function f(){b.classList.remove('show');a.setAttribute('aria-expanded','false');b.setAttribute('aria-hidden','true');document.removeEventListener('click',g,{capture:true});window.removeEventListener('resize',d);window.removeEventListener('scroll',d)}function g(h){if(b.contains(h.target)||a.contains(h.target))return;f()}
13744a.addEventListener('click',function(h){h.preventDefault();b.classList.contains('show')?f():e()});if(c)c.addEventListener('click',f)})();
13745
13746/* ===== Share Product Modal ===== */
13747(function(){const a=document.getElementById('pd-share-btn'),z=document.getElementById('mf-share'),b=document.getElementById('pd-share-modal-overlay'),c=document.getElementById('pd-share-close'),d=document.getElementById('pd-share-copy-url-btn'),e=document.getElementById('pd-share-url-input');if((!a&&!z)||!b)return;const f="https://sunnypork.com/",g="Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Win 11 Home/Eclipse Black/3Y",h="Rp 20.789.000";const discountPercent=1;let i='';if(discountPercent>0){i=`🔥 Lagi Promo Nih! Diskon ${discountPercent}%\n\n✨ ${g}\n💰 Cuma ${h} aja!\n\n🛒 Buruan cek sebelum kehabisan!\n👉 `}else{i=`✨ Ada barang bagus nih!\n\n📦 ${g}\n💸 Harga: ${h}\n\n🛍️ Yuk, langsung cek!\n👉 `}function j(){b.classList.add('show');document.body.style.overflow='hidden'}function k(){b.classList.remove('show');document.body.style.overflow=''}if(a)a.addEventListener('click',j);if(z)z.addEventListener('click',j);if(c)c.addEventListener('click',k);b.addEventListener('click',function(l){if(l.target===b)k()});document.addEventListener('keydown',function(l){if(l.key==='Escape'&&b.classList.contains('show'))k()});b.addEventListener('click',function(l){const m=l.target.closest('[data-share]');if(!m)return;const n=m.getAttribute('data-share'),o=encodeURIComponent(f),p=e
13747ncodeURIComponent(i),q=encodeURIComponent(g);let r='';switch(n){case 'whatsapp':r=`https://wa.me/?text=${p}${o}`;break;case 'facebook':r=`https://www.facebook.com/sharer/sharer.php?u=${o}`;break;case 'twitter':r=`https://twitter.com/intent/tweet?text=${p}${o}`;break;case 'telegram':r=`https://t.me/share/url?url=${o}&text=${p}`;break;case 'email':r=`mailto:?subject=${encodeURIComponent('🔥 Cek Produk Keren Ini!')}&body=${p}${o}`;window.location.href=r;k();return;case 'copy':s();return;case 'pdf':const ps=document.querySelector('.product-single');if(ps){ps.setAttribute('data-print-date',new Date().toLocaleDateString('id-ID',{year:'numeric',month:'long',day:'numeric'}))}k();setTimeout(function(){window.print();try{if(window.ga4Ecomm&&window.ga4Ecomm.trackEvent)window.ga4Ecomm.trackEvent('share',{method:'pdf_export',content_type:'product',item_id:"SB0B3T"})}catch(_){}},300);return}if(r){window.open(r,'_blank','noopener,noreferrer');try{if(window.ga4Ecomm&&window.ga4Ecomm.trackEvent)window.ga4Ecomm.trackEvent('share',{method:n,content_type:'product',item_id:"SB0B3T"})}catch(_){}}});function s(){if(!e)return;e.select();e.setSelectionRange(0,99999);try{if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(f).then(function(){t()}).catch(function(){document.execCommand('copy');t()})}else{document.execCommand('copy');t()}}catch(u){console.error('Failed to copy:',u)}}function t(){if(!d)return;const v=d.innerHTML;d.classList.add('copied');d.innerHTML='<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg><span class="pd-share-copy-text">Tersalin!</span>';if(window.showToast)showToast({type:'success',title:'✨ Link Tersalin!',message:'Link produk berhasil disalin. Yuk bagikan ke teman!',duration:3000});setTimeout(function(){d.classList.remove('copied');d.innerHTML=v},2000)}if(d)d.addEventListener('click',s);if(navigator.share){const shareHandler=function(w){w.preventDefault();navigator.share({title:g,text:i+f,url:f}).catch(function(x){console.log('Native share cancelled or failed:',x)})};if(a)a.addEventListener('dblclick',shareHandler);if(z)z.addEventListener('dblclick',shareHandler)}})();
13748
13749/* ===== Embedding variants (server → client) ===== */
13750(function(){
13751  window.__PD_VARIANTS_WITH_BRANCH__ = [];
13752})();
13753
13754/* ===== Helper: update angka popover ===== */
13755function updateStockPopoverForVariant(v){
13756  try{
13757    const elMeg = document.getElementById('sp-qty-meg');
13758    const elMdf = document.getElementById('sp-qty-mdf');
13759    const elBda = document.getElementById('sp-qty-bda');
13760
13761    if (typeof v?.smm === 'number' && elMeg) elMeg.textContent = formatStockLabel(Math.max(0, v.smm|0));
13762    if (typeof v?.smf === 'number' && elMdf) elMdf.textContent = formatStockLabel(Math.max(0, v.smf|0));
13763    if (typeof v?.sba === 'number' && elBda) elBda.textContent = formatStockLabel(Math.max(0, v.sba|0));
13764  }catch(_){}
13765}
13766
13767/* ===== applyVariant: 10+ dan update popover ===== */
13768(function patchApplyVariant(){
13769  if (typeof applyVariant !== 'function') return;
13770  const original = applyVariant;
13771  window.applyVariant = function(v){
13772    original(v);
13773    
13774    try {
13775      const elStock = document.getElementById('pd-stock-badge');
13776      if (elStock){
13777        let txt = elStock.textContent || '';
13778        let m = txt.match(/\d+/);
13779        if (m) {
13780          const n = parseInt(m[0]||'0',10)||0;
13781          elStock.textContent = (n>0) ? ('Stok: ' + formatStockLabel(n)) : 'Habis';
13782        }
13783      }
13784    } catch(_){}
13785if(window.__PD_VARIANTS_WITH_BRANCH__&&v){const vv=window.__PD_VARIANTS_WITH_BRANCH__.find(x=>x.id===v.id)||v;updateStockPopoverForVariant(vv)}
13786  };
13787})();
13788
13789(function initCurrentVariantBranch(){
13790  const list = window.__PD_VARIANTS_WITH_BRANCH__ || [];
13791  if (!list.length) return;
13792  const hid = document.getElementById('pd-variant-id');
13793  const vid = hid ? parseInt(hid.value||'0',10) : 0;
13794  const current = list.find(x => x.id === vid) || list[0] || null;
13795  if (current) updateStockPopoverForVariant(current);
13796})();
13797
13798(function(){
13799    if (!window.ga4Ecomm) return;
13800    const catSegs = ["Komputer & Laptop","Laptop","Gaming"];
13801    window.ga4Ecomm.viewItem({
13802      currency: 'IDR',
13803      value: 20789000,
13804      item_id: "SB0B3T",
13805      item_name: "Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Win 11 Home/Eclipse Black/3Y",
13806      item_brand: "Lenovo ",
13807      item_category: catSegs && catSegs.length ? catSegs.join(' / ') : ''
13808    });
13809  })();
13810  
13811(function patchApplyVariantFocus(){
13812  if (typeof applyVariant !== 'function') return;
13813  const original = applyVariant;
13814  window.applyVariant = function(v, focusImage = true){
13815    
13816    original(v, focusImage);
13817    
13818    if (focusImage && v && v.image_url) {
13819      _0xau1v(v.image_url);
13820    }
13821  };
13822})();
13823
13824(function() {
13825  var soldMap = [];
13826  var soldTotalLabel = "Terjual 2";
13827
13828  function formatSoldLabel(qty) {
13829    if (qty >= 1000000) {
13830      var v = Math.floor((qty / 100000) ) / 10;
13831      return 'Terjual ' + (String(v).replace('.', ',')) + 'jt+';
13832    }
13833    if (qty >= 1000) {
13834      var v2 = Math.floor((qty / 100) ) / 10;
13835      return 'Terjual ' + (String(v2).replace('.', ',')) + 'rb+';
13836    }
13837    if (qty >= 100) {
13838      var v2 = Math.floor((qty / 100) ) * 100;
13839      return 'Terjual ' + (String(v2).replace('.', ',')) + '+';
13840    }
13841    if (qty >= 10) {
13842      var v2 = Math.floor((qty / 10) ) * 10;
13843      return 'Terjual ' + (String(v2).replace('.', ',')) + '+';
13844    }
13845    return 'Terjual ' + (qty.toLocaleString('id-ID'));
13846  }
13847
13848  function updateSoldLabel(labelText) {
13849    var el = document.querySelector('#pd-sold-inline, [data-role="sold-chip"]');
13850    if (el) el.textContent = labelText;
13851  }
13852
13853  window.updateSoldBySku = function(sku) {
13854    if (!sku || typeof sku !== 'string') {
13855      updateSoldLabel(soldTotalLabel);
13856      return;
13857    }
13858    var qty = soldMap[sku] || 0;
13859    updateSoldLabel(formatSoldLabel(qty));
13860  };
13861
13862    updateSoldLabel(soldTotalLabel);
13863  })();
13864
13865(function(){const h=document.getElementById('pd-sticky-header');if(!h)return;const p=document.getElementById('pd-price-anchor');const ac=document.getElementById('pd-sticky-add-cart');const by=document.getElementById('pd-sticky-buy');let lt=window.scrollY;let tk=false;function u(){if(!p)return;const r=p.getBoundingClientRect();const s=window.scrollY;if(r.top<0){h.classList.add('visible')}else{h.classList.remove('visible')}lt=s;tk=false}function rt(){if(!tk){window.requestAnimationFrame(u);tk=true}}window.addEventListener('scroll',rt,{passive:true});u();function sy(){const ma=document.getElementById('btn-add-cart');const mb=document.getElementById('btn-buy');if(ac&&ma){ac.disabled=ma.disabled}if(by&&mb){by.disabled=mb.disabled}}sy();const ob=new MutationObserver(sy);const ma=document.getElementById('btn-add-cart');const mb=document.getElementById('btn-buy');if(ma)ob.observe(ma,{attributes:true,attributeFilter:['disabled']});if(mb)ob.observe(mb,{attributes:true,attributeFilter:['disabled']});const ss=document.getElementById('pd-sticky-share-btn');const ms=document.getElementById('pd-share-btn');if(ss&&ms){ss.addEventListener('click',function(e){e.preventDefault();ms.click()})}if(ac){ac.addEventListener('click',function(e){e.preventDefault();if(ac.disabled)return;const m=document.getElementById('btn-add-cart');if(m&&!m.disabled){m.click()}})}if(by){by.addEventListener('click',function(e){e.preventDefault();if(by.disabled)return;const m=document.getElementById('btn-buy');if(m&&!m.disabled&&window.buyNow){window.buyNow(e)}})}
13866function getChatProductData(){const varInput=document.getElementById('pd-variant-id');const variantId=varInput?parseInt(varInput.value||'0',10):0;const variantSummary=getVariantSummaryText();const priceEl=document.getElementById('pd-price-current');const currentPrice=priceEl?priceEl.textContent.trim():'Rp 20.789.000';const pd={id:325,name:"Lenovo Legion Go 8ASP2 - 83N0000RID \/AMD Ryzen Z2 Extreme\/32GB\/1TB SSD\/AMD Radeon Graphics\/8.8\" WUXGA OLED Touch\/Win 11 Home\/Eclipse Black\/3Y",price:currentPrice,image:"\/uploads\/products\/lenovo-legion-go-8asp2-83n0000rid.webp",url:window.location.href};if(variantId>0){pd.variant_id=variantId}if(variantSummary){pd.variant_summary=variantSummary}return pd}
13867const ch=document.getElementById('pd-sticky-chat');if(ch){ch.addEventListener('click',function(e){e.preventDefault();if(!window.plazaChat){console.warn('Chat widget not loaded');return}window.plazaChat.attachProduct(getChatProductData());window.plazaChat.openWidget()})}const dch=document.getElementById('btn-chat-icon');if(dch){dch.addEventListener('click',function(e){e.preventDefault();if(!window.plazaChat){console.warn('Chat widget not loaded');return}window.plazaChat.attachProduct(getChatProductData());window.plazaChat.openWidget()})}const mch=document.getElementById('mf-chat');if(mch){mch.addEventListener('click',function(e){e.preventDefault();if(!window.plazaChat){console.warn('Chat widget not loaded');return}window.plazaChat.attachProduct(getChatProductData());window.plazaChat.openWidget()})}window.addEventListener('pd-variant-changed',function(e){const d=e.detail||{};if(d.price!==undefined){const sp=document.getElementById('pd-sticky-price');if(sp){sp.textContent=formatRupiah(d.price)}}if(d.originalPrice!==undefined&&d.originalPrice>d.price){const so=document.getElementById('pd-sticky-price-original');if(so){so.textContent=formatRupiah(d.originalPrice);so.style.display=''}}else{const so=document.getElementById('pd-sticky-price-original');if(so){so.style.display='none'}}if(d.discount!==undefined&&d.discount>0){const sd=document.getElementById('pd-sticky-discount');if(sd){sd.textContent='-'+d.discount+'%';sd.style.display=''}}else{const sd=document.getElementById('pd-sticky-discount');if(sd){sd.style.display='none'}}const hs=d.stock>0;if(ac){ac.disabled=!hs}if(by){by.disabled=!hs}});
13867function formatRupiah(n){return'Rp'+Math.floor(n).toLocaleString('id-ID')}})();
13868
13869(function(){const t=document.getElementById('pdv-toggle-btn');const v=document.getElementById('pd-voucher-section');if(t&&v){const h=v.querySelectorAll('.pdv-min-hidden');const n=h.length;t.addEventListener('click',function(){const e=t.classList.toggle('expanded');h.forEach(function(i){if(e){i.classList.remove('pdv-min-hidden')}else{i.classList.add('pdv-min-hidden')}});t.textContent=e?'Tutup':'+'+n})}const m=document.getElementById('voucher-detail-modal');const mc=document.getElementById('voucher-modal-close');const ma=document.getElementById('voucher-modal-cancel');const mcp=document.getElementById('voucher-modal-copy');if(m){const bb=document.querySelectorAll('.pdv-badge-btn');bb.forEach(function(btn){btn.addEventListener('click',function(){if(btn.disabled)return;const c=btn.getAttribute('data-voucher-code');const nm=btn.getAttribute('data-voucher-name');const dl=btn.getAttribute('data-discount-label');const bl=btn.getAttribute('data-badge-label');const mp=parseInt(btn.getAttribute('data-min-purchase'));const md=parseInt(btn.getAttribute('data-max-discount'));const vu=btn.getAttribute('data-valid-until');document.getElementById('voucher-modal-badge').innerHTML='<span class="vmb-value">'+dl+'</span><span class="vmb-label">'+bl+'</span>';document.getElementById('voucher-modal-code').textContent=c;document.getElementById('voucher-modal-name').textContent=nm;const me=document.getElementById('voucher-modal-min');if(mp>0){me.style.display='';document.getElementById('voucher-modal-min-val').textContent=fr(mp)}else{me.style.display='none'}const xe=document.getElementById('voucher-modal-max');if(md>0){xe.style.display='';document.getElementById('voucher-modal-max-val').textContent=fr(md)}else{xe.style.display='none'}if(vu){const d=new Date(vu);const ft=d.toLocaleDateString('id-ID',{day:'numeric',month:'long',year:'numeric',hour:'2-digit',minute:'2-digit'});document.getElementById('voucher-modal-valid-val').textContent=ft}mcp.setAttribute('data-code',c);m.classList.add('show');document.body.style.overflow='hidden'})});function cm(){m.classList.remove('show');document.body.style.overflow=''}if(mc)mc.addEventListener('click',cm);if(ma)ma.addEventListener('click',cm);m.addEventListener('click',function(e){if(e.target===m)cm()});if(mcp){mcp.addEventListener('click',function(){const c=mcp.getAttribute('data-code');if(!c)return;if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(c).then(function(){smc()}).catch(function(err){fc(c)})}else{fc(c)}})}}const b=document.querySelectorAll('.pdv-min-copy');b.forEach(function(btn){btn.addEventListener('click',function(){if(btn.disabled)return;const code=btn.getAttribute('data-code');if(!code)return;if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(code).then(function(){sc(btn)}).catch(function(err){fc(code,btn)})}else{fc(code,btn)}})});function fc(text,btn){const tx=document.createElement('textarea');tx.value=text;tx.style.position='fixed';tx.style.opacity='0';document.body.appendChild(tx);tx.select();try{document.execCommand('copy');if(btn)sc(btn);else smc()}catch(err){}document.body.removeChild(tx)}function sc(btn){const o=btn.textContent;btn.classList.add('copied');btn.textContent='✓ Tersalin';setTimeout(function(){btn.classList.remove('copied');btn.textContent=o},2000)}function smc(){const o=mcp.textContent;mcp.textContent='✓ Kode Tersalin';mcp.style.background='#22c55e';setTimeout(function(){mcp.textContent=o;mcp.style.background=''},2000)}function fr(a){return'Rp'+Math.floor(a).toLocaleString('id-ID')}})();
13870</script>
vendor: 2 bytes, line 13870
13870
13871<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
13872window.PD_ID=325;window.PD_NAME=_0x3c4d;window.PD_SKU=_0x5e6f;window.PD_CATEGORY_PATH="Komputer & Laptop/Laptop/Gaming";window.PD_BEST_VOUCHER=null;
13873window.IS_DIGITAL=IS_DIGITAL;
13874window.isPreorderProduct=false;
13875window.PD_PREORDER_DATA=null;
13876</script>
13876
13877
13878<script data-inlined="pd-lightbox.js">
13879// PlazaIT Product Lightbox (vanilla, no CDN) — v4 (swipe-down to close)
13880(function(){
13881  const state = {
13882    items: [],
13883    open: false,
13884    idx: 0,
13885    scale: 1,
13886    minScale: 1,
13887    maxScale: 4,
13888    tx: 0, ty: 0,
13889    isDragging: false,
13890    dragStartX: 0, dragStartY: 0,
13891    baseTx: 0, baseTy: 0,
13892    touchStartX: 0, touchStartY: 0,
13893    lastTap: 0,
13894    swipeMode: 'none', // 'none' | 'h' | 'v'
13895    totalDx: 0, totalDy: 0
13896  };
13897
13898  let root, imgEl, counterEl, btnPrev, btnNext, btnClose, canvas;
13899
13900  const ORIGIN = (location.origin || (location.protocol + '//' + location.host));
13901
13902  function absUrl(u){
13903    if (!u) return '';
13904    try {
13905      if (/^https?:\/\//i.test(u)) return u;
13906      return new URL(u, ORIGIN).href;
13907    } catch(_) { return u; }
13908  }
13909  function fileName(u){
13910    try { return new URL(u, ORIGIN).pathname.split('/').pop().toLowerCase(); }
13911    catch(_){ return String(u||'').split('/').pop().toLowerCase(); }
13912  }
13913
13914  function buildItems(){
13915    const gd = (window.galleryData || []).filter(Boolean);
13916    if (gd.length){
13917      state.items = gd.map(g => ({
13918        src: absUrl(g.url), alt: g.alt || '', slug: g.slug || '', w: g.w || 1200, h: g.h || 1200
13919      })).filter(it => !!it.src);
13920      return;
13921    }
13922    const thumbs = document.querySelectorAll('.pd-thumb-btn');
13923    state.items = Array.from(thumbs).map(btn => {
13924      const raw = btn.getAttribute('data-full') || '';
13925      return { src: absUrl(raw), alt: btn.getAttribute('data-alt') || '', slug: btn.getAttribute('data-slug') || '', w: 1200, h: 1200 };
13926    }).filter(it => !!it.src);
13927  }
13928
13929  function ensureUI(){
13930    if (root) return;
13931    root = document.createElement('div');
13932    root.className = 'pd-lb';
13933    root.id = 'pd-lightbox';
13934    root.setAttribute('role','dialog');
13935    root.setAttribute('aria-modal','true');
13936    root.setAttribute('aria-hidden','true');
13937    root.setAttribute('tabindex','-1');
13938
13939    // Canvas terlebih dulu (z-index di CSS membuat tombol di atas)
13940    root.innerHTML = `
13941      <div class="pd-lb-canvas"><img class="pd-lb-img" id="pd-lb-img" alt=""></div>
13942      <button class="pd-lb-close" aria-label="Tutup" title="Tutup">&times;</button>
13943      <button class="pd-lb-prev" aria-label="Sebelumnya" title="Sebelumnya">&#10094;</button>
13944      <button class="pd-lb-next" aria-label="Berikutnya" title="Berikutnya">&#10095;</button>
13945      <div class="pd-lb-counter" id="pd-lb-counter">1 / 1</div>
13946    `;
13947    document.body.appendChild(root);
13948
13949    imgEl     = root.querySelector('#pd-lb-img');
13950    counterEl = root.querySelector('#pd-lb-counter');
13951    btnPrev   = root.querySelector('.pd-lb-prev');
13952    btnNext   = root.querySelector('.pd-lb-next');
13953    btnClose  = root.querySelector('.pd-lb-close');
13954    canvas    = root.querySelector('.pd-lb-canvas');
13955
13956    // Button clicks
13957    btnPrev.addEventListener('click', (e)=>{ e.stopPropagation(); prev(); });
13958    btnNext.addEventListener('click', (e)=>{ e.stopPropagation(); next(); });
13959    btnClose.addEventListener('click', (e)=>{ e.stopPropagation(); close(); });
13960
13961    // Klik backdrop untuk tutup
13962    root.addEventListener('click', (e)=>{ if (e.target === root) close(); });
13963
13964    // Keyboard global
13965    document.addEventListener('keydown', (e)=>{
13966      if (!state.open) return;
13967      if (e.key === 'Escape') { e.preventDefault(); close(); }
13968      if (e.key === 'ArrowRight') { e.preventDefault(); next(); }
13969      if (e.key === 'ArrowLeft')  { e.preventDefault(); prev(); }
13970    });
13971
13972    // Wheel zoom (desktop)
13973    root.addEventListener('wheel', (e)=>{
13974      if (!state.open) return;
13975      e.preventDefault();
13976      const delta = -Math.sign(e.deltaY) * 0.2;
13977      zoomAt(delta, e.clientX, e.clientY);
13978    }, { passive: false });
13979
13980    // Drag pan (mouse)
13981    canvas.addEventListener('mousedown', (e)=>{
13982      if (state.scale <= 1) return;
13983      state.isDragging = true;
13984      state.dragStartX = e.clientX;
13985      state.dragStartY = e.clientY;
13986      state.baseTx = state.tx;
13987      state.baseTy = state.ty;
13988      e.preventDefault();
13989    });
13990    window.addEventListener('mousemove', (e)=>{
13991      if (!state.isDragging) return;
13992      const dx = e.clientX - state.dragStartX;
13993      const dy = e.clientY - state.dragStartY;
13994      setTranslate(state.baseTx + dx, state.baseTy + dy);
13995    });
13996    window.addEventListener('mouseup', ()=>{ state.isDragging = false; });
13997
13998    // Touch: swipe navigate (H), swipe-down close (V), double-tap zoom, drag pan
13999    canvas.addEventListener('touchstart', (e)=>{
14000      if (!state.open) return;
14001      if (e.touches.length === 1) {
14002        const t = e.touches[0];
14003        state.touchStartX = t.clientX;
14004        state.touchStartY = t.clientY;
14005        state.totalDx = 0;
14006        state.totalDy = 0;
14007        state.swipeMode = 'none';
14008
14009        state.isDragging = state.scale > 1;
14010        state.dragStartX = t.clientX;
14011        state.dragStartY = t.clientY;
14012        state.baseTx = state.tx;
14013        state.baseTy = state.ty;
14014
14015        // Double-tap toggle zoom
14016        const now = Date.now();
14017        if (now - state.lastTap < 300) {
14018          if (state.scale > 1) resetTransform(); else zoomAt(+1, t.clientX, t.clientY, 2);
14019          state.lastTap = 0;
14020          e.preventDefault();
14021          return;
14022        }
14023        state.lastTap = now;
14024      }
14025    }, { passive: true });
14026
14027    canvas.addEventListener('touchmove', (e)=>{
14028      if (!state.open) return;
14029
14030      // Drag pan saat zoom
14031      if (state.isDragging && e.touches.length === 1 && state.scale > 1) {
14032        e.preventDefault();
14033        const t = e.touches[0];
14034        const dx = t.clientX - state.dragStartX;
14035        const dy = t.clientY - state.dragStartY;
14036        setTranslate(state.baseTx + dx, state.baseTy + dy);
14037        return;
14038      }
14039
14040      // Gesture saat scale <= 1
14041      if (state.scale <= 1 && e.touches.length === 1) {
14042        const t = e.touches[0];
14043        const dx = t.clientX - state.touchStartX;
14044        const dy = t.clientY - state.touchStartY;
14045        state.totalDx = dx; state.totalDy = dy;
14046
14047        // Tentukan mode swipe di awal gerakan
14048        if (state.swipeMode === 'none') {
14049          const absX = Math.abs(dx), absY = Math.abs(dy);
14050          if (absY > 12 && absY > absX * 1.3) {
14051            state.swipeMode = 'v'; // swipe vertical (untuk close)
14052          } else if (absX > 12 && absX > absY * 1.3) {
14053            state.swipeMode = 'h'; // swipe horizontal (ganti slide)
14054          }
14055        }
14056
14057        if (state.swipeMode === 'v') {
14058          // Ikuti jari, kurangi opacity untuk feedback
14059          e.preventDefault();
14060          setRootDrag(dy);
14061        } else if (state.swipeMode === 'h') {
14062          // Biarkan sampai touchend untuk menentukan next/prev (tidak perlu preventDefault di sini)
14063        }
14064      }
14065    }, { passive: false });
14066
14067    canvas.addEventListener('touchend', (e)=>{
14068      if (!state.open) return;
14069
14070      if (state.scale <= 1) {
14071        const dx = state.totalDx;
14072        const dy = state.totalDy;
14073
14074        if (state.swipeMode === 'v') {
14075          const TH = 80; // threshold jarak piksel untuk menutup
14076          if (Math.abs(dy) > TH) {
14077            // Animasi keluar arah swipe
14078            animateClose(dy);
14079          } else {
14080            // Snap back
14081            resetRootDrag(true);
14082          }
14083        } else if (state.swipeMode === 'h') {
14084          if (Math.abs(dx) > 50) { dx < 0 ? next() : prev(); }
14085        }
14086      }
14087
14088      state.isDragging = false;
14089      state.swipeMode = 'none';
14090      state.totalDx = 0;
14091      state.totalDy = 0;
14092    });
14093  }
14094
14095  function setImage(idx){
14096    if (!state.items.length) return;
14097    state.idx = (idx + state.items.length) % state.items.length;
14098    const it = state.items[state.idx];
14099    imgEl.src = it.src;
14100    imgEl.alt = it.alt || '';
14101    counterEl.textContent = (state.idx + 1) + ' / ' + state.items.length;
14102    resetTransform();
14103    preloadNeighbors();
14104  }
14105
14106  function resetTransform(){
14107    state.scale = 1;
14108    state.tx = 0; state.ty = 0;
14109    applyTransform();
14110  }
14111
14112  function setTranslate(tx, ty){
14113    state.tx = tx; state.ty = ty;
14114    applyTransform();
14115  }
14116
14117  function applyTransform(){
14118    imgEl.style.transform = `translate3d(${state.tx}px, ${state.ty}px, 0) scale(${state.scale})`;
14119  }
14120
14121  function zoomAt(delta, clientX, clientY, snapTo){
14122    const old = state.scale;
14123    let next = (typeof snapTo === 'number') ? snapTo : (old + delta);
14124    next = Math.max(state.minScale, Math.min(state.maxScale, next));
14125
14126    if (next !== old) {
14127      const rect = imgEl.getBoundingClientRect();
14128      const cx = clientX - rect.left - rect.width/2;
14129      const cy = clientY - rect.top  - rect.height/2;
14130      state.tx -= cx * (next/old - 1);
14131      state.ty -= cy * (next/old - 1);
14132      state.scale = next;
14133      applyTransform();
14134    }
14135  }
14136
14137  function preloadNeighbors(){
14138    if (state.items.length < 2) return;
14139    const nextIdx = (state.idx + 1) % state.items.length;
14140    const prevIdx = (state.idx - 1 + state.items.length) % state.items.length;
14141    [nextIdx, prevIdx].forEach(i=>{
14142      const img = new Image(); img.src = state.items[i].src;
14143    });
14144  }
14145
14146  // ----- Swipe-down visual helpers -----
14147  function setRootDrag(dy){
14148    const damped = dy * 0.6; // redam gerakan agar lembut
14149    const abs = Math.abs(dy);
14150    const fade = Math.max(0, Math.min(1, 1 - abs / 300)); // transparansi sesuai jarak
14151    root.style.transform = `translate3d(0, ${damped}px, 0)`;
14152    root.style.opacity = String(0.95 * fade + 0.05); // jangan jadi 0 total sebelum close
14153  }
14154  function resetRootDrag(withAnim){
14155    if (withAnim) {
14156      root.style.transition = 'transform .18s ease, opacity .18s ease';
14157      root.style.transform = 'translate3d(0,0,0)';
14158      root.style.opacity = '1';
14159      setTimeout(()=>{ root.style.transition = ''; }, 190);
14160    } else {
14161      root.style.transition = '';
14162      root.style.transform = '';
14163      root.style.opacity = '';
14164    }
14165  }
14166  function animateClose(dy){
14167    root.style.transition = 'transform .18s ease, opacity .18s ease';
14168    const off = (dy > 0 ? window.innerHeight : -window.innerHeight);
14169    root.style.transform = `translate3d(0, ${off}px, 0)`;
14170    root.style.opacity = '0';
14171    setTimeout(()=>{ root.style.transition = ''; close(); }, 180);
14172  }
14173
14174  function setSiblingsInert(makeInert){
14175    // Inert semua saudara root (elemen lain di body) agar fokus tidak lari
14176    const nodes = Array.from(document.body.children).filter(el => el !== root);
14177    nodes.forEach(el=>{
14178      if (makeInert) {
14179        if (!el.hasAttribute('inert')) el.setAttribute('inert','');
14180      } else {
14181        if (el.hasAttribute('inert')) el.removeAttribute('inert');
14182      }
14183    });
14184  }
14185
14186  function open(idx){
14187    if (!state.items.length) buildItems();
14188    if (!state.items.length) return;
14189    ensureUI();
14190
14191    // Reset efek drag visual
14192    resetRootDrag(false);
14193
14194    document.body.style.overflow = 'hidden';
14195    root.classList.add('show');
14196    root.removeAttribute('aria-hidden'); // hindari aria-hidden saat fokus child
14197    setSiblingsInert(true);
14198
14199    state.open = true;
14200    setImage(typeof idx === 'number' ? idx : 0);
14201
14202    // Fokuskan dialog agar keyboard pasti tertangkap
14203    try { root.focus(); } catch(_){}
14204  }
14205
14206  function close(){
14207    state.open = false;
14208    root.classList.remove('show');
14209    root.setAttribute('aria-hidden','true');
14210    setSiblingsInert(false);
14211    document.body.style.overflow = '';
14212
14213    // Bersihkan transform/opacity
14214    resetRootDrag(false);
14215  }
14216
14217  function next(){ setImage(state.idx + 1); }
14218  function prev(){ setImage(state.idx - 1); }
14219
14220  function findIndexByUrl(url){
14221    const target = fileName(absUrl(url));
14222    const idx = state.items.findIndex(it => fileName(it.src) === target);
14223    return idx >= 0 ? idx : 0;
14224  }
14225
14226  function initPdLightbox(){
14227    buildItems();
14228    ensureUI();
14229
14230    // Klik gambar utama → buka LB di index yang sama
14231    const mainImg = document.getElementById('pd-main-img');
14232    if (mainImg) {
14233      mainImg.style.cursor = 'zoom-in';
14234      mainImg.addEventListener('click', ()=>{
14235        open(findIndexByUrl(mainImg.src));
14236      });
14237    }
14238
14239    // Klik gambar slider mobile → buka LB di gambar yang diketuk
14240    const slider = document.getElementById('pd-mobile-slider');
14241    if (slider) {
14242      slider.addEventListener('click', (e)=>{
14243        const im = e.target && e.target.tagName === 'IMG' ? e.target : e.target.closest('img');
14244        if (!im) return;
14245        open(findIndexByUrl(im.src));
14246      });
14247    }
14248
14249    // (Opsional) double-click pada thumbnail untuk buka langsung
14250    const thumbs = document.querySelectorAll('.pd-thumb-btn');
14251    thumbs.forEach((btn)=>{
14252      let last = 0;
14253      btn.addEventListener('click', ()=>{
14254        const now = Date.now();
14255        if (now - last < 300) {
14256          const src = btn.getAttribute('data-full') || '';
14257          open(findIndexByUrl(absUrl(src)));
14258          last = 0;
14259        } else {
14260          last = now;
14261        }
14262      });
14263    });
14264
14265    // Ekspos API
14266    window.PDLightbox = { open, close, next, prev, findIndexByUrl };
14267  }
14268
14269  if (document.readyState === 'loading') {
14270    document.addEventListener('DOMContentLoaded', initPdLightbox);
14271  } else {
14272    initPdLightbox();
14273  }
14274})();
14275</script>
vendor: 2 bytes, line 14275
14275
14276<script data-inlined="cart.js">
14277(function(){
14278  function $(s,ctx=document){ return ctx.querySelector(s); }
14279  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
14280  
14281  // CSRF token helper
14282  function getCsrfToken() {
14283    const metaTag = document.querySelector('meta[name="csrf-token"]');
14284    return metaTag ? metaTag.getAttribute('content') : '';
14285  }
14286  
14287  function guessPdMeta(){
14288    const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : ($('#pd-title')?.textContent || '');
14289    const sku  = (function(){ const el=$('#pd-sku'); return el ? el.textContent.trim() : (window.PD_SKU||''); })();
14290    // SECURITY FIX: Remove price from client-side metadata (will be validated server-side)
14291    const imgEl = $('#pd-main-img');
14292    const image_url = imgEl ? (imgEl.getAttribute('src') || '') : '';
14293    return { name, sku, image_url }; // price removed for security
14294  }
14295
14296  async function addToCart(productId, qty=1, variantId=0, metaOpt){
14297    const meta = Object.assign(guessPdMeta(), metaOpt||{});
14298    const payload = {
14299      product_id: parseInt(productId||0,10) || 0,
14300      qty: parseInt(qty||1,10) || 1,
14301      variant_id: parseInt(variantId||0,10) || 0,
14302      meta
14303    };
14304    
14305    // Check if user is logged in
14306    const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
14307                      document.body.classList.contains('logged-in') ||
14308                      window.userLoggedIn === true ||
14309                      document.body.dataset.userLoggedIn === 'true';
14310    
14311    // Skip localStorage untuk logged user - mereka pakai database
14312    if (!isLoggedIn && window.guestCart) {
14313      try {
14314        window.guestCart.add(payload.product_id, payload.qty, payload.variant_id, meta);
14315      } catch (e) {
14316        console.warn('Failed to add to localStorage cart:', e);
14317      }
14318    }
14319    
14320    const res = await fetch('/api/cart/add.php', {
14321      method: 'POST',
14322      headers: { 
14323        'Content-Type':'application/json',
14324        'X-CSRF-Token': getCsrfToken()
14325      },
14326      credentials: 'same-origin',
14327      body: JSON.stringify({
14328        ...payload,
14329        csrf_token: getCsrfToken()
14330      }),
14331    });
14332    const data = await res.json().catch(()=>({ ok:false, error:'Gagal menambahkan ke keranjang' }));
14333    if (!res.ok || !data.ok) {
14334      const msg = data && data.error ? data.error : 'Gagal menambahkan ke keranjang';
14335      throw new Error(msg);
14336    }
14337
14338    // Update badge di navbar dengan data dari response
14339    try {
14340      const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
14341      if (el && data.cart && typeof data.cart.items_count === 'number') {
14342        el.textContent = String(data.cart.items_count);
14343        el.classList.toggle('is-empty', data.cart.items_count <= 0);
14344      }
14345    } catch(_){}
14346
14347    // Siarkan event global (dipakai cart-badge.js dan halaman lain)
14348    try {
14349      document.dispatchEvent(new CustomEvent('cart:updated', { detail: data.cart || {} }));
14350    } catch(_){}
14351
14352    // Update CartBadge dengan data dari response (lebih reliable)
14353    try { 
14354      if (window.CartBadge && window.CartBadge.set) {
14355        window.CartBadge.set(data.cart.items_count||0);
14356      }
14357    } catch(_){}
14358
14359    // Tambahan: refresh cart counter setelah delay kecil untuk memastikan data tersinkronisasi
14360    setTimeout(() => {
14361      if (window.updateCartCounter) {
14362        window.updateCartCounter();
14363      }
14364    }, 100);
14365
14366    return data;
14367  }
14368
14369  // Function to update cart counter from server
14370  async function updateCartCounter(){
14371    try {
14372      const res = await fetch('/api/cart/get.php', { credentials:'same-origin' });
14373      const data = await res.json().catch(()=>({ok:false}));
14374      if (data.ok && typeof data.items_count === 'number') {
14375        const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
14376        if (el) {
14377          el.textContent = String(data.items_count);
14378          el.classList.toggle('is-empty', data.items_count <= 0);
14379        }
14380        // Dispatch event
14381        try {
14382          document.dispatchEvent(new CustomEvent('cart:updated', { detail: { items_count: data.items_count } }));
14383        } catch(_){}
14384      }
14385    } catch (err) {
14386      console.log('Could not update cart counter:', err);
14387    }
14388  }
14389
14390  // Function to manually merge guest cart (if needed)
14391  async function mergeGuestCart(){
14392    try {
14393      const res = await fetch('/api/cart/merge-guest.php', {
14394        method: 'POST',
14395        headers: { 
14396          'Content-Type': 'application/json',
14397          'X-CSRF-Token': getCsrfToken()
14398        },
14399        credentials: 'same-origin',
14400        body: JSON.stringify({ csrf_token: getCsrfToken() })
14401      });
14402      const data = await res.json().catch(()=>({ok:false}));
14403      
14404      if (data.ok && data.merged_items > 0) {
14405        console.log(`Guest cart merged: ${data.merged_items} items`);
14406        // Update cart counter after merge
14407        await updateCartCounter();
14408        return data;
14409      }
14410      return data;
14411    } catch (err) {
14412      console.log('Could not merge guest cart:', err);
14413      return { ok: false, error: err.message };
14414    }
14415  }
14416
14417  window.addToCart = addToCart;
14418  window.updateCartCounter = updateCartCounter;
14419  window.mergeGuestCart = mergeGuestCart;
14420
14421  // Optional: auto-bind untuk tombol apapun yang punya data-addtocart
14422  document.addEventListener('click', async (e)=>{
14423    const btn = e.target.closest('[data-addtocart]');
14424    if (!btn) return;
14425    e.preventDefault();
14426    if (btn.disabled) return;
14427
14428    const pid = parseInt(btn.getAttribute('data-pid')||'0',10);
14429    const vid = parseInt(btn.getAttribute('data-vid')||'0',10);
14430    const qty = parseInt(btn.getAttribute('data-qty')||'1',10) || 1;
14431
14432    try {
14433      const data = await addToCart(pid, qty, vid);
14434      if (window.showToast) {
14435        const meta = guessPdMeta();
14436        window.showToast({
14437          type:'success',
14438          title:'Berhasil ditambahkan',
14439          message:`<strong>${esc(meta.name)}</strong>${meta.sku?` · <em>SKU: ${esc(meta.sku)}</em>`:''}<br>Qty: ${qty}`,
14440          duration: 3800,
14441          actions: [
14442            { label:'Lihat Keranjang', onClick: ()=> { window.location.href='/keranjang'; } },
14443            { label:'Lanjut Belanja', variant:'alt', onClick: ()=>{} }
14444          ]
14445        });
14446      }
14447    } catch (err) {
14448      if (window.showToast) {
14449        window.showToast({ type:'warn', title:'Gagal', message: String(err?.message||'Gagal menambahkan'), duration: 4200 });
14450      } else {
14451        console.error(err);
14452      }
14453    }
14454  });
14455})();
14456</script>
vendor: 2 bytes, line 14456
14456
14457<script data-inlined="stock-validator-vanilla.js">
14458/**
14459 * Stock Validation - Vanilla JS Version
14460 * 
14461 * This script provides client-side stock validation without jQuery dependency
14462 * - Works with native JavaScript and fetch API
14463 * - Graceful fallback for older browsers
14464 * - Same functionality as jQuery version
14465 */
14466
14467window.StockValidator = (function() {
14468    'use strict';
14469
14470    const config = {
14471        apiEndpoint: '/api/stock/check.php',
14472        modalSelector: '#stock-modal-overlay',  // We create our own modal
14473        loadingClass: 'loading-stock',
14474        disabledClass: 'stock-disabled',
14475        debug: true  // Enable debug logging
14476    };
14477
14478    /**
14479     * Cross-browser event listener helper
14480     */
14481    function addEventListener(element, event, handler) {
14482        if (element.addEventListener) {
14483            element.addEventListener(event, handler);
14484        } else if (element.attachEvent) {
14485            element.attachEvent('on' + event, handler);
14486        }
14487    }
14488
14489    /**
14490     * Get stock information from embedded variant data (instant, no AJAX needed)
14491     */
14492    function getStockFromEmbeddedData(variantId) {
14493        // Check if embedded variant data exists
14494        if (!window.__PD_VARIANTS_WITH_BRANCH__ || !Array.isArray(window.__PD_VARIANTS_WITH_BRANCH__)) {
14495            return null;
14496        }
14497        
14498        const variants = window.__PD_VARIANTS_WITH_BRANCH__;
14499        
14500        // SPECIAL CASE: Simple product (no variants) - empty array means simple product
14501        if (variants.length === 0) {
14502            // For simple products, we cannot determine stock from embedded data
14503            // Return null to force API validation
14504            return null;
14505        }
14506        
14507        // If no variant specified, check base product stock (all variants)
14508        if (!variantId || variantId <= 0) {
14509            // For base product, check if ANY variant has stock
14510            const isPreorder = window.isPreorderProduct || false;
14511            let hasAnyStock = false;
14512            let totalStock = 0;
14513            
14514            if (isPreorder) {
14515                // For pre-order, check preorder_remaining
14516                hasAnyStock = variants.some(v => {
14517                    if ('preorder_remaining' in v && v.preorder_remaining !== null) {
14518                        return v.preorder_remaining > 0;
14519                    }
14520                    return v.stock > 0;
14521                });
14522                totalStock = variants.reduce((sum, v) => {
14523                    if ('preorder_remaining' in v && v.preorder_remaining !== null) {
14524                        return sum + (v.preorder_remaining || 0);
14525                    }
14526                    return sum + (v.stock || 0);
14527                }, 0);
14528            } else {
14529                hasAnyStock = variants.some(v => v.stock > 0);
14530                totalStock = variants.reduce((sum, v) => sum + (v.stock || 0), 0);
14531            }
14532            
14533            return {
14534                hasStock: hasAnyStock,
14535                stock: totalStock,
14536                sku: 'Base Product',
14537                variant: null
14538            };
14539        }
14540        
14541        // Find specific variant
14542        const variant = variants.find(v => v.id === variantId);
14543        
14544        if (!variant) {
14545            return {
14546                hasStock: false,
14547                stock: 0,
14548                sku: 'Unknown Variant',
14549                variant: null
14550            };
14551        }
14552        
14553        // For pre-order products, check preorder_remaining instead of stock
14554        const isPreorder = window.isPreorderProduct || false;
14555        let hasStock = false;
14556        let availableStock = 0;
14557        
14558        if (isPreorder && 'preorder_remaining' in variant && variant.preorder_remaining !== null) {
14559            availableStock = parseInt(variant.preorder_remaining, 10);
14560            if (isNaN(availableStock)) availableStock = 0;
14561            hasStock = availableStock > 0;
14562        } else {
14563            availableStock = variant.stock || 0;
14564            hasStock = availableStock > 0;
14565        }
14566        
14567        return {
14568            hasStock: hasStock,
14569            stock: availableStock,
14570            sku: variant.sku,
14571            variant: variant
14572        };
14573    }
14574
14575    /**
14576     * Cross-browser AJAX helper
14577     */
14578    function makeRequest(url, options) {
14579        return new Promise((resolve, reject) => {
14580            // Try fetch first (modern browsers)
14581            if (typeof fetch !== 'undefined') {
14582                const fetchOptions = {
14583                    method: options.method || 'POST',
14584                    headers: {
14585                        'Content-Type': 'application/x-www-form-urlencoded',
14586                    },
14587                    body: options.data ? new URLSearchParams(options.data).toString() : null
14588                };
14589
14590                fetch(url, fetchOptions)
14591                    .then(response => {
14592                        if (!response.ok) {
14593                            throw new Error(`HTTP ${response.status}`);
14594                        }
14595                        return response.json();
14596                    })
14597                    .then(resolve)
14598                    .catch(reject);
14599            } else {
14600                // Fallback to XMLHttpRequest
14601                const xhr = new XMLHttpRequest();
14602                xhr.open(options.method || 'POST', url);
14603                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
14604                
14605                xhr.onload = function() {
14606                    if (xhr.status >= 200 && xhr.status < 300) {
14607                        try {
14608                            resolve(JSON.parse(xhr.responseText));
14609                        } catch (e) {
14610                            reject(new Error('Invalid JSON response'));
14611                        }
14612                    } else {
14613                        reject(new Error(`HTTP ${xhr.status}`));
14614                    }
14615                };
14616                
14617                xhr.onerror = function() {
14618                    reject(new Error('Network error'));
14619                };
14620                
14621                const data = options.data ? new URLSearchParams(options.data).toString() : null;
14622                xhr.send(data);
14623            }
14624        });
14625    }
14626
14627    /**
14628     * Check stock via AJAX
14629     */
14630    function checkStock(productId, variantId, qty) {
14631        return makeRequest(config.apiEndpoint, {
14632            method: 'POST',
14633            data: {
14634                product_id: productId,
14635                variant_id: variantId || 0,
14636                qty: qty || 1
14637            }
14638        });
14639    }
14640
14641    /**
14642     * Create and show stock alert modal using modal.css structure
14643     */
14644    function showStockModal(stockInfo, action = 'add to cart') {
14645        let title, message, type, iconClass;
14646
14647        // Handle both string and object formats
14648        if (typeof stockInfo === 'string') {
14649            title = 'Stok Tidak Tersedia';
14650            message = stockInfo;
14651            type = 'warning';
14652            iconClass = 'warning';
14653        } else if (stockInfo && typeof stockInfo === 'object') {
14654            if (!stockInfo.is_active) {
14655                title = 'Produk Tidak Tersedia';
14656                message = 'Maaf, produk ini sudah tidak aktif atau telah dihapus dari katalog.';
14657                type = 'danger';
14658                iconClass = 'danger';
14659            } else if (!stockInfo.is_in_stock) {
14660                title = 'Stok Habis';
14661                message = `<strong>${stockInfo.product_name || 'Produk ini'}</strong> sedang habis stok. Silakan pilih varian lain atau tunggu restock.`;
14662                if (stockInfo.sku) {
14663                    message += `<br><small style="color: #64748b;">SKU: ${stockInfo.sku}</small>`;
14664                }
14665                type = 'warning';
14666                iconClass = 'warning';
14667            } else if (!stockInfo.can_add_to_cart) {
14668                title = 'Stok Tidak Mencukupi';
14669                message = `Stok <strong>${stockInfo.product_name || 'produk'}</strong> tidak mencukupi.<br>`;
14670                message += `Tersedia: <strong>${stockInfo.available_stock}</strong>, diminta: <strong>${stockInfo.requested_qty}</strong><br>`;
14671                message += `Silakan kurangi jumlah atau pilih varian lain.`;
14672                type = 'warning';
14673                iconClass = 'warning';
14674            } else {
14675                title = 'Stok Tidak Tersedia';
14676                message = stockInfo.message || 'Produk ini tidak dapat ditambahkan ke keranjang saat ini.';
14677                type = 'warning';
14678                iconClass = 'warning';
14679            }
14680        } else {
14681            title = 'Stok Tidak Tersedia';
14682            message = 'Produk ini tidak dapat ditambahkan ke keranjang saat ini.';
14683            type = 'warning';
14684            iconClass = 'warning';
14685        }
14686
14687        // Create modal HTML using modal.css structure
14688        const modalHTML = `
14689            <div class="modal-overlay" id="stock-modal-overlay">
14690                <div class="modal-dialog">
14691                    <div class="modal-header">
14692                        <div class="modal-icon ${iconClass}">
14693                            ${getIconSVG(iconClass)}
14694                        </div>
14695                        <h3 class="modal-title">${title}</h3>
14696                    </div>
14697                    <div class="modal-body">
14698                        <p class="modal-message">${message}</p>
14699                        <div class="modal-actions">
14700                            <button type="button" class="modal-btn modal-btn-secondary" data-action="close">
14701                                Tutup
14702                            </button>
14703                            <button type="button" class="modal-btn modal-btn-primary" data-action="reload">
14704                                Muat Ulang
14705                            </button>
14706                        </div>
14707                    </div>
14708                </div>
14709            </div>
14710        `;
14711
14712        // Remove existing modal if any
14713        const existingModal = document.getElementById('stock-modal-overlay');
14714        if (existingModal) {
14715            existingModal.remove();
14716        }
14717
14718        // Add modal to body
14719        document.body.insertAdjacentHTML('beforeend', modalHTML);
14720        
14721        // Show modal with animation
14722        const modal = document.getElementById('stock-modal-overlay');
14723        setTimeout(() => {
14724            modal.classList.add('show');
14725        }, 10);
14726
14727        // Add event listeners to modal buttons
14728        const closeBtn = modal.querySelector('[data-action="close"]');
14729        const reloadBtn = modal.querySelector('[data-action="reload"]');
14730        
14731        if (closeBtn) {
14732            addEventListener(closeBtn, 'click', closeStockModal);
14733        }
14734        
14735        if (reloadBtn) {
14736            addEventListener(reloadBtn, 'click', function() {
14737                closeStockModal();
14738                window.location.reload();
14739            });
14740        }
14741
14742        // Close modal when clicking overlay
14743        addEventListener(modal, 'click', function(event) {
14744            if (event.target === modal) {
14745                closeStockModal();
14746            }
14747        });
14748
14749        // Auto-close after 8 seconds
14750        setTimeout(() => {
14751            closeStockModal();
14752        }, 8000);
14753    }
14754
14755    /**
14756     * Get icon SVG based on type
14757     */
14758    function getIconSVG(type) {
14759        const icons = {
14760            danger: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>',
14761            warning: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
14761<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>',
14762            success: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>',
14763            info: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>'
14764        };
14765        return icons[type] || icons.info;
14766    }
14767
14768    /**
14769     * Close stock modal
14770     */
14771    function closeStockModal() {
14772        const modal = document.getElementById('stock-modal-overlay');
14773        if (modal) {
14774            modal.classList.remove('show');
14775            setTimeout(() => {
14776                modal.remove();
14777            }, 200);
14778        }
14779    }
14780
14781    // Make closeStockModal globally available
14782    if (typeof window !== 'undefined') {
14783        window.closeStockModal = closeStockModal;
14784    }
14785
14786    /**
14787     * Get element data attribute with fallback
14788     */
14789    function getDataAttribute(element, attr) {
14790        if (element.dataset) {
14791            return element.dataset[attr];
14792        } else {
14793            return element.getAttribute('data-' + attr);
14794        }
14795    }
14796
14797    /**
14798     * Validate stock before cart action
14799     */
14800    async function validateBeforeCart(productId, variantId, qty, action = 'add') {
14801        try {
14802            // SMART VALIDATION: For simple products, check if we already have cached stock info
14803            // If buttons are enabled and cached info shows stock available, skip API validation
14804            if (window._currentStockInfo && window._currentStockInfo.can_add_to_cart) {
14805        
14806                return true;
14807            }
14808            
14809            // For variant products or when cached info is not available, do API validation
14810            const stockInfo = await checkStock(productId, variantId, qty);
14811            
14812            if (!stockInfo.ok) {
14813                showStockModal({ 
14814                    message: stockInfo.error || 'Gagal mengecek stok',
14815                    is_active: false 
14816                }, action);
14817                return false;
14818            }
14819
14820            if (!stockInfo.can_add_to_cart) {
14821                showStockModal(stockInfo, action);
14822                return false;
14823            }
14824
14825            return true; // Stock available, proceed
14826
14827        } catch (error) {
14828            console.error('Stock validation error:', error);
14829            showStockModal({ 
14830                message: 'Terjadi kesalahan saat mengecek stok. Silakan coba lagi.',
14831                is_active: false 
14832            }, action);
14833            return false;
14834        }
14835    }
14836
14837    /**
14838     * Handle button click with stock validation
14839     */
14840    function handleButtonClick(event, action) {
14841        const button = event.target;
14842        
14843        
14844        // FIRST CHECK: Is button marked as stock-blocked? (Anti-tampering)
14845        if (button.hasAttribute('data-stock-blocked')) {
14846            event.preventDefault();
14847            event.stopImmediatePropagation();
14848            
14849            const reason = button.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
14850            showStockModal(reason);
14851            
14852            // Show security warning
14853            if (window.showToast) {
14854                window.showToast({
14855                    type: 'warning',
14856                    title: 'Aksi Tidak Diizinkan',
14857                    message: 'Produk ini sedang habis stok. Silakan pilih varian lain.',
14858                    duration: 3000
14859                });
14860            }
14861            return false;
14862        }
14863        
14864        // SECOND CHECK: Real-time stock validation against cached stock info
14865        if (window._currentStockInfo && !window._currentStockInfo.can_add_to_cart) {
14866            event.preventDefault();
14867            event.stopImmediatePropagation();
14868            
14869            showStockModal(window._currentStockInfo.message || 'Stok tidak tersedia');
14870            
14871            // Re-enforce button disabling
14872            button.disabled = true;
14873            button.style.opacity = '0.5';
14874            button.style.cursor = 'not-allowed';
14875            button.setAttribute('data-stock-blocked', 'true');
14876            
14877            return false;
14878        }
14879        
14880        // Skip if already processing
14881        if (button.classList.contains(config.loadingClass)) {
14882            event.preventDefault();
14883            return false;
14884        }
14885
14886        // Get product data from button attributes or form
14887        let productId = parseInt(getDataAttribute(button, 'productId') || getDataAttribute(button, 'product-id') || 0);
14888        let variantId = parseInt(getDataAttribute(button, 'variantId') || getDataAttribute(button, 'variant-id') || 0);
14889        let qty = parseInt(getDataAttribute(button, 'qty') || 1);
14890
14891        // Try to get from global variables (set by product detail page)
14892        if (productId <= 0 && typeof window.PD_ID !== 'undefined') {
14893            productId = parseInt(window.PD_ID || 0);
14894        }
14895
14896        // Try to get from parent form if not in button
14897        if (productId <= 0) {
14898            const form = button.closest ? button.closest('form') : null;
14899            if (form) {
14900                const pidAttr = form.getAttribute('data-pid');
14901                const pidInput = form.querySelector('[name="product_id"]');
14902                const vidInput = form.querySelector('[name="variant_id"]');
14903                const qtyInput = form.querySelector('[name="qty"]');
14904                
14905                if (pidAttr) productId = parseInt(pidAttr);
14906                if (pidInput) productId = parseInt(pidInput.value || 0);
14907                if (vidInput) variantId = parseInt(vidInput.value || 0);
14908                if (qtyInput) qty = parseInt(qtyInput.value || 1);
14909            }
14910        }
14911
14912        // CRITICAL: Always get current variant from selector (overrides button attributes)
14913        // This ensures we validate against the currently selected variant, not cached button data
14914        const variantSelector = document.getElementById('pd-variant-id');
14915        if (variantSelector) {
14916            const currentVariantId = parseInt(variantSelector.value || 0);
14917            if (currentVariantId > 0) {
14918                variantId = currentVariantId;
14919            }
14920        }
14921
14922
14923        if (productId <= 0) {
14924            return true; // Let default handler deal with it
14925        }
14926
14927        // Prevent default and validate stock
14928        event.preventDefault();
14929        event.stopPropagation();
14930        
14931        // Show loading state
14932        button.classList.add(config.loadingClass);
14933        const originalDisabled = button.disabled;
14934        button.disabled = true;
14935        
14936        
14937        validateBeforeCart(productId, variantId, qty, action)
14938            .then(isValid => {
14939                if (isValid) {
14940                    // Stock OK, proceed with original action
14941                    if (action === 'cart' && typeof window.addToCart === 'function') {
14942                        window.addToCart(productId, qty, variantId);
14943                    } else if (action === 'buy' && typeof window.buyNow === 'function') {
14944                        // Call buyNow from pd-buynow.js without parameters (it gets data internally)
14945                        window.buyNow();
14946                    } else {
14947                        // Add fallback cart/buy logic here if needed
14948                    }
14949                }
14950            })
14951            .catch(error => {
14952            })
14953            .finally(() => {
14954                // Remove loading state
14955                button.classList.remove(config.loadingClass);
14956                button.disabled = originalDisabled;
14957            });
14958
14959        return false;
14960    }
14961
14962    /**
14963     * Handle mobile button click with stock validation
14964     */
14965    function handleMobileButtonClick(event, action) {
14966        const button = event.target;
14967        
14968        
14969        // FIRST CHECK: Is button marked as stock-blocked? (Anti-tampering)
14970        if (button.hasAttribute('data-stock-blocked')) {
14971            event.preventDefault();
14972            event.stopImmediatePropagation();
14973            
14974            const reason = button.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
14975            showStockModal(reason);
14976            
14977            // Show security warning
14978            if (window.showToast) {
14979                window.showToast({
14980                    type: 'warning',
14981                    title: 'Aksi Tidak Diizinkan',
14982                    message: 'Produk ini sedang habis stok. Silakan pilih varian lain.',
14983                    duration: 3000
14984                });
14985            }
14986            return false;
14987        }
14988        
14989        // SECOND CHECK: Real-time stock validation against cached stock info
14990        if (window._currentStockInfo && !window._currentStockInfo.can_add_to_cart) {
14991            event.preventDefault();
14992            event.stopImmediatePropagation();
14993            
14994            showStockModal(window._currentStockInfo.message || 'Stok tidak tersedia');
14995            
14996            // Re-enforce button disabling
14997            button.disabled = true;
14998            button.style.opacity = '0.5';
14999            button.style.cursor = 'not-allowed';
15000            button.setAttribute('data-stock-blocked', 'true');
15001            
15002            return false;
15003        }
15004        
15005        // Skip if already processing
15006        if (button.classList.contains(config.loadingClass)) {
15007            event.preventDefault();
15008            return false;
15009        }
15010
15011        // Get product data from mobile FAB container
15012        let productId = 0;
15013        let variantId = 0;
15014        let qty = 1;
15015
15016        // Try to get from mobile FAB data-pid
15017        const mobileFab = document.getElementById('pd-mobile-fab');
15018        if (mobileFab) {
15019            const pidAttr = getDataAttribute(mobileFab, 'pid');
15020            if (pidAttr) productId = parseInt(pidAttr);
15021        }
15022
15023        // Try to get from global variables (set by product detail page)
15024        if (productId <= 0 && typeof window.PD_ID !== 'undefined') {
15025            productId = parseInt(window.PD_ID || 0);
15026        }
15027
15028        // CRITICAL: Always get current variant from selector (real-time)
15029        // This ensures we validate against the currently selected variant
15030        const variantSelector = document.getElementById('pd-variant-id');
15031        if (variantSelector) {
15032            variantId = parseInt(variantSelector.value || 0);
15033        }
15034
15035        // Try to get quantity from quantity input
15036        const qtySelector = document.querySelector('[name="qty"]');
15037        if (qtySelector) {
15038            qty = parseInt(qtySelector.value || 1);
15039        }
15040        
15041        // Ensure minimum quantity is 1
15042        qty = Math.max(1, qty);
15043
15044
15045        if (productId <= 0) {
15046            return true; // Let default handler deal with it
15047        }
15048
15049        // Event already prevented at interceptor level, just proceed with validation
15050        
15051        // Show loading state
15052        button.classList.add(config.loadingClass);
15053        const originalDisabled = button.disabled;
15054        const originalText = button.textContent;
15055        button.disabled = true;
15056        button.textContent = action === 'cart' ? 'Validasi...' : 'Validasi...';
15057        
15058        
15059        // PREEMPTIVE BLOCKING: Set blocking flags BEFORE validation to prevent race conditions
15060        if (window.addToCart && typeof window.addToCart === 'function') {
15061            window.addToCart._stockValidatorBlocked = true;
15062        }
15063        
15064        // Also preemptively block other cart functions
15065        const originalAddToCartSubmit = window.addToCartSubmit;
15066        const originalPdAddToCartSubmit = window._pdAddToCartSubmit;
15067        
15068        // Temporarily block ALL cart functions during validation
15069        window.addToCartSubmit = (...args) => {
15070            return Promise.resolve(false);
15071        };
15072        
15073        if (originalPdAddToCartSubmit) {
15074            window._pdAddToCartSubmit = (...args) => {
15075                return Promise.resolve(false);
15076            };
15077        }
15078        
15079        validateBeforeCart(productId, variantId, qty, action)
15080            .then(isValid => {
15081                if (isValid) {
15082                    // Stock OK, proceed with original action
15083                    
15084                    if (action === 'cart') {
15085                        // For mobile cart, we need to recreate the original behavior
15086                        // Strategy: Try multiple methods to ensure cart add + toast success
15087                        
15088                        
15089                        let cartAddSuccess = false;
15090                        let errorMessage = '';
15091                        
15092                        // Method 1: Try original addToCart function
15093                        const originalAddToCart = window.addToCart && window.addToCart._original;
15094                        if (originalAddToCart && !cartAddSuccess) {
15095                            try {
15096                                const result = originalAddToCart(productId, qty, variantId);
15097                                
15098                                // Handle both sync and async returns
15099                                if (result && typeof result.then === 'function') {
15100                                    result.then(() => {
15101                                        cartAddSuccess = true;
15102                                    }).catch(err => {
15103                                        errorMessage = err.message || 'addToCart failed';
15104                                    });
15105                                } else {
15106                                    cartAddSuccess = true;
15107                                }
15108                            } catch (error) {
15109                                errorMessage = error.message || 'addToCart exception';
15110                            }
15111                        }
15112                        
15113                        // Method 2: Try pd-addtocart.js function (backup method)
15114                        if (typeof window._pdAddToCartSubmit === 'function' && !cartAddSuccess) {
15115                            try {
15116                                
15117                                // Create fake form for pd-addtocart.js
15118                                const fakeForm = document.createElement('form');
15119                                const qtyInput = document.createElement('input');
15120                                qtyInput.name = 'qty';
15121                                qtyInput.value = qty;
15122                                fakeForm.appendChild(qtyInput);
15123                                
15124                                // Set variant selector value for pd-addtocart to read
15125                                const variantSelector = document.getElementById('pd-variant-id');
15126                                if (variantSelector) {
15127                                    variantSelector.value = variantId;
15128                                }
15129                                
15130                                const fakeEvent = { 
15131                                    preventDefault: () => {},
15132                                    target: fakeForm
15133                                };
15134                                
15135                                // Call pd-addtocart.js function (this should handle toast internally)
15136                                const result = window._pdAddToCartSubmit(fakeEvent, productId);
15137                                if (result && typeof result.then === 'function') {
15138                                    result.then(() => {
15139                                        cartAddSuccess = true;
15140                                    });
15141                                } else {
15142                                    cartAddSuccess = true;
15143                                }
15144                            } catch (error) {
15145                            }
15146                        }
15147                        
15148                        // Method 3: Direct addToCart call (fallback)
15149                        if (typeof window.addToCart === 'function' && !cartAddSuccess) {
15150                            try {
15151                                const result = window.addToCart(productId, qty, variantId);
15152                                if (result && typeof result.then === 'function') {
15153                                    result.then(() => {
15154                                        cartAddSuccess = true;
15155                                    });
15156                                } else {
15157                                    cartAddSuccess = true;
15158                                }
15159                            } catch (error) {
15160                            }
15161                        }
15162                        
15163                        // Always show success toast for mobile (regardless of which method worked)
15164                        // This ensures user gets feedback for successful cart addition
15165                        setTimeout(() => {
15166                            if (window.showToast) {
15167                                
15168                                // Get product details for toast
15169                                const getVariantSummaryText = () => {
15170                                    const vs = document.getElementById('pd-variant-summary');
15171                                    if (!vs) return '';
15172                                    const items = Array.from(vs.querySelectorAll('.vs-item strong'))
15173                                        .map(el => el.textContent.trim())
15174                                        .filter(Boolean);
15175                                    return items.length ? items.join(' / ') : '';
15176                                };
15177                                
15178                                const escapeHtml = (s) => String(s||'').replace(/[&<>"']/g, m=>({
15179                                    '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'
15180                                }[m]));
15181                                
15182                                const variantText = getVariantSummaryText();
15183                                const skuText = (function(){ 
15184                                    const el = document.getElementById('pd-sku'); 
15185                                    return el ? el.textContent.trim() : (window.PD_SKU || 'N/A'); 
15186                                })();
15187                                const productName = window.PD_NAME || 'Product';
15188                                
15189                                window.showToast({
15190                                    type: 'success',
15191                                    title: 'Berhasil ditambahkan ke Keranjang',
15192                                    message: `<strong>${escapeHtml(productName)}</strong>${variantText ? ` · <em>${escapeHtml(variantText)}</em>` : ''}<br>SKU: ${escapeHtml(skuText)}
15192 · Qty: ${qty}`,
15193                                    duration: 5000,
15194                                    actions: [
15195                                        { 
15196                                            label: 'Lihat Keranjang', 
15197                                            onClick: () => { window.location.href = '/keranjang'; }
15198                                        },
15199                                        { 
15200                                            label: 'Lanjut Belanja', 
15201                                            variant: 'alt', 
15202                                            onClick: () => {} 
15203                                        }
15204                                    ]
15205                                });
15206                            } else {
15207                            }
15208                        }, 100); // Small delay to ensure cart operation completes first
15209                        
15210                    } else if (action === 'buy') {
15211                        // For mobile buy, call buyNow
15212                        if (typeof window.buyNow === 'function') {
15213                            window.buyNow();
15214                        }
15215                    }
15216                } else {
15217                    // Stock validation FAILED - absolutely NO cart function calls allowed
15218                    
15219                    // DO NOT CALL ANY CART FUNCTIONS - only show modal (already handled by validateBeforeCart)
15220                }
15221                
15222                // Always restore functions after validation (success or failure)
15223                setTimeout(() => {
15224                    // Remove blocking flag
15225                    if (window.addToCart && typeof window.addToCart === 'function') {
15226                        window.addToCart._stockValidatorBlocked = false;
15227                    }
15228                    
15229                    // Restore other functions
15230                    if (originalAddToCartSubmit && typeof originalAddToCartSubmit === 'function') {
15231                        window.addToCartSubmit = originalAddToCartSubmit;
15232                    }
15233                    if (originalPdAddToCartSubmit && typeof originalPdAddToCartSubmit === 'function') {
15234                        window._pdAddToCartSubmit = originalPdAddToCartSubmit;
15235                    }
15236                    
15237                }, 1000); // Shorter timeout since we want to restore quickly after success
15238            })
15239            .catch(error => {
15240                // On error, also block the action
15241            })
15242            .finally(() => {
15243                // Remove loading state
15244                button.classList.remove(config.loadingClass);
15245                button.disabled = originalDisabled;
15246                button.textContent = originalText;
15247            });
15248
15249        return false;
15250    }
15251
15252    /**
15253     * Intercept cart buttons
15254     */
15255    function interceptCartButtons() {
15256
15257        
15258        // CRITICAL: Remove existing mobile cart handlers from product-detail.php
15259        setTimeout(() => {
15260            const mobileCartBtn = document.getElementById('mf-cart');
15261            if (mobileCartBtn) {
15262                // Clone the button to remove ALL existing event listeners
15263                const newBtn = mobileCartBtn.cloneNode(true);
15264                mobileCartBtn.parentNode.replaceChild(newBtn, mobileCartBtn);
15265
15266                
15267                // Add our own handler to the new button
15268                newBtn.addEventListener('click', function(event) {
15269                    event.preventDefault();
15270                    event.stopPropagation();
15271                    event.stopImmediatePropagation();
15272                    handleMobileButtonClick(event, 'cart');
15273                    return false;
15274                }, true);
15275            }
15276        }, 50);
15277        
15278        // Note: Add to cart buttons are now handled via form submission override (addToCartSubmit)
15279        // This provides better integration with existing pd-addtocart.js toast system
15280
15281        // Buy now buttons - updated selectors (desktop + mobile)
15282        addEventListener(document, 'click', function(event) {
15283            const target = event.target;
15284            
15285            // Desktop buy buttons
15286            if (target.classList.contains('btn-buy') ||           // Product detail buy button
15287                target.classList.contains('btn-buy-now') || 
15288                target.classList.contains('buy-now-btn') || 
15289                target.id === 'btn-buy' ||                        // Specific ID match
15290                getDataAttribute(target, 'action') === 'buy-now' ||
15291                getDataAttribute(target, 'action') === 'buy') {
15292                
15293                handleButtonClick(event, 'buy');
15294            }
15295            
15296            // Mobile buy buttons
15297            else if (target.classList.contains('mf-buy') ||       // Mobile fab buy button
15298                     target.id === 'mf-buy') {                   // Mobile buy button ID
15299                
15300                handleMobileButtonClick(event, 'buy');
15301            }
15302            
15303            // Mobile cart buttons - ENHANCED BLOCKING
15304            else if (target.classList.contains('mf-cart') ||      // Mobile fab cart button
15305                     target.id === 'mf-cart') {                  // Mobile cart button ID
15306                
15307                
15308                // Immediately prevent all event propagation
15309                event.preventDefault();
15310                event.stopPropagation();
15311                event.stopImmediatePropagation();
15312                
15313                handleMobileButtonClick(event, 'cart');
15314                
15315                // Return false to ensure no other handlers run
15316                return false;
15317            }
15318        }, true);
15318 // Use capturing phase to ensure we intercept before other handlers
15319        
15320        // Additional protection: Override addToCart function to add mobile button state checking
15321        const originalAddToCart = typeof window.addToCart === 'function' ? window.addToCart : null;
15322        if (originalAddToCart) {
15323            window.addToCart = async function(...args) {
15324                // Check if this call is coming from a mobile button that's currently being validated
15325                const mobileCartBtn = document.getElementById('mf-cart');
15326                if (mobileCartBtn && mobileCartBtn.classList.contains('loading-stock')) {
15327                    return Promise.reject(new Error('Mobile validation in progress - cart action blocked'));
15328                }
15329                
15330                // Check if functions are temporarily blocked (during stock failure)
15331                if (window.addToCart._stockValidatorBlocked) {
15332                    return Promise.reject(new Error('Stock validation failed - cart action blocked'));
15333                }
15334                
15335                // Allow normal calls
15336                return originalAddToCart.apply(this, args);
15337            };
15338            
15339            // Store reference to original for restoration
15340            window.addToCart._original = originalAddToCart;
15341        }
15342    }
15343
15344    /**
15345     * Setup global click interceptor for security
15346     * This catches ALL clicks on cart/buy buttons and validates stock BEFORE any other handler
15347     */
15348    function setupGlobalClickInterceptor() {
15349        // Add capture-phase listener to catch clicks before other handlers
15350        document.addEventListener('click', function(event) {
15351            const target = event.target;
15352            
15353            // Check if this is a cart/buy button
15354            const isCartButton = target.matches('.btn-add-cart, .mf-cart, #mf-cart, #btn-add-cart') ||
15355                                target.closest('.btn-add-cart, .mf-cart, #mf-cart, #btn-add-cart');
15356            const isBuyButton = target.matches('.btn-buy-now, .mf-buy, #mf-buy, #btn-buy') ||
15357                               target.closest('.btn-buy-now, .mf-buy, #mf-buy, #btn-buy');
15358                               
15359            if (!isCartButton && !isBuyButton) {
15360                return; // Not our target, allow normal processing
15361            }
15362            
15363            const button = isCartButton ? 
15364                (target.matches('.btn-add-cart, .mf-cart, #mf-cart, #btn-add-cart') ? target : target.closest('.btn-add-cart, .mf-cart, #mf-cart, #btn-add-cart')) :
15365                (target.matches('.btn-buy-now, .mf-buy, #mf-buy, #btn-buy') ? target : target.closest('.btn-buy-now, .mf-buy, #mf-buy, #btn-buy'));
15366            
15367            // IMMEDIATE BLOCK: Prevent any processing first
15368            event.preventDefault();
15369            event.stopImmediatePropagation();
15370            
15371            // SECURITY CHECK 1: Is button marked as stock-blocked?
15372            if (button && button.hasAttribute('data-stock-blocked')) {
15373                
15374                const reason = button.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
15375                showStockModal(reason);
15376                
15377                // Show security alert
15378                if (window.showToast) {
15379                    window.showToast({
15380                        type: 'error',
15381                        title: 'Aksi Diblokir',
15382                        message: 'Produk ini sedang habis stok dan tidak dapat ditambahkan ke keranjang.',
15383                        duration: 4000
15384                    });
15385                }
15386                
15387                return false;
15388            }
15389            
15390            // SECURITY CHECK 2: Real-time validation against cached stock info
15391            if (window._currentStockInfo && !window._currentStockInfo.can_add_to_cart) {
15392                
15393                showStockModal(window._currentStockInfo.message || 'Stok tidak tersedia');
15394                
15395                // Re-enforce button disabling
15396                if (button) {
15397                    button.disabled = true;
15398                    button.style.opacity = '0.5';
15399                    button.style.cursor = 'not-allowed';
15400                    button.setAttribute('data-stock-blocked', 'true');
15401                    button.setAttribute('data-stock-reason', window._currentStockInfo.message || 'Stok tidak tersedia');
15402                }
15403                
15404                return false;
15405            }
15406            
15407            // SECURITY CHECK 3: Smart client-side validation
15408            const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15409            const variantSelector = document.getElementById('pd-variant-id');
15410            const variantId = variantSelector ? parseInt(variantSelector.value || 0
15410) : 0;
15411            
15412            if (productId > 0) {
15413                // SMART VALIDATION: If we already have cached stock info showing stock is available,
15414                // and buttons are enabled, proceed immediately without re-validation
15415                if (window._currentStockInfo && window._currentStockInfo.can_add_to_cart && 
15416                    button && !button.disabled && !button.hasAttribute('data-stock-blocked')) {
15417                    
15418                    // Allow the action to proceed immediately with toast
15419                    if (isCartButton) {
15420                        // Trigger cart action with proper toast handling
15421                        if (typeof window._pdAddToCartSubmit === 'function') {
15422                            // Use pd-addtocart.js function for proper toast handling
15423                            const form = document.getElementById('pd-buy-form') || button.closest('form');
15424                            if (form) {
15425                                const fakeEvent = { 
15426                                    target: form,
15427                                    preventDefault: () => {}
15428                                };
15429                                window._pdAddToCartSubmit(fakeEvent, productId);
15430                            }
15431                        } else if (typeof window.addToCartSubmit === 'function') {
15432                            // Fallback to addToCartSubmit
15433                            const form = document.getElementById('pd-buy-form') || button.closest('form');
15434                            if (form) {
15435                                const fakeEvent = { 
15436                                    target: form,
15437                                    preventDefault: () => {}
15438                                };
15439                                window.addToCartSubmit(fakeEvent, productId);
15440                            }
15441                        } else if (typeof window.addToCart === 'function') {
15442                            // Direct addToCart call
15443                            window.addToCart(productId, 1, variantId);
15444                        }
15445                    } else if (isBuyButton) {
15446                        // Trigger buy action
15447                        if (typeof window.buyNow === 'function') {
15448                            window.buyNow();
15449                        }
15450                    }
15451                    
15452                    return false; // Action completed
15453                }
15454                
15455                // Get stock info from embedded variant data (for variant products)
15456                const stockInfo = getStockFromEmbeddedData(variantId);
15457                
15458                if (stockInfo && stockInfo.hasStock) {
15459                    
15460                    // Update global stock cache
15461                    window._currentStockInfo = {
15462                        can_add_to_cart: true,
15463                        is_in_stock: true,
15464                        available_stock: stockInfo.stock,
15465                        message: 'Stok tersedia'
15466                    };
15467                    
15468                    // IMPORTANT: Check if button was explicitly blocked by variant logic (e.g., pre-order exhausted)
15469                    // If data-stock-blocked is set, respect it and don't enable the button
15470                    const isExplicitlyBlocked = button.getAttribute('data-stock-blocked') === 'true';
15471                    
15472                    if (!isExplicitlyBlocked) {
15473                        // Remove stock blocking if it was set and enable button
15474                        button.removeAttribute('data-stock-blocked');
15475                        button.removeAttribute('data-stock-reason');
15476                        button.disabled = false;
15477                        button.style.opacity = '';
15478                        button.style.cursor = '';
15479                        button.classList.remove(config.disabledClass);
15480                    } else {
15481                        // Button is blocked by variant logic, show reason
15482                        const reason = button.getAttribute('data-stock-reason') || 'Tidak tersedia';
15483                        console.log('Button blocked by variant logic:', reason);
15484                        e.preventDefault();
15485                        e.stopPropagation();
15486                        return false;
15487                    }
15488                    
15489                    // Allow the action to proceed immediately with toast
15490                    if (isCartButton) {
15491                        // Trigger cart action with proper toast handling
15492                        if (typeof window._pdAddToCartSubmit === 'function') {
15493                            // Use pd-addtocart.js function for proper toast handling
15494                            const form = document.getElementById('pd-buy-form') || button.closest('form');
15495                            if (form) {
15496                                const fakeEvent = { 
15497                                    target: form,
15498                                    preventDefault: () => {}
15499                                };
15500                                window._pdAddToCartSubmit(fakeEvent, productId);
15501                            }
15502                        } else if (typeof window.addToCartSubmit === 'function') {
15503                            // Fallback to addToCartSubmit
15504                            const form = document.getElementById('pd-buy-form') || button.closest('form');
15505                            if (form) {
15506                                const fakeEvent = { 
15507                                    target: form,
15508                                    preventDefault: () => {}
15509                                };
15510                                window.addToCartSubmit(fakeEvent, productId);
15511                            }
15512                        } else if (typeof window.addToCart === 'function') {
15513                            // Direct addToCart call
15514                            window.addToCart(productId, 1, variantId);
15515                        }
15516                    } else if (isBuyButton) {
15517                        // Trigger buy action
15518                        if (typeof window.buyNow === 'function') {
15519                            window.buyNow();
15520                        }
15521                    }
15522                } else if (stockInfo && !stockInfo.hasStock) {
15523                    
15524                    // Update global stock cache
15525                    window._currentStockInfo = {
15526                        can_add_to_cart: false,
15527                        is_in_stock: false,
15528                        available_stock: stockInfo.stock,
15529                        message: `Stok tidak tersedia${stockInfo.sku ? ` untuk ${stockInfo.sku}` : ''}`
15530                    };
15531                    
15532                    // Force block button
15533                    button.disabled = true;
15534                    button.style.opacity = '0.5';
15535                    button.style.cursor = 'not-allowed';
15536                    button.setAttribute('data-stock-blocked', 'true');
15537                    button.setAttribute('data-stock-reason', window._currentStockInfo.message);
15538                    
15539                    // Show modal with variant info
15540                    const modalMessage = stockInfo ? 
15541                        `Varian yang dipilih (${stockInfo.sku}) sedang habis stok. Silakan pilih varian lain.` :
15542                        'Produk ini sedang habis stok. Silakan pilih varian lain.';
15543                    
15544                    showStockModal(modalMessage);
15545                    
15546                    // Show security warning
15547                    if (window.showToast) {
15548                        window.showToast({
15549                            type: 'warning',
15550                            title: 'Stok Tidak Tersedia',
15551                            message: 'Varian yang dipilih sedang habis stok. Silakan pilih varian lain.',
15552                            duration: 3000
15553                        });
15554                    }
15555                } else {
15556                    // stockInfo is null (simple product) - this means embedded data is not available
15557                    // For simple products, if buttons are enabled, we should proceed (trust the API response from updateStockDisplay)
15558                    if (button && !button.disabled && !button.hasAttribute('data-stock-blocked')) {
15559                        
15560                        // Allow the action to proceed immediately with toast
15561                        if (isCartButton) {
15562                            // Trigger cart action with proper toast handling
15563                            if (typeof window._pdAddToCartSubmit === 'function') {
15564                                // Use pd-addtocart.js function for proper toast handling
15565                                const form = document.getElementById('pd-buy-form') || button.closest('form');
15566                                if (form) {
15567                                    const fakeEvent = { 
15568                                        target: form,
15569                                        preventDefault: () => {}
15570                                    };
15571                                    window._pdAddToCartSubmit(fakeEvent, productId);
15572                                }
15573                            } else if (typeof window.addToCartSubmit === 'function') {
15574                                // Fallback to addToCartSubmit
15575                                const form = document.getElementById('pd-buy-form') || button.closest('form');
15576                                if (form) {
15577                                    const fakeEvent = { 
15578                                        target: form,
15579                                        preventDefault: () => {}
15580                                    };
15581                                    window.addToCartSubmit(fakeEvent, productId);
15582                                }
15583                            } else if (typeof window.addToCart === 'function') {
15584                                // Direct addToCart call
15585                                window.addToCart(productId, 1, variantId);
15586                            }
15587                        } else if (isBuyButton) {
15588                            // Trigger buy action
15589                            if (typeof window.buyNow === 'function') {
15590                                window.buyNow();
15591                            }
15592                        }
15593                    } else {
15594                        // Button is disabled, don't allow action
15595                        showStockModal('Produk ini sedang tidak tersedia.');
15596                    }
15597                }
15598            } else {
15599                // No product ID, block action
15600                showStockModal('Data produk tidak valid.');
15601            }
15602            
15603            return false; // Always block at capture phase
15604            
15605        }, true); // Use capture phase to run BEFORE other listeners
15606        
15607
15608    }
15609
15610    /**
15611     * Initialize button data attributes from current page
15612     */
15613    function initializeButtonAttributes() {
15614        // Get product data from page globals or form
15615        let productId = 0, variantId = 0;
15616        
15617        // Try to get from global variables (set by PHP)
15618        if (window.PD_ID) productId = parseInt(window.PD_ID);
15619        
15620        // Try to get from form
15621        const form = document.getElementById('pd-buy-form');
15622        if (form) {
15623            const pidAttr = form.getAttribute('data-pid');
15624            if (pidAttr) productId = parseInt(pidAttr);
15625            
15626            const varInput = document.getElementById('pd-variant-id');
15627            if (varInput) variantId = parseInt(varInput.value || 0);
15628        }
15629        
15630        // Try to get from mobile FAB
15631        const mobileFab = document.getElementById('pd-mobile-fab');
15632        if (mobileFab && productId <= 0) {
15633            const pidAttr = getDataAttribute(mobileFab, 'pid');
15634            if (pidAttr) productId = parseInt(pidAttr);
15635        }
15636        
15637        // Set attributes on desktop buttons
15638        const desktopButtons = document.querySelectorAll('.btn-add-cart, .btn-buy-now, #btn-add-cart, #btn-buy, #btn-buy-voucher');
15639        desktopButtons.forEach(button => {
15640            if (productId > 0) {
15641                button.setAttribute('data-product-id', productId);
15642                if (variantId > 0) {
15643                    button.setAttribute('data-variant-id', variantId);
15644                }
15645            }
15646        });
15647        
15648        // Set attributes on mobile buttons
15649        const mobileButtons = document.querySelectorAll('.mf-cart, .mf-buy, #mf-cart, #mf-buy');
15650        mobileButtons.forEach(button => {
15651            if (productId > 0) {
15652                button.setAttribute('data-product-id', productId);
15653                if (variantId > 0) {
15654                    button.setAttribute('data-variant-id', variantId);
15655                }
15656            }
15657        });
15658        
15659
15660        
15661        // Initial stock display update on page load
15662        if (productId > 0) {
15663            setTimeout(() => {
15664                updateStockDisplay(productId, variantId);
15665
15666            }, 100);
15667        }
15668    }
15669
15670    /**
15671     * Update button attributes when variant changes
15672     */
15673    function updateButtonAttributesForVariant(variantId) {
15674        const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15675        
15676        if (productId <= 0) return;
15677        
15678        // Update desktop buttons
15679        const desktopButtons = document.querySelectorAll('.btn-add-cart, .btn-buy-now, #btn-add-cart, #btn-buy, #btn-buy-voucher');
15680        desktopButtons.forEach(button => {
15681            button.setAttribute('data-product-id', productId);
15682            if (variantId > 0) {
15683                button.setAttribute('data-variant-id', variantId);
15684            } else {
15685                button.removeAttribute('data-variant-id');
15686            }
15687        });
15688        
15689        // Update mobile buttons
15690        const mobileButtons = document.querySelectorAll('.mf-cart, .mf-buy, #mf-cart, #mf-buy');
15691        mobileButtons.forEach(button => {
15692            button.setAttribute('data-product-id', productId);
15693            if (variantId > 0) {
15694                button.setAttribute('data-variant-id', variantId);
15695            } else {
15696                button.removeAttribute('data-variant-id');
15697            }
15698        });
15699        
15700
15701    }
15702
15703    /**
15704     * Setup variant change integration with existing product-detail.php handlers
15705     */
15706    function setupVariantChangeIntegration() {
15707        // Strategy: Hook into existing applyVariant function instead of adding new listener
15708        // This prevents conflicts with existing variant change functionality (gallery, pricing, etc.)
15709        
15710        // Method 1: Hook into existing applyVariant function
15711        setTimeout(() => {
15712            if (typeof window.applyVariant === 'function') {
15713                const originalApplyVariant = window.applyVariant;
15714                window.applyVariant = function(v, focusImage) {
15715                    // Call original function first
15716                    const result = originalApplyVariant.call(this, v, focusImage);
15717                    
15718                // Then add our stock validator logic
15719                const variantId = v ? (v.id || v.ID || 0) : 0;
15720                
15721                // Update button attributes for stock validator
15722                updateButtonAttributesForVariant(variantId);
15723                
15724                // Update stock display using embedded data (instant)
15725                const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15726                if (productId > 0) {
15727                    updateStockDisplay(productId, variantId);
15728                }                    return result;
15729                };
15730                
15731
15732            } else {
15733
15734                setupVariantObserver();
15735            }
15736        }, 500);
15737        
15738        // Method 2: Direct observation of hidden input value changes (fallback)
15739        setTimeout(() => {
15740            setupVariantObserver();
15741        }, 1000);
15742    }
15743
15744    /**
15745     * Setup variant observer as fallback when applyVariant hook fail
15745s
15746     */
15747    function setupVariantObserver() {
15748        const variantInput = document.getElementById('pd-variant-id');
15749        if (variantInput) {
15750            let lastVariantId = parseInt(variantInput.value || '0');
15751            
15752            // Use MutationObserver to watch for value changes
15753            if (window.MutationObserver) {
15754                const observer = new MutationObserver((mutations) => {
15755                    mutations.forEach((mutation) => {
15756                        if (mutation.type === 'attributes' && mutation.attributeName === 'value') {
15757                            const newVariantId = parseInt(variantInput.value || '0');
15758                            if (newVariantId !== lastVariantId) {
15759                    
15760                                updateButtonAttributesForVariant(newVariantId);
15761                                
15762                                // Update stock display with embedded data
15763                                const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15764                                if (productId > 0) {
15765                                    updateStockDisplay(productId, newVariantId);
15766                                }
15767                                
15768                                lastVariantId = newVariantId;
15769                            }
15770                        }
15771                    });
15772                });
15773                
15774                observer.observe(variantInput, { 
15775                    attributes: true, 
15776                    attributeFilter: ['value'] 
15777                });
15778                
15779
15780            }
15781            
15782            // Additional polling as ultimate fallback
15783            setInterval(() => {
15784                const currentVariantId = parseInt(variantInput.value || '0');
15785                if (currentVariantId !== lastVariantId) {
15786        
15787                    updateButtonAttributesForVariant(currentVariantId);
15788                    
15789                    // Update stock display with embedded data
15790                    const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15791                    if (productId > 0) {
15792                        updateStockDisplay(productId, currentVariantId);
15793                    }
15794                    
15795                    lastVariantId = currentVariantId;
15796                }
15797            }, 1000);
15798            
15799
15800        }
15801    }
15802
15803    /**
15804     * Update stock display and enforce stock-based button blocking using embedded data
15805     */
15806    function updateStockDisplay(productId, variantId = 0) {
15807
15808        
15809        // Get stock info from embedded data (instant)
15810        const embeddedStockInfo = getStockFromEmbeddedData(variantId);
15811        
15812        let stockInfo;
15813        if (embeddedStockInfo) {
15814            // Convert embedded data to standard format
15815            stockInfo = {
15816                can_add_to_cart: embeddedStockInfo.hasStock,
15817                is_in_stock: embeddedStockInfo.hasStock,
15818                available_stock: embeddedStockInfo.stock,
15819                message: embeddedStockInfo.hasStock ? 'Stok tersedia' : `Stok habis${embeddedStockInfo.sku ? ` untuk ${embeddedStockInfo.sku}` : ''}`,
15820                product_name: window.PD_NAME || 'Produk',
15821                sku: embeddedStockInfo.sku
15822            };
15823            
15824
15825        } else {
15826            // Fallback to API call if embedded data not available
15827
15828            checkStock(productId, variantId, 1)
15829                .then(apiStockInfo => {
15830                    updateStockDisplayWithInfo(apiStockInfo, productId, variantId);
15831                })
15832                .catch(error => {
15833                    console.error('Failed to update stock display via API:', error);
15834                });
15835            return;
15836        }
15837        
15838        updateStockDisplayWithInfo(stockInfo, productId, variantId);
15839    }
15840
15841    /**
15842     * Update stock display with provided stock info
15843     */
15844    function updateStockDisplayWithInfo(stockInfo, productId, variantId) {
15845        // Store current stock status globally for blocking checks
15846        window._currentStockInfo = stockInfo;
15847        
15848        // Update stock indicators
15849        const stockIndicators = document.querySelectorAll('.stock-indicator');
15850        stockIndicators.forEach(indicator => {
15851            if (stockInfo.is_in_stock) {
15852                indicator.classList.add('in-stock');
15853                indicator.classList.remove('out-of-stock');
15854            } else {
15855                indicator.classList.add('out-of-stock');
15856                indicator.classList.remove('in-stock');
15857            }
15858        });
15859        
15860        // Update stock count
15861        const stockCounts = document.querySelectorAll('.stock-count');
15862        stockCounts.forEach(count => {
15863            count.textContent = stockInfo.available_stock || 0;
15864        });
15865        
15866        // CRITICAL: For digital products, DO NOT modify button states here
15867        // PHP already handles button disable/enable based on LICENSE_POOL count
15868        // Only physical products need JavaScript stock validation
15869        const isDigitalProduct = window.IS_DIGITAL === true || window.IS_DIGITAL === 'true';
15870        
15871        if (isDigitalProduct) {
15872            // For digital products: ONLY update global stock info, don't touch buttons
15873            // Let PHP-rendered disabled state remain authoritative
15874            console.log('[Stock Validator] Digital product detected - skipping button state modification');
15875            return; // Exit early, preserve PHP-set button states
15876        }
15877        
15878        // ENHANCED: Force disable buttons for out-of-stock with anti-tampering (PHYSICAL PRODUCTS ONLY)
15879        const allButtons = document.querySelectorAll('.btn-add-cart, .btn-buy-now, .mf-cart, .mf-buy, #btn-add-cart, #btn-buy, #btn-buy-voucher, #mf-cart, #mf-buy');
15880        allButtons.forEach(button => {
15881            if (stockInfo.can_add_to_cart) {
15882                // STOCK AVAILABLE - ENABLE BUTTONS
15883                button.classList.remove(config.disabledClass);
15884                button.disabled = false;
15885                button.style.opacity = '';
15886                button.style.cursor = '';
15887                
15888                // CRITICAL: Remove stock blocking attributes when stock becomes available
15889                button.removeAttribute('data-stock-blocked');
15890                button.removeAttribute('data-stock-reason');
15891                
15892
15893            } else {
15894                // OUT OF STOCK - FORCE DISABLE WITH ANTI-TAMPERING
15895                button.classList.add(config.disabledClass);
15896                button.disabled = true;
15897                button.style.opacity = '0.5';
15898                button.style.cursor = 'not-allowed';
15899                
15900                // Mark as stock-blocked to prevent re-enabling
15901                button.setAttribute('data-stock-blocked', 'true');
15902                button.setAttribute('data-stock-reason', stockInfo.message || 'Stok tidak tersedia');
15903                
15904
15905            }
15906        });
15907        
15908        // Start aggressive button monitoring for out-of-stock variants
15909        if (!stockInfo.can_add_to_cart) {
15910            enforceStockDisabling(productId, variantId);
15911        }
15912        
15913
15914    }
15915
15916    /**
15917     * Aggressively enforce button disabling for out-of-stock items
15918     * CRITICAL: For digital products, DO NOT modify button states (PHP is authoritative)
15919     */
15920    function enforceStockDisabling(productId, variantId) {
15921        // CRITICAL: Skip enforcement for digital products - PHP handles button states
15922        const isDigitalProduct = window.IS_DIGITAL === true || window.IS_DIGITAL === 'true';
15923        if (isDigitalProduct) {
15924            console.log('[Stock Validator] Digital product - skipping button enforcement');
15925            return; // Exit early, let PHP-rendered disabled state remain
15926        }
15927        
15928        const monitorInterval = setInterval(() => {
15929            // Check if variant changed
15930            const currentVariantSelector = document.getElementById('pd-variant-id');
15931            const currentVariantId = currentVariantSelector ? parseInt(currentVariantSelector.value || 0) : 0;
15932            
15933            if (currentVariantId !== variantId) {
15934                // Variant changed, stop monitoring this variant
15935                clearInterval(monitorInterval);
15936
15937                return;
15938            }
15939            
15940            // Check current stock status using embedded data (real-time check)
15941            const currentStockInfo = getStockFromEmbeddedData(currentVariantId);
15942            
15943            if (currentStockInfo && !currentStockInfo.hasStock) {
15944                // Still out of stock - continue monitoring for tampering
15945                const buttons = document.querySelectorAll('.btn-add-cart, .btn-buy-now, .mf-cart, .mf-buy, #btn-add-cart, #btn-buy, #btn-buy-voucher, #mf-cart, #mf-buy');
15946                buttons.forEach(button => {
15947                    // Check if button was manually enabled (inspect attack)
15948                    if (!button.disabled || button.style.opacity !== '0.5') {
15949                        
15950                        // Force re-disable
15951                        button.disabled = true;
15952                        button.style.opacity = '0.5';
15953                        button.style.cursor = 'not-allowed';
15954                        button.setAttribute('data-stock-blocked', 'true');
15955                        button.setAttribute('data-stock-reason', `Stok tidak tersedia${currentStockInfo.sku ? ` untuk ${currentStockInfo.sku}` : ''}`);
15956                        
15957                        // Show warning toast
15958                        if (window.showToast) {
15959                            window.showToast({
15960                                type: 'warning',
15961                                title: 'Aksi Tidak Diizinkan',
15962                                message: 'Produk ini sedang habis stok. Silakan pilih varian lain.',
15963                                duration: 3000
15964                            });
15965                        }
15966                    }
15967                });
15968            } else {
15969                // Stock available OR embedded data shows stock - stop monitoring
15970                clearInterval(monitorInterval);
15971
15972                // For PHYSICAL products only, enable buttons
15973                // For DIGITAL products, skip (PHP is authoritative)
15974                const isDigitalProduct = window.IS_DIGITAL === true || window.IS_DIGITAL === 'true';
15975                
15976                if (!isDigitalProduct && currentStockInfo && currentStockInfo.hasStock) {
15977                    const buttons = document.querySelectorAll('.btn-add-cart, .btn-buy-now, .mf-cart, .mf-buy, #btn-add-cart, #btn-buy, #btn-buy-voucher, #mf-cart, #mf-buy');
15978                    buttons.forEach(button => {
15979                        button.disabled = false;
15980                        button.style.opacity = '';
15981                        button.style.cursor = '';
15982                        button.removeAttribute('data-stock-blocked');
15983                        button.removeAttribute('data-stock-reason');
15984                        button.classList.remove(config.disabledClass);
15985                    });
15986
15987                }
15988            }
15989        }, 500); // Check every 500ms for tampering attempts
15990        
15991        // Auto-stop monitoring after 30 seconds to prevent memory leaks
15992        setTimeout(() => {
15993            clearInterval(monitorInterval);
15994
15995        }, 30000);
15996    }
15997
15998    /**
15999     * Override addToCartSubmit function to add stock validation
16000     * This function now works with pd-addtocart.js integration
16001     */
16002    function overrideAddToCartSubmitFunction() {
16003        // Check if pd-addtocart.js is already loaded
16004        const pdAddToCartExists = typeof window.addToCartSubmit === 'function' && 
16005                                  window.addToCartSubmit.toString().includes('showToast');
16006        
16007        if (pdAddToCartExists) {
16008
16009            
16010            // Store the pd-addtocart.js function
16011            const pdAddToCartSubmit = window.addToCartSubmit;
16012            window._pdAddToCartSubmit = pdAddToCartSubmit;
16013            
16014            // Override with stock validation that calls pd-addtocart.js
16015            window.addToCartSubmit = async function(e, productId) {
16016                
16017                // Don't prevent default here - let pd-addtocart handle it if stock is valid
16018                
16019                try {
16020                    // Extract product data from form (same way as pd-addtocart.js)
16021                    const form = e.target;
16022                    const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16023                    
16024                    // CRITICAL: Always get current variant from selector (real-time)
16025                    let variantId = 0;
16026                    const variantSelector = document.getElementById('pd-variant-id');
16027                    if (variantSelector) {
16028                        variantId = parseInt(variantSelector.value || '0', 10) || 0;
16029                    }
16030                    
16031                    if (!productId || productId <= 0) {
16032                        return pdAddToCartSubmit(e, productId);
16033                    }
16034
16035
16036                    // Validate stock before proceeding
16037                    const isValid = await validateBeforeCart(productId, variantId, qty, 'cart');
16038                    
16039                    if (isValid) {
16040                        // Stock OK, call pd-addtocart.js function (it will handle toast)
16041                        return pdAddToCartSubmit(e, productId);
16042                    } else {
16043                        // Prevent the form submission
16044                        e.preventDefault();
16045                        // Stock validation failed, modal already shown by validateBeforeCart
16046                        return false;
16047                    }
16048                    
16049                } catch (error) {
16050                    e.preventDefault();
16051                    // On error, show modal and don't proceed
16052                    showStockModal({ 
16053                        message: 'Terjadi kesalahan saat memvalidasi stok. Silakan coba lagi.',
16054                        is_active: false 
16055                    }, 'cart');
16056                    return false;
16057                }
16058            };
16059        } else {
16060            // Original logic for when pd-addtocart.js is not present
16061            const originalAddToCartSubmit = typeof window.addToCartSubmit === 'function' ? window.addToCartSubmit : null;
16062            
16063            if (originalAddToCartSubmit) {
16064                window._originalAddToCartSubmit = originalAddToCartSubmit;
16065            }
16066            
16067            // Override with stock validation
16068            window.addToCartSubmit = async function(e, productId) {
16069                
16070                e.preventDefault();
16071                
16072                try {
16073                    const form = e.target;
16074                    const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16075                    
16076                    // CRITICAL: Always get current variant from selector (real-time)
16077                    let variantId = 0;
16078                    const variantSelector = document.getElementById('pd-variant-id');
16079                    if (variantSelector) {
16080                        variantId = parseInt(variantSelector.value || '0', 10) || 0;
16081                    }
16082                    
16083                    if (!productId || productId <= 0) {
16084                        if (originalAddToCartSubmit) return originalAddToCartSubmit(e, productId);
16085                        return false;
16086                    }
16087
16088
16089                    const isValid = await validateBeforeCart(productId, variantId, qty, 'cart');
16090                    
16091                    if (isValid) {
16092                        if (originalAddToCartSubmit) {
16093                            return originalAddToCartSubmit(e, productId);
16094                        } else {
16095                            if (typeof window.addToCart === 'function') {
16096                                await window.addToCart(productId, qty, variantId);
16097                            }
16098                        }
16099                    } else {
16100                    }
16101                    
16102                } catch (error) {
16103                    showStockModal({ 
16104                        message: 'Terjadi kesalahan saat memvalidasi stok. Silakan coba lagi.',
16105                        is_active: false 
16106                    }, 'cart');
16107                }
16108                
16109                return false;
16110            };
16111        }
16112
16113
16114    }
16115
16116    /**
16117     * Override buyNow function to add stock validation
16118     */
16119    function overrideBuyNowFunction() {
16120        // Store original buyNow function if it exists
16121        const originalBuyNow = typeof window.buyNow === 'function' ? window.buyNow : null;
16122        
16123        // Override with stock validation
16124        window.buyNow = async function(e) {
16125            
16126            try {
16127                // Get product data (same way as pd-buynow.js)
16128                const form = document.getElementById('pd-buy-form');
16129                if (!form) {
16130                    if (originalBuyNow) return originalBuyNow(e);
16131                    return;
16132                }
16133
16134                const productId = parseInt(form.dataset.pid || '0', 10);
16135                const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16136                
16137                // CRITICAL: Always get current variant from selector (real-time)
16138                let variantId = 0;
16139                const variantSelector = document.getElementById('pd-variant-id');
16140                if (variantSelector) {
16141                    variantId = parseInt(variantSelector.value || '0', 10) || 0;
16142                }
16143                
16144                if (!productId || productId <= 0) {
16145                    if (originalBuyNow) return originalBuyNow(e);
16146                    return;
16147                }
16148
16149
16150                // Validate stock before proceeding
16151                const isValid = await validateBeforeCart(productId, variantId, qty, 'buy');
16152                
16153                if (isValid) {
16154                    // Stock OK, call original buyNow function
16155                    if (originalBuyNow) {
16156                        return originalBuyNow(e);
16157                    } else {
16158                    }
16159                } else {
16160                    // Stock validation failed, modal already shown by validateBeforeCart
16161                }
16162                
16163            } catch (error) {
16164                // On error, show modal and don't proceed
16165                showStockModal({ 
16166                    message: 'Terjadi kesalahan saat memvalidasi stok. Silakan coba lagi.',
16167                    is_active: false 
16168                }, 'buy');
16169            }
16170        };
16171
16172
16173    }
16174
16175    /**
16176     * Initialize stock validation
16177     */
16178    function init() {
16179        // Wait for DOM ready
16180        if (document.readyState === 'loading') {
16181            addEventListener(document, 'DOMContentLoaded', function() {
16182        initializeButtonAttributes();
16183        setupVariantChangeIntegration(); // Setup variant change integration
16184        interceptCartButtons();
16185        setupGlobalClickInterceptor(); // Setup global click security interceptor                // Override functions after other scripts load
16186                // Use multiple timeouts to catch late-loading scripts like pd-addtocart.js
16187                setTimeout(() => {
16188                    overrideAddToCartSubmitFunction();
16189                    overrideBuyNowFunction();
16190                }, 100);
16191                
16192                setTimeout(() => {
16193                    // Re-check and re-override if pd-addtocart.js loaded later
16194                    overrideAddToCartSubmitFunction();
16195                }, 500);
16196                
16197                setTimeout(() => {
16198                    // Final check for very late loading scripts
16199                    overrideAddToCartSubmitFunction();
16200                }, 1000);
16201                
16202
16203            });
16204        } else {
16205            initializeButtonAttributes();
16206            setupVariantChangeIntegration(); // Setup variant change integration
16207            interceptCartButtons();
16208            setupGlobalClickInterceptor(); // Setup global click security interceptor
16209            
16210            // Override functions after other scripts load
16211            // Use multiple timeouts to catch late-loading scripts like pd-addtocart.js
16212            setTimeout(() => {
16213                overrideAddToCartSubmitFunction();
16214                overrideBuyNowFunction();
16215            }, 100);
16216            
16217            setTimeout(() => {
16218                // Re-check and re-override if pd-addtocart.js loaded later
16219                overrideAddToCartSubmitFunction();
16220            }, 500);
16221            
16222            setTimeout(() => {
16223                // Final check for very late loading scripts
16224                overrideAddToCartSubmitFunction();
16225            }, 1000);
16226            
16227
16228        }
16229    }
16230
16231    // Public API
16232    return {
16233        init: init,
16234        checkStock: checkStock,
16235        validateBeforeCart: validateBeforeCart,
16236        updateStockDisplay: updateStockDisplay,
16237        showStockModal: showStockModal,
16238        config: config
16239    };
16240})();
16241
16242// Auto-initialize
16243if (typeof window !== 'undefined') {
16244    StockValidator.init();
16245}
16246
16247</script>
vendor: 2 bytes, line 16247
16247
16248<script data-inlined="pd-addtocart.js">
16249(function(){
16250  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
16251  function getVariantSummaryText(){
16252    const vs = document.getElementById('pd-variant-summary');
16253    if (!vs) return '';
16254    const items = Array.from(vs.querySelectorAll('.vs-item strong')).map(el => el.textContent.trim()).filter(Boolean);
16255    return items.length ? items.join(' / ') : '';
16256  }
16257  window.addToCartSubmit = async function(e, productId){
16258    e.preventDefault();
16259    try {
16260      const form = e.target;
16261      const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16262      const variantId = parseInt(document.getElementById('pd-variant-id')?.value || '0', 10) || 0;
16263      await (window.addToCart ? window.addToCart(productId, qty, variantId) : Promise.reject(new Error('addToCart tidak tersedia')));
16264
16265      if (window.PlazaAnalytics) {
16266        window.PlazaAnalytics.track('add_to_cart', { slug: String(window._pit_slug || '') });
16267      }
16268
16269      const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : (document.getElementById('pd-title')?.textContent || '');
16270      const sku  = (function(){ const el=document.getElementById('pd-sku'); return el?el.textContent.trim(): (window.PD_SKU||''); })();
16271      const variantText = getVariantSummaryText();
16272
16273      if (window.showToast) {
16274        window.showToast({
16275          type: 'success',
16276          title: 'Berhasil ditambahkan ke Keranjang',
16277          message: `<strong>${esc(name)}</strong>${variantText?` · <em>${esc(variantText)}</em>`:''}<br>SKU: ${esc(sku)} · Qty: ${qty}`,
16278          duration: 5000,
16279          actions: [
16280            { label: 'Lihat Keranjang', onClick: ()=> { window.location.href = '/keranjang'; } },
16281            { label: 'Lanjut Belanja', variant: 'alt', onClick: ()=>{} }
16282          ]
16283        });
16284      }
16285    } catch (err) {
16286      if (window.showToast) {
16287        window.showToast({ type:'warn', title:'Gagal menambahkan', message: String(err?.message||'Terjadi kesalahan'), duration: 4500 });
16288      } else {
16289        console.error(err);
16290      }
16291    }
16292    return false;
16293  };
16294})();
16295</script>
vendor: 2 bytes, line 16295
16295
16296<script data-inlined="pd-buynow.js">
16297(function(){
16298  
16299  // CSRF Token Helper
16300  function getCsrfToken() {
16301    return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
16302  }
16303  
16304  // Utility functions
16305  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
16306  
16307  function getVariantSummaryText(){
16308    const vs = document.getElementById('pd-variant-summary');
16309    if (!vs) return '';
16310    const items = Array.from(vs.querySelectorAll('.vs-item strong')).map(el => el.textContent.trim()).filter(Boolean);
16311    return items.length ? items.join(' / ') : '';
16312  }
16313
16314  function getCurrentProductData(){
16315    const form = document.getElementById('pd-buy-form');
16316    if (!form) return null;
16317
16318    const productId = parseInt(form.dataset.pid || '0', 10);
16319    const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16320    const variantId = parseInt(document.getElementById('pd-variant-id')?.value || '0', 10) || 0;
16321    
16322    if (!productId || productId <= 0) {
16323      throw new Error('Product ID tidak valid');
16324    }
16325
16326    if (qty <= 0) {
16327      throw new Error('Quantity harus lebih dari 0');
16328    }
16329
16330    return { productId, qty, variantId };
16331  }
16332
16333  function createBuyNowPayload(productId, qty, variantId) {
16334    // Get product metadata
16335    const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : (document.getElementById('pd-title')?.textContent || '');
16336    
16337    // Get correct price, SKU, and weight from selected variant data instead of DOM elements
16338    let price = 0;
16339    let sku = '';
16340    let weight = 0;
16341    let dimensions = null;
16342    
16343    if (variantId > 0 && typeof window.variants !== 'undefined' && Array.isArray(window.variants)) {
16344      // Find the selected variant in the variants array
16345      const selectedVariant = window.variants.find(v => v.id === variantId);
16346      if (selectedVariant) {
16347        price = selectedVariant.final || 0;
16348        sku = selectedVariant.sku || '';
16349        weight = selectedVariant.weight || 0;
16350        
16351        // Include dimensions if available
16352        if (selectedVariant.length_cm || selectedVariant.width_cm || selectedVariant.height_cm) {
16353          dimensions = {
16354            length_cm: selectedVariant.length_cm || 0,
16355            width_cm: selectedVariant.width_cm || 0,
16356            height_cm: selectedVariant.height_cm || 0
16357          };
16358        }
16359        
16360        // Debug info to verify fix is working
16361        if (window.console && window.console.debug) {
16362          console.debug('BuyNow: Using variant data', {
16363            variantId: variantId,
16364            price: price,
16365            sku: sku,
16366            weight: weight,
16367            dimensions: dimensions,
16368            variantData: selectedVariant
16369          });
16370        }
16371      }
16372    }
16373    
16374    // Fallback to DOM elements if variant data not found
16375    if (price === 0) {
16376      const priceTxt = document.getElementById('pd-price-current')?.textContent || '';
16377      price = parseInt(String(priceTxt).replace(/[^\d]/g,''), 10) || 0;
16378      
16379      if (window.console && window.console.debug) {
16380        console.debug('BuyNow: Using DOM price fallback', { 
16381          priceTxt: priceTxt, 
16382          price: price,
16383          variantId: variantId,
16384          reason: variantId > 0 ? 'variant_data_not_found' : 'no_variant_selected'
16385        });
16386      }
16387    }
16388    
16389    if (!sku) {
16390      const skuEl = document.getElementById('pd-sku');
16391      sku = skuEl ? skuEl.textContent.trim() : (window.PD_SKU || '');
16392    }
16393    
16394    const imgEl = document.getElementById('pd-main-img');
16395    const image_url = imgEl ? (imgEl.getAttribute('src') || '') : '';
16396
16397    const variantText = getVariantSummaryText();
16398    
16399    // Get pre-order data if product is pre-order
16400    const preorderData = window.PD_PREORDER_DATA || null;
16401    
16402    return {
16403      product_id: productId,
16404      qty: qty,
16405      variant_id: variantId,
16406      meta: {
16407        name,
16408        sku,
16409        price,
16410        image_url,
16411        variant_text: variantText,
16412        weight: weight,
16413        dimensions: dimensions
16414      },
16415      is_preorder: preorderData ? 'Y' : 'N',
16416      preorder_estimated_arrival: preorderData ? preorderData.estimated_arrival : null,
16417      preorder_note: preorderData ? preorderData.note : ''
16418    };
16419  }
16420
16421  /**
16422   * Main Buy Now function
16423   * Performs validation and redirects to checkout with buy_now mode
16424   */
16425  window.buyNow = async function(e) {
16426    if (e) e.preventDefault();
16427    
16428    try {
16429      // Check user login status
16430      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
16431                        document.body.classList.contains('logged-in') ||
16432                        window.userLoggedIn === true ||
16433                        document.body.dataset.userLoggedIn === 'true';
16434      
16435      if (!isLoggedIn) {
16436        // Redirect to login with return URL to current page
16437        const currentUrl = encodeURIComponent(window.location.pathname + window.location.search);
16438        window.location.href = '/login?return=' + currentUrl;
16439        return;
16440      }
16441
16442      // Get current product data
16443      const { productId, qty, variantId } = getCurrentProductData();
16444      
16445      // Create payload for buy now
16446      const payload = createBuyNowPayload(productId, qty, variantId);
16447      
16448      // Store buy now data in sessionStorage for checkout page
16449      sessionStorage.setItem('buyNowData', JSON.stringify(payload));
16450
16451      // Track add_to_cart (buy now counts as purchase intent)
16452      if (window.PlazaAnalytics) {
16453        window.PlazaAnalytics.track('add_to_cart', { slug: String(window._pit_slug || '') });
16454      }
16455
16456      // Redirect to checkout with buy_now mode
16457      window.location.href = '/checkout?mode=buy_now';
16458      
16459    } catch (err) {
16460      console.error('Buy Now error:', err);
16461      
16462      if (window.showToast) {
16463        window.showToast({ 
16464          type: 'warn', 
16465          title: 'Gagal memproses', 
16466          message: String(err?.message || 'Terjadi kesalahan saat memproses pembelian'), 
16467          duration: 4500 
16468        });
16469      } else {
16470        alert(err.message || 'Terjadi kesalahan saat memproses pembelian');
16471      }
16472    }
16473  };
16474
16475  /**
16476   * Alternative Buy Now with confirmation dialog
16477   */
16478  window.buyNowWithConfirm = async function(e) {
16479    if (e) e.preventDefault();
16480    
16481    try {
16482      const { productId, qty, variantId } = getCurrentProductData();
16483      
16484      const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : (document.getElementById('pd-title')?.textContent || '');
16485      const variantText = getVariantSummaryText();
16486      const priceTxt = document.getElementById('pd-price-current')?.textContent || 'Rp0';
16487      
16488      const confirmMessage = `Lanjut ke checkout?\n\nProduk: ${name}${variantText ? `\nVariant: ${variantText}` : ''}\nQty: ${qty}\nHarga: ${priceTxt}`;
16489      
16490      if (!confirm(confirmMessage)) {
16491        return;
16492      }
16493      
16494      // Proceed with buy now
16495      await window.buyNow(e);
16496      
16497    } catch (err) {
16498      console.error('Buy Now with confirm error:', err);
16499      alert(err.message || 'Terjadi kesalahan');
16500    }
16501  };
16502
16503  /**
16504   * Attach event listener for Buy with Voucher button
16505   */
16506  function setupBuyButtons() {
16507    // Setup desktop buy button (regular buy now without voucher)
16508    const buyBtn = document.getElementById('btn-buy');
16509    if (buyBtn) {
16510      buyBtn.addEventListener('click', function(e) {
16511        e.preventDefault();
16512        if (buyBtn.disabled) return;
16513        if (window.buyNow) {
16514          window.buyNow(e);
16515        }
16516      });
16517    }
16518    
16519    // Setup desktop buy with voucher button
16520    const voucherBtn = document.getElementById('btn-buy-voucher');
16521    if (voucherBtn) {
16522      voucherBtn.addEventListener('click', function(e) {
16523        e.preventDefault();
16524        
16525        // Validate stock availability before proceeding
16526        if (this.disabled || this.getAttribute('data-stock-blocked') === 'true') {
16527          const stockReason = this.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
16528          
16529          if (window.showToast) {
16530            window.showToast({
16531              type: 'warn',
16532              title: 'Stok Tidak Tersedia',
16533              message: stockReason,
16534              duration: 3500
16535            });
16536          } else {
16537            alert(stockReason);
16538          }
16539          return;
16540        }
16541        
16542        const voucherCode = this.dataset.voucherCode;
16543        if (voucherCode) {
16544          window.buyNowWithVoucher(e, voucherCode);
16545        }
16546      });
16547    }
16548    
16549  }
16550  
16551  // CRITICAL: Attach mobile button handler at DOCUMENT level with CAPTURE phase
16552  // This is the ONLY way to guarantee it fires regardless of other scripts
16553  document.addEventListener('click', function(e) {
16554    // Check if click is on mobile buy button or its children
16555    const target = e.target.closest('#mf-buy');
16556    if (!target) return; // Not our button
16557    
16558    e.preventDefault();
16559    e.stopPropagation();
16560    e.stopImmediatePropagation();
16561    
16562    if (target.disabled) return;
16563    
16564    // Get voucher code from parent FAB's data attribute
16565    const mobileFab = document.getElementById('pd-mobile-fab');
16566    const voucherCode = mobileFab ? (mobileFab.getAttribute('data-voucher-code') || '') : '';
16567    
16568    if (voucherCode && voucherCode.trim() !== '' && window.buyNowWithVoucher) {
16569      window.buyNowWithVoucher(e, voucherCode);
16570    } else if (window.buyNow) {
16571      window.buyNow(e);
16572    }
16573  }, true); // CAPTURE PHASE - fires BEFORE any bubble phase handlers
16574  
16575  // Execute immediately if DOM already loaded, otherwise wait
16576  if (document.readyState === 'loading') {
16577    document.addEventListener('DOMContentLoaded', setupBuyButtons);
16578  } else {
16579    setupBuyButtons();
16580  }
16581
16582  /**
16583   * Buy Now with Auto-Applied Voucher
16584   * Applies voucher to session before redirecting to checkout (like cart does)
16585   * 
16586   * @param {Event} e - Click event
16587   * @param {string} voucherCode - Voucher code to apply
16588   */
16589  window.buyNowWithVoucher = async function(e, voucherCode) {
16590    if (e) e.preventDefault();
16591    
16592    console.log('[buyNowWithVoucher] Called with voucher code:', voucherCode);
16593    
16594    try {
16595      // CRITICAL: Check stock availability first
16596      const voucherBtn = document.getElementById('btn-buy-voucher');
16597      if (voucherBtn) {
16598        if (voucherBtn.disabled || voucherBtn.getAttribute('data-stock-blocked') === 'true') {
16599          const stockReason = voucherBtn.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
16600          
16601          if (window.showToast) {
16602            window.showToast({
16603              type: 'warn',
16604              title: 'Stok Tidak Tersedia',
16605              message: stockReason,
16606              duration: 3500
16607            });
16608          } else {
16609            alert(stockReason);
16610          }
16611          return;
16612        }
16613      }
16614      
16615      // Check login status first
16616      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
16617                        document.body.classList.contains('logged-in') ||
16618                        window.userLoggedIn === true ||
16619                        document.body.dataset.userLoggedIn === 'true';
16620      
16621      console.log('[buyNowWithVoucher] User logged in:', isLoggedIn);
16622      
16623      if (!isLoggedIn) {
16624        const currentUrl = encodeURIComponent(window.location.pathname + window.location.search);
16625        window.location.href = '/login?return=' + currentUrl;
16626        return;
16627      }
16628
16629      // Get current product data
16630      const { productId, qty, variantId } = getCurrentProductData();
16631      const payload = createBuyNowPayload(productId, qty, variantId);
16632      
16633      console.log('[buyNowWithVoucher] Product data:', { productId, qty, variantId });
16634      
16635      // Get category path for voucher validation
16636      const categoryPath = window.PD_CATEGORY_PATH || '';
16637      
16638      // Step 1: Clear any existing voucher first (important for clean state)
16639      console.log('[buyNowWithVoucher] Step 1: Clearing existing voucher...');
16640      try {
16641        await fetch('/api/voucher/remove.php', {
16642          method: 'POST',
16643          headers: { 
16644            'X-CSRF-Token': getCsrfToken()
16645          },
16646          body: JSON.stringify({
16647            csrf_token: getCsrfToken()
16648          }),
16649          credentials: 'same-origin'
16650        });
16651      } catch (e) {
16652        // Ignore error if no voucher exists
16653        console.debug('No existing voucher to remove or removal failed');
16654      }
16655      
16656      // Step 2: Apply voucher to session (like cart does)
16657      console.log('[buyNowWithVoucher] Step 2: Applying voucher to session...');
16658      const applyRes = await fetch('/api/voucher/apply.php', {
16659        method: 'POST',
16660        headers: { 
16661          'Content-Type': 'application/json',
16662          'X-CSRF-Token': getCsrfToken()
16663        },
16664        credentials: 'same-origin',
16665        body: JSON.stringify({
16666          csrf_token: getCsrfToken(),
16667          code: voucherCode,
16668          items: [{
16669            pid: productId,
16670            price: payload.meta.price,
16671            qty: qty,
16672            category_path: categoryPath
16673          }],
16674          shipping_cost: null // Will be calculated in checkout
16675        })
16676      });
16677      
16678      const applyData = await applyRes.json();
16679      
16680      console.log('[buyNowWithVoucher] Voucher apply response:', applyData);
16681      
16682      if (!applyData.success) {
16683        // Voucher invalid - show error and fallback to regular buy now
16684        console.warn('[buyNowWithVoucher] Voucher application failed:', applyData.message);
16685        if (window.showToast) {
16686          window.showToast({
16687            type: 'error',
16688            title: 'Voucher Tidak Valid',
16689            message: applyData.message || 'Voucher tidak dapat diterapkan untuk produk ini',
16690            duration: 4000
16691          });
16692        } else {
16693          alert(applyData.message || 'Voucher tidak dapat diterapkan');
16694        }
16695        
16696        // Fallback to regular buy now without voucher
16697        console.log('Voucher failed, proceeding with regular buy now');
16698        return window.buyNow(e);
16699      }
16700      
16701      // Step 3: Store buy now data in sessionStorage
16702      console.log('[buyNowWithVoucher] Step 3: Storing buy now data in sessionStorage');
16703      sessionStorage.setItem('buyNowData', JSON.stringify(payload));
16704      
16705      // Step 4: Redirect to checkout
16706      // Voucher sudah di session, checkout akan auto-load via loadAppliedVoucher()
16707      console.log('[buyNowWithVoucher] Step 4: Redirecting to checkout with voucher applied');
16708      window.location.href = '/checkout?mode=buy_now';
16709      
16710    } catch (err) {
16711      console.error('Buy Now with Voucher error:', err);
16712      
16713      if (window.showToast) {
16714        window.showToast({
16715          type: 'error',
16716          title: 'Terjadi Kesalahan',
16717          message: 'Gagal memproses pembelian dengan voucher. Silakan coba lagi.',
16718          duration: 3000
16719        });
16720      } else {
16721        alert('Gagal memproses pembelian. Silakan coba lagi.');
16722      }
16723    }
16724  };
16725})();
16726</script>
vendor: 2 bytes, line 16726
16726
16727<script data-inlined="pd-voucher-recalc.js">
16728/**
16729 * Product Detail - Voucher Discount Recalculation
16730 * 
16731 * Handles dynamic voucher discount recalculation when variant price changes
16732 * Supports both PERCENTAGE and FIXED_AMOUNT voucher types
16733 */
16734
16735(function() {
16736  'use strict';
16737
16738  /**
16739   * Calculate voucher discount based on type and price
16740   * 
16741   * @param {string} type - Voucher type: 'PERCENTAGE' or 'FIXED_AMOUNT'
16742   * @param {number} value - Voucher value (percentage or fixed amount)
16743   * @param {number} price - Current variant price
16744   * @param {number} maxDiscount - Maximum discount for percentage vouchers (0 = no limit)
16745   * @return {number} Calculated discount amount
16746   */
16747  function calculateVoucherDiscount(type, value, price, maxDiscount) {
16748    if (type === 'PERCENTAGE') {
16749      // Calculate percentage discount
16750      let discount = (price * value) / 100;
16751      
16752      // Apply max discount cap if set
16753      if (maxDiscount > 0 && discount > maxDiscount) {
16754        discount = maxDiscount;
16755      }
16756      
16757      return Math.round(discount);
16758    } else {
16759      // FIXED_AMOUNT - discount doesn't change with price
16760      return Math.round(value);
16761    }
16762  }
16763
16764  /**
16765   * Format number as Rupiah without 'Rp' prefix
16766   * 
16767   * @param {number} num - Number to format
16768   * @return {string} Formatted number (e.g., "20.000")
16769   */
16770  function formatNumber(num) {
16771    return Math.round(num).toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.');
16772  }
16773
16774  /**
16775   * Update voucher button discount label
16776   * 
16777   * @param {number} newPrice - New variant price
16778   */
16779  function updateVoucherDiscount(newPrice) {
16780    const voucherBtn = document.getElementById('btn-buy-voucher');
16781    if (!voucherBtn) return;
16782
16783    // Get voucher data from button attributes
16784    const voucherType = voucherBtn.dataset.voucherType;
16785    const voucherValue = parseFloat(voucherBtn.dataset.voucherValue) || 0;
16786    const voucherMax = parseFloat(voucherBtn.dataset.voucherMax) || 0;
16787
16788    if (!voucherType || voucherValue === 0) return;
16789
16790    // Calculate new discount
16791    const newDiscount = calculateVoucherDiscount(voucherType, voucherValue, newPrice, voucherMax);
16792
16793    // Update the discount label
16794    const saveLabel = voucherBtn.querySelector('.bv-save');
16795    if (saveLabel) {
16796      saveLabel.textContent = '-Rp' + formatNumber(newDiscount);
16797    }
16798
16799    // Also update mobile FAB voucher display if exists
16800    updateMobileFabVoucher(newDiscount);
16801  }
16802
16803  /**
16804   * Update mobile FAB voucher value
16805   * 
16806   * @param {number} discountAmount - New discount amount
16807   */
16808  function updateMobileFabVoucher(discountAmount) {
16809    const fab = document.getElementById('pd-mobile-fab');
16810    if (fab) {
16811      fab.dataset.voucherValue = discountAmount.toString();
16812    }
16813  }
16814
16815  /**
16816   * Listen to variant change events and recalculate discount
16817   */
16818  function initVoucherRecalculation() {
16819    window.addEventListener('pd-variant-changed', function(e) {
16820      const detail = e.detail || {};
16821      const newPrice = detail.price || 0;
16822
16823      if (newPrice > 0) {
16824        updateVoucherDiscount(newPrice);
16825      }
16826    });
16827  }
16828
16829  // Initialize when DOM is ready
16830  if (document.readyState === 'loading') {
16831    document.addEventListener('DOMContentLoaded', initVoucherRecalculation);
16832  } else {
16833    initVoucherRecalculation();
16834  }
16835
16836})();
16837
16838</script>
vendor: 2 bytes, line 16838
16838
16839<script nonce="se3iyzEHMc9FQEB4wSyd0w==">document.addEventListener('DOMContentLoaded',function(){if(typeof ProductReviews!=='undefined'){ProductReviews.init(325);}const r=document.getElementById('pd-rating-inline');if(r){r.addEventListener('click',function(e){e.preventDefault();const s=document.getElementById('reviews');if(s){s.scrollIntoView({behavior:'smooth',block:'start'});s.classList.add('highlight-flash');setTimeout(()=>{s.classList.remove('highlight-flash')},1500)}});}});</script>
16839
16840
16841<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
16842(function(){function _0x9i0j(el){if(typeof gtag==='function'){gtag('event','qoala_insurance_click',{event_category:'Insurance',event_label:'Qoala Banner Click - Product Detail',link_url:'/plazait-protect',page_location:window.location.href,product_id:parseInt(el.getAttribute('data-product-id'))||0,product_name:el.getAttribute('data-product-name')||'',category_path:el.getAttribute('data-category-path')||'',custom_map:{custom_parameter_1:'qoala_banner_product_detail'}})}}
16843
16844function _0x1k2l(){var a=document.querySelector('.qoala-cta[data-ga-event="qoala_banner_click"]');if(a){a.addEventListener('click',function(e){_0x9i0j(this)})}}if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',_0x1k2l)}else{_0x1k2l()}})();
16845
16846(function(){const pwpSection=document.getElementById('pwp-deals-section');const pwpBadge=document.getElementById('pwp-trigger-badge');if(!pwpSection)return;const productId=parseInt(pwpSection.getAttribute('data-product-id'))||0;const variantId=parseInt(pwpSection.getAttribute('data-variant-id'))||0;const categoryPath=pwpSection.getAttribute('data-category-path')||'';if(!productId)return;function formatRupiah(n){return'Rp'+Math.floor(n).toLocaleString('id-ID')}function loadPWPDeals(){const url=`/api/pwp/get_by_product.php?product_id=${productId}${variantId?`&variant_id=${variantId}`:''}${categoryPath?`&category_path=${encodeURIComponent(categoryPath)}`:''}`;fetch(url).then(r=>r.json()).then(data=>{if(data.success&&data.promotions&&data.promotions.length>0){renderPWPDeals(data.promotions);pwpSection.hidden=false;if(pwpBadge)pwpBadge.hidden=false;setTimeout(function(){if(typeof window.adjustSidebarBanner==='function'){window.adjustSidebarBanner()}},150)}else{pwpSection.hidden=true;if(pwpBadge)pwpBadge.hidden=true}}).catch(err=>{console.error('PWP load error:',err);pwpSection.hidden=true;if(pwpBadge)pwpBadge.hidden=true})}function renderPWPDeals(promotions){const loading=pwpSection.querySelector('.pwp-deals-loading');const grid=document.getElementById('pwp-deals-grid');if(loading)loading.hidden=true;if(!grid)return;grid.innerHTML='';promotions.forEach(promo=>{promo.bundled_products.forEach(product=>{const card=document.createElement('div');card.className='pwp-deal-card';const discount=product.discount>0?product.discount:0;const discountPercent=product.regular_price>0?Math.round((discount/product.regular_price)*100):0;const imgUrl=product.image_url?`/uploads/products/${product.image_url}`:'/assets/img/no-image.png';card.innerHTML=`<div class="pwp-deal-thumb"><img src="${imgUrl}" alt="${escapeHtml(product.name)}" loading="lazy"></div><div class="pwp-deal-info"><h4 class="pwp-deal-name">${escapeHtml(product.name)}</h4><div class="pwp-deal-prices"><span class="pwp-deal-price">${formatRupiah(product.pwp_price)}</span>${product.regular_price>product.pwp_price?`<span class="pwp-deal-original">${formatRupiah(product.regular_price)}</span>`:''}</div></div>${discountPercent>0?`<span class="pwp-deal-badge">-${discountPercent}%</span>`:''}`;grid.appendChild(card)})})}function escapeHtml(str){const div=document.createElement('div');div.textContent=str||'';return div.innerHTML}loadPWPDeals();window.addEventListener('pd-variant-changed',function(e){const detail=e.detail||{};const newVid=detail.variant_id||0;const currentVid=parseInt(pwpSection.getAttribute('data-variant-id'))||0;if(newVid!==currentVid){pwpSection.setAttribute('data-variant-id',newVid);loadPWPDeals()}})})();
16847</script>
16847<footer class="site-footer footer-compact variant-stripes" role="contentinfo">
16848  <div class="ft-top ft-collapsible" id="ftTop">
16849    <div class="container ft-grid">
16850
16851      <div class="ft-col ft-help">
16852        <h4 class="ft-title">Butuh Bantuan?</h4>
16853        <p class="ft-desc">Hubungi kami melalui Official WhatsApp interaktif sekarang.</p>
16854        <a class="ft-wa-btn" href="https://sunnypork.com/" target="_blank" rel="nofollow noopener">
16855          <span class="ico" aria-hidden="true">
16856            <svg viewBox="0 0 24 24" width="18" height="18"><path fill="#25D366" d="M12.04 2c-5.52 0-10 4.43-10 9.9 0 1.75.47 3.45 1.38 4.94L2 22l5.3-1.38c1.43.78 3.05 1.19 4.7 1.19 5.52 0 10-4.43 10-9.9S17.56 2 12.04 2Zm5.86 13.9c-.25.7-1.23 1.28-1.7 1.33-.45.06-1.02.08-1.65-.1-.38-.12-.88-.29-1.52-.56-2.66-1.15-4.4-3.83-4.53-4.01-.13-.18-1.08-1.44-1.08-2.75 0-1.31.68-1.95.92-2.22.25-.27.54-.33.72-.33.18 0 .36.01.52.01.17 0 .39-.06.62.47.25.6.85 2.07.92 2.22.07.15.12.33.02.53-.1.2-.15.33-.3.5-.15.17-.32.39-.46.53-.15.15-.3.31-.13.6.18.3.8 1.31 1.73 2.12 1.19 1.03 2.17 1.36 2.48 1.5.31.14.49.12.67-.07.18-.18.77-.82.98-1.1.21-.27.42-.23.7-.14.27.09 1.72.81 2.02.95.3.14.5.22.58.35.08.13.08.72-.17 1.42Z"/></svg>
16857          </span>
16858          Kirim Pesan
16859        </a>
16860
16861        <p class="ft-desc sm-gap">Ikuti kami:</p>
16862        <div class="ft-social">
16863          <a aria-label="Facebook" href="https://facebook.com/PlazaIt/"><svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M13.5 22v-8.2h2.7l.4-3.2h-3.1V8c0-.9.3-1.5 1.6-1.5H17V3.7c-.3 0-1.2-.1-2.2-.1-2.2 0-3.8 1.3-3.8 3.9v2.2H8v3.2h3v8.1h2.5Z"/></svg></a>
16864          <a aria-label="Instagram" href="https://www.instagram.com/plazait"><svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M7.8 2h8.4A5.8 5.8 0 0 1 22 7.8v8.4A5.8 5.8 0 0 1 16.2 22H7.8A5.8 5.8 0 0 1 2 16.2V7.8A5.8 5.8 0 0 1 7.8 2Zm0 2A3.8 3.8 0 0 0 4 7.8v8.4A3.8 3.8 0 0 0 7.8 20h8.4a3.8 3.8 0 0 0 3.8-3.8
16864V7.8A3.8 3.8 0 0 0 16.2 4H7.8Zm9.9 1.7a1.1 1.1 0 1 1 0 2.3 1.1 1.1 0 0 1 0-2.3ZM12 7a5 5 0 1 1 0 10 5 5 0 0 1 0-10Zm0 2a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z"/></svg></a>
16865          <a aria-label="TikTok" href="https://www.tiktok.com/@plazait"><svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M18.9 7.4a6.4 6.4 0 0 1-3.2-1.2v6.3a5.47 5.47 0 1 1-5.5-5.5c.2 0 .4 0 .6.02v2.37a2.92 2.92 0 0 0-.6-.07 2.8 2.8 0 1 0 2.8 2.8V2h2.3a4.6 4.6 0 0 0 4.5 4.1v2.3Z"/></svg></a>
16866        </div>
16867      </div>
16868
16869            <!-- Collapsible Content Wrapper (Non-Homepage Only) -->
16870      <div class="ft-collapsible-content" id="ftCollapsible">
16871      
16872      <div class="ft-col">
16873        <h4 class="ft-title">Tentang NAGARP</h4>
16874        <ul class="ft-links">
16875          <li><a href="https://sunnypork.com/">Tentang NAGARP</a></li>
16876          <li><a href="https://sunnypork.com/">Kontak NAGARP</a></li>
16877          <li><a href="https://sunnypork.com/">Lokasi Toko</a></li>
16878          <li><a href="https://sunnypork.com/">FAQ</a></li>
16879        </ul>
16880        <div class="ft-address">
16881          <p class="company">NAGARP</p>
16882                      <p>JL. Cengkareng Barat No. 56, Jakarta Timur</p>
16883                      <p>Kec. Jakarta, Kota Jakarta, DKI JAKARTA, 11752</p>
16884                  </div>
16885      </div>
16886
16887      <div class="ft-col">
16888        <h4 class="ft-title">Informasi</h4>
16889        <ul class="ft-links">
16890          <li><a href="/syarat-dan-ketentuan">Syarat &amp; Ketentuan</a></li>
16891          <li><a href="/kebijakan-privasi">Kebijakan Privasi</a></li>
16892          <li><a href="/kebijakan-cookie">Kebijakan Cookie</a></li>
16893          <li><a href="/pengiriman-dan-pengembalian">Pengiriman &amp; Pengembalian</a></li>
16894          <li><a href="/pembayaran-dan-cicilan">Pembayaran &amp; Cicilan</a></li>
16895          <li><a href="/garansi-dan-layanan">Garansi &amp; Layanan</a></li>
16896        </ul>
16897
16898        <div class="ft-payments payments-small" aria-label="Metode Pembayaran">
16899          <div class="pay-row">
16900            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-mandiri.png" alt="Mandiri" loading="lazy">
16901            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-bni.gif" alt="BNI" loading="lazy">
16902            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-bri.gif" alt="BRI" loading="lazy">
16903            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-qris.gif" alt="QRIS" loading="lazy">
16904            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-bca.gif" alt="BCA" loading="lazy">
16905          </div>
16906        </div>
16907      </div>
16908
16909            </div>
16910      <!-- End Collapsible Content -->
16911      
16912      <!-- Expand Button (Mobile Only) -->
16913      <button class="ft-expand-btn" id="ftExpandBtn" aria-label="Lihat semua informasi footer">
16914        <span class="expand-text">Selengkapnya</span>
16915        <svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
16916          <path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/>
16917        </svg>
16918      </button>
16919      
16920    </div>
16921  </div>
16922  <div class="ft-bottom">
16923    <div class="container ft-bottom-inner">
16924      <div class="ft-copy">
16925        <span>NAGARP &copy; <span id="yearNow">2026</span></span>
16926        <span>NAGARP OFFICIAL. All rights reserved.</span>
16927      </div>
16928    </div>
16929  </div>
16930  
16931          <style nonce="se3iyzEHMc9FQEB4wSyd0w==">
16932            /* Chat Widget Styles - Lightweight & Minimal */
16933            .chat-widget {
16934                position: fixed;
16935                bottom: 20px;
16936                right: 20px;
16937                z-index: 9999;
16938                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16939            }
16940            
16941            /* Hide floating button on mobile - use bottom nav instead */
16942            @media (max-width: 640px) {
16943                .chat-toggle-btn {
16944                    display: none !important;
16945                }
16946                
16947                /* Ensure popup is still visible on mobile even when button is hidden */
16948                .chat-widget {
16949                    display: block !important;
16950                }
16951                
16952                .chat-popup {
16953                    display: none; /* Hidden by default, shown via JS */
16954                }
16955                
16956                .chat-popup[style*="display: block"],
16957                .chat-popup[style*="display:block"] {
16958                    display: block !important;
16959                }
16960            }
16961            
16962            .chat-toggle-btn {
16963                width: 60px;
16964                height: 60px;
16965                border-radius: 50%;
16966                background: linear-gradient(135deg, #C10DFF 0%, #C10DFF 100%);
16967                border: none;
16968                color: white;
16969                cursor: pointer;
16970                box-shadow: 0 4px 12px rgba(245,202,86,0.3);
16971                position: relative;
16972                transition: transform 0.2s, box-shadow 0.2s;
16973            }
16974            
16975            .chat-toggle-btn:hover {
16976                transform: scale(1.05);
16977                box-shadow: 0 6px 20px rgba(245,202,86,0.4);
16978            }
16979            
16980            #chat-toggle-btn .chat-icon {
16981                position: absolute;
16982                top: 50%;
16983                left: 50%;
16984                transform: translate(-50%, -50%);
16985                width: 28px !important;
16986                height: 28px !important;
16987            }
16988            
16989            .chat-unread-badge {
16990                position: absolute;
16991                top: -5px;
16992                right: -5px;
16993                background: #e2a642;
16994                color: white;
16995                border-radius: 12px;
16996                padding: 2px 6px;
16997                font-size: 11px;
16998                font-weight: 600;
16999                min-width: 20px;
17000                text-align: center;
17001            }
17002            
17003            .chat-popup {
17004                position: absolute;
17005                bottom: 80px;
17006                right: 0;
17007                width: 380px;
17008                max-width: calc(100vw - 40px);
17009                height: 500px;
17010                max-height: calc(100vh - 120px);
17011                background: white;
17012                border-radius: 12px;
17013                box-shadow: 0 10px 40px rgba(0,0,0,0.2);
17014                display: flex;
17015                flex-direction: column;
17016                overflow: hidden;
17017            }
17018            
17019            .chat-popup-header {
17020                background: linear-gradient(135deg, #C10DFF 0%, #C10DFF 100%);
17021                color: white;
17022                padding: 16px 20px;
17023                display: flex;
17024                justify-content: space-between;
17025                align-items: center;
17026                flex-shrink: 0;
17027            }
17028            
17029            .chat-header-title {
17030                display: flex;
17031                flex-direction: column;
17032                gap: 2px;
17033                min-height: 0;
17034            }
17035            
17036            .chat-popup-header h3 {
17037                margin: 0;
17038                font-size: 18px;
17039                font-weight: 600;
17040                line-height: 1.2;
17041            }
17042            
17043            .chat-operating-hours {
17044                font-size: 11px;
17045                opacity: 0.9;
17046                font-weight: 400;
17047                display: flex;
17048                align-items: center;
17049                gap: 4px;
17050                line-height: 1.2;
17051            }
17052            
17053            .chat-operating-hours svg {
17054                width: 12px;
17055                height: 12px;
17056                opacity: 0.8;
17057                flex-shrink: 0;
17058            }
17059            
17060            .chat-header-actions {
17061                display: flex;
17062                align-items: center;
17063                gap: 8px;
17064            }
17065            
17066            /* Notification Toggle Button */
17067            .btn-notif-toggle {
17068                background: none;
17069                border: none;
17070                color: white;
17071                font-size: 18px;
17072                cursor: pointer;
17073                padding: 4px 8px;
17074                transition: all 0.2s;
17075                opacity: 0.8;
17076            }
17077            
17078            .btn-notif-toggle:hover {
17079                opacity: 1;
17080                transform: scale(1.1);
17081            }
17082            
17083            /* Icon states */
17084            .btn-notif-toggle[data-status="not-subscribed"] svg {
17085                opacity: 0.7;
17086            }
17087            
17088            .btn-notif-toggle[data-status="not-subscribed"]:hover svg {
17089                opacity: 1;
17090            }
17091            
17092            .btn-notif-toggle[data-status="subscribed"] svg {
17093                opacity: 1;
17094                animation: bellRing 2s ease-in-out;
17095            }
17096            
17097            .btn-notif-toggle[data-status="blocked"] svg {
17098                color: #e2a642;
17099            }
17100            
17101            .btn-notif-toggle[data-status="not-supported"] {
17102                display: none !important;
17103            }
17104            
17105            @keyframes bellRing {
17106                0%, 100% { transform: rotate(0deg); }
17107                10%, 30% { transform: rotate(-10deg); }
17108                20%, 40% { transform: rotate(10deg); }
17109            }
17110            
17111            /* Notification Info Bar */
17112            .chat-notif-info-bar {
17113                position: absolute;
17114                top: 64px;
17115                left: 0;
17116                right: 0;
17117                background: linear-gradient(to right, #e3f2fd, #f5f5f5);
17118                border-bottom: 1px solid #bbdefb;
17119                padding: 8px 12px;
17120                display: flex;
17121                align-items: center;
17122                gap: 8px;
17123                font-size: 12px;
17124                color: #616161;
17125                z-index: 10;
17126                animation: slideDown 0.3s ease;
17127            }
17128            
17129            .chat-notif-info-bar svg {
17130                flex-shrink: 0;
17131            }
17132            
17133            .chat-notif-info-bar .small {
17134                flex: 1;
17135                line-height: 1.4;
17136            }
17137            
17138            .chat-notif-info-bar .btn-dismiss {
17139                background: none;
17140                border: none;
17141                color: #9e9e9e;
17142                cursor: pointer;
17143                padding: 0 4px;
17144                font-size: 16px;
17145                transition: color 0.2s;
17146            }
17147            
17148            .chat-notif-info-bar .btn-dismiss:hover {
17149                color: #616161;
17150            }
17151            
17152            @keyframes slideDown {
17153                from {
17154                    max-height: 0;
17155                    opacity: 0;
17156                }
17157                to {
17158                    max-height: 50px;
17159                    opacity: 1;
17160                }
17161            }
17162            
17163            .chat-close-btn {
17164                background: none;
17165                border: none;
17166                color: white;
17167                font-size: 28px;
17168                line-height: 1;
17169                cursor: pointer;
17170                padding: 0;
17171                width: 30px;
17172                height: 30px;
17173            }
17174            
17175            .chat-popup-body {
17176                flex: 1 1 auto;
17177                display: flex;
17178                flex-direction: column;
17179                min-height: 0;
17180                overflow: hidden;
17181                height: calc(100% - 64px);
17182                position: relative;
17183                transition: padding-top 0.3s ease;
17184            }
17185            
17186            /* Add padding when info bar is visible */
17187            .chat-popup-body.has-info-bar {
17188                padding-top: 40px;
17189            }
17190            
17191            .chat-view {
17192                flex: 1 1 auto;
17193                display: none;
17194                flex-direction: column;
17195                min-height: 0;
17196                overflow: hidden;
17197            }
17198            
17199            #chat-list-view {
17200                display: flex;
17201            }
17202            
17203            #chat-messages-view {
17204                display: none;
17205            }
17206            
17207            #chat-messages-view.active {
17208                display: flex;
17209                flex-direction: column;
17210            }
17211            
17212            #chat-new-view {
17213                display: none;
17214            }
17215            
17216            #chat-new-view.active {
17217                display: flex;
17218            }
17219            
17220            .chat-conversations-list {
17221                flex: 1;
17222                overflow-y: auto;
17223                padding: 0;
17224                background: white;
17225            }
17226            
17227            /* Custom Scrollbar for Conversations List */
17228            .chat-conversations-list::-webkit-scrollbar {
17229                width: 6px;
17230            }
17231            
17232            .chat-conversations-list::-webkit-scrollbar-track {
17233                background: #f1f1f1;
17234            }
17235            
17236            .chat-conversations-list::-webkit-scrollbar-thumb {
17237                background: #C10DFF;
17238                border-radius: 3px;
17239            }
17240            
17241            .chat-conversations-list::-webkit-scrollbar-thumb:hover {
17242                background: #C10DFF;
17243            }
17244            
17245            /* Firefox */
17246            .chat-conversations-list {
17247                scrollbar-width: thin;
17248                scrollbar-color: #C10DFF #f1f1f1;
17249            }
17250            
17251            .chat-conversation-item {
17252                padding: 12px 16px;
17253                cursor: pointer;
17254                display: flex;
17255                gap: 12px;
17256                align-items: flex-start;
17257                transition: background 0.15s ease;
17258                color: #3a270a;
17259                border-bottom: 1px solid #f0f0f0;
17260            }
17261            
17262            .chat-conversation-item:hover {
17263                background: #f5f5f5;
17264            }
17265            
17266            .chat-conversation-item.active {
17267                background: #FFF5EB;
17268            }
17269            
17270            /* WhatsApp-like Avatar */
17271            .conversation-avatar {
17272                width: 48px;
17273                height: 48px;
17274                border-radius: 50%;
17275                background: linear-gradient(135deg, #C10DFF 0%, #C10DFF 100%);
17276                flex-shrink: 0;
17277                display: flex;
17278                align-items: center;
17279                justify-content: center;
17280                color: white;
17281                font-weight: 600;
17282                font-size: 18px;
17283            }
17284            
17285            .conversation-avatar svg {
17286                width: 24px;
17287                height: 24px;
17288            }
17289            
17290            /* WhatsApp-like Content */
17291            .conversation-content {
17292                flex: 1;
17293                min-width: 0;
17294                display: flex;
17295                flex-direction: column;
17296                gap: 4px;
17297            }
17298            
17299            .conversation-header {
17300                display: flex;
17301                justify-content: space-between;
17302                align-items: center;
17303                gap: 8px;
17304            }
17305            
17306            .conversation-name {
17307                font-weight: 600;
17308                font-size: 15px;
17309                color: #111;
17310                white-space: nowrap;
17311                overflow: hidden;
17312                text-overflow: ellipsis;
17313            }
17314            
17315            .conversation-time {
17316                font-size: 12px;
17317                color: #7d8166;
17318                flex-shrink: 0;
17319            }
17320            
17321            .conversation-preview {
17322                display: flex;
17323                justify-content: space-between;
17324                align-items: center;
17325                gap: 8px;
17326            }
17327            
17328            .conversation-message {
17329                font-size: 14px;
17330                color: #667781;
17331                white-space: nowrap;
17332                overflow: hidden;
17333                text-overflow: ellipsis;
17334                margin: 0;
17335                flex: 1;
17336            }
17337            
17338            .conversation-unread {
17339                background: #25D366;
17340                color: white;
17341                border-radius: 12px;
17342                padding: 2px 8px;
17343                font-size: 12px;
17344                font-weight: 600;
17345                min-width: 20px;
17346                text-align: center;
17347                flex-shrink: 0;
17348            }
17349            
17350            /* Active item styling */
17351            .chat-conversation-item.active .conversation-name {
17352                color: #C10DFF;
17353            }
17354            
17355            .chat-conversation-item.active .conversation-time {
17356                color: #C10DFF;
17357            }
17358            
17359            .chat-new-btn {
17360                margin: 12px;
17361                padding: 12px;
17362                background: linear-gradient(135deg, #C10DFF 0%, #C10DFF 100%);
17363                color: white;
17364                border: none;
17365                border-radius: 8px;
17366                cursor: pointer;
17367                font-weight: 500;
17368                font-size: 14px;
17369                display: flex;
17370                align-items: center;
17371                justify-content: center;
17372                gap: 8px;
17373                transition: all 0.2s;
17374                flex-shrink: 0;
17375            }
17376            
17377            .chat-new-btn:hover {
17378                transform: translateY(-1px);
17379                box-shadow: 0 4px 8px rgba(245,202,86,0.3);
17380            }
17381            
17382            .chat-conversation-title {
17383                flex: 1;
17384                font-weight: 600;
17385                font-size: 16px;
17386                color: #1f2937;
17387            }
17388            
17389            .chat-messages-container {
17390                flex: 1;
17391                overflow-y: auto;
17392                padding: 16px;
17393                display: flex;
17394                flex-direction: column;
17395                gap: 8px;
17396                min-height: 0;
17397                background: #E5DDD5;
17398            }
17399            
17400            /* Custom Scrollbar for Messages Container */
17401            .chat-messages-container::-webkit-scrollbar {
17402                width: 6px;
17403            }
17404            
17405            .chat-messages-container::-webkit-scrollbar-track {
17406                background: rgba(0, 0, 0, 0.05);
17407            }
17408            
17409            .chat-messages-container::-webkit-scrollbar-thumb {
17410                background: rgba(245,202,86,0.6);
17411                border-radius: 3px;
17412            }
17413            
17414            .chat-messages-container::-webkit-scrollbar-thumb:hover {
17415                background: #C10DFF;
17416            }
17417            
17418            /* Firefox */
17419            .chat-messages-container {
17420                scrollbar-width: thin;
17421                scrollbar-color: rgba(245,202,86,0.6) rgba(0, 0, 0, 0.05);
17422            }
17423            
17424            /* WhatsApp Group-like Messages */
17425            .chat-message {
17426                display: flex;
17427                margin-bottom: 4px;
17428                animation: fadeIn 0.2s ease;
17429            }
17430            
17431            @keyframes fadeIn {
17432                from { opacity: 0; transform: translateY(10px); }
17433                to { opacity: 1; transform: translateY(0); }
17434            }
17435            
17436            .chat-message.mine {
17437                justify-content: flex-end;
17438            }
17439            
17440            .chat-message-bubble {
17441                max-width: 80%;
17442                padding: 6px 8px 8px 10px;
17443                border-radius: 8px;
17444                position: relative;
17445                box-shadow: 0 1px 0.5px rgba(0,0,0,0.13);
17446            }
17447            
17448            /* Bubble from others (Admin/CS) */
17449            .chat-message:not(.mine) .chat-message-bubble {
17450                background: #FFFFFF;
17451                border-radius: 0 8px 8px 8px;
17452            }
17453            
17454            /* Bubble tail for others */
17455            .chat-message:not(.mine) .chat-message-bubble::before {
17456                content: '';
17457                position: absolute;
17458                top: 0;
17459                left: -8px;
17460                width: 0;
17461                height: 0;
17462                border-style: solid;
17463                border-width: 0 8px 13px 0;
17464                border-color: transparent #FFFFFF transparent transparent;
17465            }
17466            
17467            /* Bubble from user (mine) */
17468            .chat-message.mine .chat-message-bubble {
17469                background: #e4f5d0; /* Light blue for user messages */
17470                border-radius: 8px 0 8px 8px;
17471            }
17472            
17473            /* Bubble tail for mine */
17474            .chat-message.mine .chat-message-bubble::before {
17475                content: '';
17476                position: absolute;
17477                top: 0;
17478                right: -8px;
17479                width: 0;
17480                height: 0;
17481                border-style: solid;
17482                border-width: 0 0 13px 8px;
17483                border-color: transparent transparent transparent #eaf5d0;
17484            }
17485            
17486            .message-sender {
17487                font-size: 12.5px;
17488                font-weight: 600;
17489                color: #C10DFF !important;
17490                margin-bottom: 4px;
17491            }
17492            
17493            .chat-message.mine .message-sender {
17494                display: none;
17495            }
17496            
17497            /* Message text */
17498            .message-text {
17499                font-size: 14px;
17500                line-height: 19px;
17501                color: #ffffff !important;
17502                word-wrap: break-word;
17503                white-space: pre-wrap;
17504                margin-bottom: 4px;
17505            }
17506            
17507            /* Message metadata (time + status) */
17508            .message-meta {
17509                display: flex;
17510                align-items: center;
17511                justify-content: flex-end;
17512                gap: 4px;
17513                margin-top: 2px;
17514            }
17515            
17516            .message-time {
17517                font-size: 11px;
17518                color: #728166 !important;
17519                line-height: 15px;
17520            }
17521            
17522            /* Use multiple selectors to override global styles */
17523            .chat-message-bubble .message-status,
17524            .message-meta .message-status,
17525            .chat-message .message-status {
17526                width: 16px !important;
17527                height: 16px !important;
17528                min-width: 16px !important;
17529                min-height: 16px !important;
17530                max-width: 16px !important;
17531                max-height: 16px !important;
17532                flex-shrink: 0;
17533                color: #728166 !important;
17534                fill: currentColor !important;
17535            }
17536            
17537            .chat-message:not(.mine) .message-status {
17538                display: none;
17539            }
17540            
17541            /* Loading dots animation */
17542            @keyframes loadingDots {
17543                0%, 20% { content: '.'; }
17544                40% { content: '..'; }
17545                60%, 100% { content: '...'; }
17546            }
17547            
17548            .message-status.loading::after {
17549                content: '...';
17550                animation: loadingDots 1.5s infinite;
17551                font-size: 16px;
17552                line-height: 16px;
17553            }
17554            
17555            .message-status.loading path {
17556                display: none;
17557            }
17558            
17559            .message-status.failed {
17560                color: #e2a642 !important;
17561                cursor: pointer;
17562            }
17563            
17564            .message-status.sent {
17565                color: #667781 !important;
17566            }
17567            
17568            .chat-new-form {
17569                flex: 1;
17570                overflow-y: auto;
17571                padding: 16px;
17572                display: flex;
17573                flex-direction: column;
17574            }
17575            
17576            .chat-new-form form {
17577                display: flex;
17578                flex-direction: column;
17579                gap: 16px;
17580                flex: 1;
17581            }
17582            
17583            .chat-new-form .form-group {
17584                display: flex;
17585                flex-direction: column;
17586                gap: 6px;
17587            }
17588            
17589            .chat-new-form label {
17590                font-size: 14px;
17591                font-weight: 500;
17592                color: #374151;
17593            }
17594            
17595            .chat-new-form .form-control {
17596                width: 100%;
17597                padding: 10px 12px;
17598                border: 1px solid #d1d5db;
17599                border-radius: 8px;
17600                font-size: 14px;
17601                font-family: inherit;
17602                transition: border-color 0.2s;
17603                box-sizing: border-box;
17604            }
17605            
17606            .chat-new-form .form-control:focus {
17607                outline: none;
17608                border-color: #C10DFF;
17609                box-shadow: 0 0 0 3px rgba(245,202,86,0.1);
17610            }
17611            
17612            .chat-new-form textarea.form-control {
17613                resize: vertical;
17614                min-height: 100px;
17615            }
17616            
17617            .chat-new-form .btn {
17618                padding: 12px 20px;
17619                border: none;
17620                border-radius: 8px;
17621                font-size: 15px;
17622                font-weight: 500;
17623                cursor: pointer;
17624                transition: all 0.2s;
17625                text-align: center;
17626            }
17627            
17628            .chat-new-form .btn-primary {
17629                background: linear-gradient(135deg, #C10DFF 0%, #C10DFF 100%);
17630                color: white;
17631            }
17632            
17633            .chat-new-form .btn-primary:hover {
17634                transform: translateY(-1px);
17635                box-shadow: 0 4px 8px rgba(245,202,86,0.3);
17636            }
17637            
17638            .chat-new-form .btn-primary:active {
17639                transform: scale(0.98);
17640            }
17641            
17642            .chat-new-form .btn-block {
17643                width: 100%;
17644                margin-top: auto;
17645            }
17646            
17647            .chat-message-form {
17648                padding: 12px;
17649                border-top: 1px solid #e5e7eb;
17650                display: flex;
17651                flex-wrap: wrap;
17652                gap: 8px;
17653                align-items: flex-end;
17654                flex-shrink: 0;
17655                background: white;
17656            }
17657            
17658            .chat-product-card {
17659                flex: 1 1 100%;
17660                max-width: 100%;
17661                display: flex;
17662                gap: 12px;
17663                padding: 12px;
17664                background: #f3f4f6;
17665                border-radius: 8px;
17666                margin-bottom: 4px;
17667                border: 1px solid #e5e7eb;
17668            }
17669            
17670            .chat-product-card img {
17671                width: 60px;
17672                height: 60px;
17673                object-fit: cover;
17674                border-radius: 6px;
17675                flex-shrink: 0;
17676            }
17677            
17678            .chat-product-card-content {
17679                flex: 1;
17680                min-width: 0;
17681            }
17682            
17683            .chat-product-card-name {
17684                font-weight: 500;
17685                font-size: 14px;
17686                color: #1f2937;
17687                margin-bottom: 4px;
17688                white-space: nowrap;
17689                overflow: hidden;
17690                text-overflow: ellipsis;
17691            }
17692            
17693            .chat-product-card-variant {
17694                font-size: 12px;
17695                color: #74806b;
17696                margin-bottom: 4px;
17697            }
17698            
17699            .chat-product-card-price {
17700                font-size: 13px;
17701                color: #8bea66;
17702                font-weight: 600;
17703            }
17704            
17705            .chat-product-card-remove {
17706                align-self: flex-start;
17707                background: none;
17708                border: none;
17709                color: #9ca3af;
17710                cursor: pointer;
17711                padding: 4px;
17712                font-size: 18px;
17713                line-height: 1;
17714                transition: color 0.2s;
17715            }
17716            
17717            .chat-product-card-remove:hover {
17718                color: #e2a642;
17719            }
17720            
17721            /* Product card in messages (read-only display) */
17722            .message-product-card {
17723                display: flex;
17724                gap: 10px;
17725                padding: 10px;
17726                background: rgba(255, 255, 255, 0.5);
17727                border-radius: 8px;
17728                margin-bottom: 6px;
17729                border: 1px solid rgba(0, 0, 0, 0.08);
17730                align-items: center;
17731            }
17732            
17733            .chat-message.mine .message-product-card {
17734                background: rgba(255, 255, 255, 0.7);
17735            }
17736            
17737            .message-product-card img {
17738                width: 50px;
17739                height: 50px;
17740                object-fit: cover;
17741                border-radius: 6px;
17742                flex-shrink: 0;
17743            }
17744            
17745            .message-product-info {
17746                flex: 1;
17747                min-width: 0;
17748            }
17749            
17750            .message-product-name {
17751                font-weight: 500;
17752                font-size: 13px;
17753                color: #3a270a;
17754                margin-bottom: 2px;
17755                overflow: hidden;
17756                text-overflow: ellipsis;
17757                display: -webkit-box;
17758                -webkit-line-clamp: 2;
17759                -webkit-box-orient: vertical;
17760                line-height: 1.3;
17761            }
17762            
17763            .message-product-variant {
17764                font-size: 11px;
17765                color: #78806b;
17766                margin-bottom: 2px;
17767            }
17768            
17769            .message-product-price {
17770                font-size: 12px;
17771                color: #c7ea66;
17772                font-weight: 600;
17773            }
17774            
17775            .message-product-link {
17776                flex-shrink: 0;
17777                width: 32px;
17778                height: 32px;
17779                display: flex;
17780                align-items: center;
17781                justify-content: center;
17782                background: rgba(102, 126, 234, 0.1);
17783                border-radius: 6px;
17784                color: #c7ea66;
17785                text-decoration: none;
17786                transition: all 0.2s;
17787            }
17788            
17789            .message-product-link:hover {
17790                background: #b7ea66;
17791                color: white;
17792            }
17793            
17794            .message-product-link svg {
17795                width: 16px;
17796                height: 16px;
17797            }
17798            
17799            .chat-message-input {
17800                flex: 1;
17801                border: 1px solid #d1d5db;
17802                border-radius: 20px;
17803                padding: 10px 16px;
17804                resize: none;
17805                max-height: 100px;
17806                font-family: inherit;
17807                font-size: 14px;
17808                line-height: 1.5;
17809                box-sizing: border-box;
17810                overflow-y: hidden;
17811            }
17812            
17813            .chat-message-input:focus {
17814                outline: none;
17815                border-color: #C10DFF;
17816                box-shadow: 0 0 0 3px rgba(245,202,86,0.1);
17817            }
17818            
17819            .chat-send-btn {
17820                width: 40px;
17821                height: 40px;
17822                border-radius: 50%;
17823                background: linear-gradient(135deg, #C10DFF 0%, #C10DFF 100%);
17824                border: none;
17825                color: white;
17826                cursor: pointer;
17827                flex-shrink: 0;
17828                display: flex;
17829                align-items: center;
17830                justify-content: center;
17831                transition: all 0.2s;
17832            }
17833            
17834            .chat-send-btn:hover {
17835                transform: scale(1.05);
17836                box-shadow: 0 2px 8px rgba(245,202,86,0.3);
17837            }
17838            
17839            .chat-send-btn:active {
17840                transform: scale(0.95);
17841            }
17842            
17843            .chat-loading {
17844                text-align: center;
17845                padding: 20px;
17846                color: #6b7280;
17847            }
17848            
17849            /* Date Separator (WhatsApp-style) */
17850            .date-separator {
17851                display: flex;
17852                align-items: center;
17853                gap: 12px;
17854                margin: 24px 0 16px;
17855                opacity: 0;
17856                animation: fadeInSeparator 0.3s ease forwards;
17857            }
17858
17859            @keyframes fadeInSeparator {
17860                from {
17861                    opacity: 0;
17862                    transform: translateY(-10px);
17863                }
17864                to {
17865                    opacity: 1;
17866                    transform: translateY(0);
17867                }
17868            }
17869
17870            .date-separator-line {
17871                flex: 1;
17872                height: 1px;
17873                background: linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.1) 50%, transparent);
17874            }
17875
17876            .date-separator-text {
17877                flex-shrink: 0;
17878                padding: 4px 12px;
17879                background: #e9ecef;
17880                color: #6c757d;
17881                font-size: 11px;
17882                font-weight: 600;
17883                text-transform: uppercase;
17884                letter-spacing: 0.5px;
17885                border-radius: 12px;
17886                box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
17887            }
17888            
17889            /* Mobile responsive */
17890            @media (max-width: 640px) {
17891                .chat-popup {
17892                    position: fixed !important; /* Override absolute positioning */
17893                    bottom: 45px !important; /* Above bottom nav */
17894                    right: 0 !important;
17895                    left: 0 !important;
17896                    width: 100% !important;
17897                    max-width: 100% !important;
17898                    /* Use dvh (dynamic viewport height) for browser UI awareness */
17899                    height: calc(100dvh - 45px) !important;
17900                    max-height: calc(100dvh - 45px) !important;
17901                    /* Fallback for browsers that don't support dvh */
17902                    height: calc(var(--viewport-height, 100vh) - 45px) !important;
17903                    max-height: calc(var(--viewport-height, 100vh) - 45px) !important;
17904                    border-radius: 0 !important;
17905                    z-index: 9999 !important;
17906                }
17907                
17908                .chat-popup-header {
17909                    border-radius: 0;
17910                }
17911                
17912                .chat-messages-container,
17913                .chat-conversations-list {
17914                    /* Adjust for mobile keyboard using dynamic viewport */
17915                    max-height: calc(100dvh - 190px);
17916                    /* Fallback for browsers that don't support dvh */
17917                    max-height: calc(var(--viewport-height, 100vh) - 190px);
17918                }
17919            }
17920        </style>
17921                <!-- Chat Widget Button -->
17922        <div id="chat-widget" class="chat-widget" 
17923             data-user-id=""
17924             data-is-logged-in="false"
17925             data-csrf-token="df5d776748b1ed8c8dd0d987bae99b1ee3dea6c6dcbe0eedb1bea766c56d569a">
17926            <button id="chat-toggle-btn" class="chat-toggle-btn" aria-label="Toggle chat">
17927                <svg class="chat-icon" width="28" height="28" viewBox="0 0 640 640" fill="currentColor">
17928                    <path d="M320 128C241 128 175.3 185.3 162.3 260.7C171.6 257.7 181.6 256 192 256L208 256C234.5 256 256 277.5 256 304L256 400C256 426.5 234.5 448 208 448L192 448C139 448 96 405 96 352L96 288C96 164.3 196.3 64 320 64C443.7 64 544 164.3 544 288L544 456.1C544 522.4 490.2 576.1 423.9 576.1L336 576L304 576C277.5 576 256 554.5 256 528C256 501.5 277.5 480 304 480L336 480C362.5 480 384 501.5 384 528L384 528L424 528C463.8 528 496 495.8 496 456L496 435.1C481.9 443.3 465.5 447.9 448 447.9L432 447.9C405.5 447.9 384 426.4 384 399.9L384 303.9C384 277.4 405.5 255.9 432 255.9L448 255.9C458.4 255.9 468.3 257.5 477.7 260.6C464.7 185.3 399.1 127.9 320 127.9z"/>
17929                </svg>
17930                <span id="chat-unread-badge" class="chat-unread-badge" style="display: none;">0</span>
17931            </button>
17932            
17933            <div id="chat-popup" class="chat-popup" style="display: none;">
17934                <div class="chat-popup-header">
17935                    <div class="chat-header-title">
17936                        <h3>Bantuan</h3>
17937                        <span id="chatOperatingHours" class="chat-operating-hours" style="display: none;"></span>
17938                    </div>
17939                    <div class="chat-header-actions">
17940                        <button id="chatNotifToggle" class="btn-notif-toggle" data-status="not-supported" title="Push notification" style="display: none;">
17941                            <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
17942                                <path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/>
17943                            </svg>
17944                        </button>
17945                        <button id="chat-close-btn" class="chat-close-btn" aria-label="Close chat">
17945×</button>
17946                    </div>
17947                </div>
17948                
17949                <!-- Notification Info Bar (conditional) -->
17950                <div class="chat-notif-info-bar" id="chatNotifInfoBar" style="display:none;">
17951                    <svg width="14" height="14" viewBox="0 0 24 24" fill="#ffc107">
17952                        <path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"/>
17953                    </svg>
17954                    <span class="small">Aktifkan notifikasi untuk pemberitahuan balasan</span>
17955                    <button class="btn-dismiss" onclick="ChatNotification.dismissInfo()">✕</button>
17956                </div>
17957                
17958                <div class="chat-popup-body">
17959                    <!-- Conversations List View -->
17960                    <div id="chat-list-view" class="chat-view">
17961                        <div id="chat-conversations-list" class="chat-conversations-list">
17962                            <div class="chat-loading">Memuat...</div>
17963                            <!-- Conversation Item Structure (rendered by JS):
17964                            <div class="chat-conversation-item">
17965                                <div class="conversation-avatar">
17966                                    <svg>...</svg>
17967                                </div>
17968                                <div class="conversation-content">
17969                                    <div class="conversation-header">
17970                                        <span class="conversation-name">Admin Support</span>
17971                                        <span class="conversation-time">10:30</span>
17972                                    </div>
17973                                    <div class="conversation-preview">
17974                                        <p class="conversation-message">Terima kasih telah menghubungi kami...</p>
17975                                        <span class="conversation-unread">2</span>
17976                                    </div>
17977                                </div>
17978                            </div>
17979                            -->
17980                        </div>
17981                        <button id="chat-new-btn" class="chat-new-btn">
17982                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
17983                                <line x1="12" y1="5" x2="12" y2="19"></line>
17984                                <line x1="5" y1="12" x2="19" y2="12"></line>
17985                            </svg>
17986                            Percakapan Baru
17987                        </button>
17988                    </div>
17989                    
17990                    <!-- Chat Messages View -->
17991                    <div id="chat-messages-view" class="chat-view" style="display: none;">
17992                        <div id="chat-messages-container" class="chat-messages-container">
17993                            <div class="chat-loading">Memuat pesan...</div>
17994                            <!-- Message Structure (rendered by JS):
17995                            <div class="chat-message"> (or .chat-message.mine)
17996                                <div class="chat-message-bubble">
17997                                    <div class="message-sender">Admin Support</div> (only for non-mine)
17998                                    <div class="message-text">Halo, ada yang bisa dibantu?</div>
17999                                    <div class="message-meta">
18000                                        <span class="message-time">10:30</span>
18001                                        <svg class="message-status">...</svg> (only for mine)
18002                                    </div>
18003                                </div>
18004                            </div>
18005                            -->
18006                        </div>
18007                        
18008                        <form id="chat-message-form" class="chat-message-form">
18009                            <input type="hidden" id="chat-current-conversation-id" value="">
18010                            <textarea 
18011                                id="chat-message-input" 
18012                                class="chat-message-input" 
18013                                placeholder="Ketik pesan..."
18014                                rows="1"
18015                                maxlength="5000"
18016                            ></textarea>
18017                            <button type="submit" class="chat-send-btn" aria-label="Send message">
18018                                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
18019                                    <line x1="22" y1="2" x2="11" y2="
1801913"></line>
18020                                    <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
18021                                </svg>
18022                            </button>
18023                        </form>
18024                    </div>
18025                    
18026                    <!-- New Chat View -->
18027                    <div id="chat-new-view" class="chat-view" style="display: none;">
18028                        <div class="chat-messages-header">
18029                            <button id="chat-new-back-btn" class="chat-back-btn" aria-label="Back to list">
18030                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
18031                                    <path d="M19 12H5M12 19l-7-7 7-7"/>
18032                                </svg>
18033                            </button>
18034                            <div class="chat-conversation-title">Percakapan Baru</div>
18035                        </div>
18036                        
18037                        <div class="chat-new-form">
18038                            <form id="chat-start-form">
18039                                <div class="form-group">
18040                                    <label for="chat-new-subject">Subjek</label>
18041                                    <input 
18042                                        type="text" 
18043                                        id="chat-new-subject" 
18044                                        class="form-control" 
18045                                        placeholder="Bantuan Pelanggan"
18046                                        maxlength="255"
18047                                    >
18048                                </div>
18049                                <div class="form-group">
18050                                    <label for="chat-new-message">Pesan</label>
18051                                    <textarea 
18052                                        id="chat-new-message" 
18053                                        class="form-control" 
18054                                        rows="4" 
18055                                        placeholder="Tulis pertanyaan Anda..."
18056                                        required
18057                                        maxlength="5000"
18058                                    ></textarea>
18059                                </div>
18060                                <button type="submit" class="btn btn-primary btn-block">Kirim</button>
18061                            </form>
18062                        </div>
18063                    </div>
18064                </div>
18065            </div>
18066        </div>
18067        
18068        <!-- Login Required Modal -->
18069        <div class="modal-overlay" id="chatLoginModal">
18070            <div class="modal-dialog">
18071                <div class="modal-header">
18072                    <h3 class="modal-title">
18073                        <div class="modal-icon info">
18074                            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18075                                <path d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z"/>
18076                            </svg>
18077                        </div>
18078                        Login Diperlukan
18079                    </h3>
18080                </div>
18081                <div class="modal-body">
18082                    <p class="modal-message">Anda harus login terlebih dahulu untuk menggunakan fitur chat bantuan pelanggan.</p>
18083                    <div class="modal-actions">
18084                        <button class="modal-btn modal-btn-secondary" id="chatLoginCancel">Batal</button>
18085                        <button class="modal-btn modal-btn-primary" id="chatLoginConfirm">Login Sekarang</button>
18086                    </div>
18087                </div>
18088            </div>
18089        </div>
18090        
18090<script src="https://plazait.co.id/assets/js/chat-widget.js" nonce="se3iyzEHMc9FQEB4wSyd0w=="></script>
18090<script data-inlined="chat-notification.js">
18091/**
18092 * Customer Chat Push Notification Manager
18093 * Handles browser push notification subscription for customers
18094 * Version: 1.0.0
18095 * Date: 2026-01-19
18096 */
18097
18098const ChatNotification = {
18099    // State
18100    status: 'not-supported', // not-supported, not-subscribed, subscribed, blocked
18101    subscription: null,
18102    vapidPublicKey: null, // Will be loaded from server
18103    
18104    /**
18105     * Initialize notification manager
18106     */
18107    async init() {
18108        // Check if user is logged in (only for logged-in users)
18109        if (!this.isUserLoggedIn()) {
18110            this.updateUI('not-supported');
18111            return;
18112        }
18113        
18114        // Check browser support
18115        if (!this.isSupported()) {
18116            this.updateUI('not-supported');
18117            return;
18118        }
18119        
18120        // Load VAPID key from server
18121        await this.loadVapidKey();
18122        
18123        if (!this.vapidPublicKey) {
18124            this.updateUI('not-supported');
18125            return;
18126        }
18127        
18128        // Register service worker if needed
18129        await this.registerServiceWorker();
18130        
18131        // Check current permission
18132        const permission = Notification.permission;
18133        
18134        if (permission === 'denied') {
18135            this.updateUI('blocked');
18136        } else if (permission === 'granted') {
18137            // Check if already subscribed
18138            const isSubscribed = await this.checkSubscriptionStatus();
18139            this.updateUI(isSubscribed ? 'subscribed' : 'not-subscribed');
18140        } else {
18141            // Default: not subscribed yet
18142            this.updateUI('not-subscribed');
18143            this.showInfoBar();
18144        }
18145    },
18146    
18147    /**
18148     * Check if user is logged in
18149     */
18150    isUserLoggedIn() {
18151        const metaTag = document.querySelector('meta[name="user-logged-in"]');
18152        return metaTag ? metaTag.getAttribute('content') === 'true' : false;
18153    },
18154    
18155    /**
18156     * Check if browser supports push notifications
18157     */
18158    isSupported() {
18159        return (
18160            'serviceWorker' in navigator &&
18161            'PushManager' in window &&
18162            'Notification' in window
18163        );
18164    },
18165    
18166    /**
18167     * Load VAPID public key from server
18168     */
18169    async loadVapidKey() {
18170        try {
18171            const response = await fetch('/api/chat/notifications/vapid_key', {
18172                method: 'GET',
18173                headers: {
18174                    'Accept': 'application/json'
18175                }
18176            });
18177            
18178            if (!response.ok) {
18179                return false;
18180            }
18181            
18182            const data = await response.json();
18183            if (data.success && data.data?.public_key) {
18184                this.vapidPublicKey = data.data.public_key;
18185                return true;
18186            }
18187            
18188            return false;
18189            
18190        } catch (error) {
18191            console.error('[ChatNotification] Error loading VAPID key:', error);
18192            return false;
18193        }
18194    },
18195    
18196    /**
18197     * Register service worker
18198     */
18199    async registerServiceWorker() {
18200        if (!('serviceWorker' in navigator)) return null;
18201        
18202        try {
18203            const registration = await navigator.serviceWorker.register('/sw-push.js', {
18204                scope: '/'
18205            });
18206            return registration;
18207        } catch (error) {
18208            return null;
18209        }
18210    },
18211    
18212    /**
18213     * Check subscription status from server
18214     */
18215    async checkSubscriptionStatus() {
18216        try {
18217            const response = await fetch('/api/chat/notifications/status', {
18218                method: 'GET',
18219                credentials: 'include',
18220                headers: {
18221                    'Accept': 'application/json'
18222                }
18223            });
18224            
18225            if (!response.ok) {
18226                return false;
18227            }
18228            
18229            const data = await response.json();
18230            return data.data?.subscribed || false;
18231            
18232        } catch (error) {
18233            console.error('[ChatNotification] Error checking subscription:', error);
18234            return false;
18235        }
18236    },
18237    
18238    /**
18239     * Update UI based on status
18240     */
18241    updateUI(status) {
18242        this.status = status;
18243        const btn = document.getElementById('chatNotifToggle');
18244        
18245        if (!btn) {
18246            // Retry after a short delay (button might not be in DOM yet)
18247            setTimeout(() => this.updateUI(status), 100);
18248            return;
18249        }
18250        
18251        btn.setAttribute('data-status', status);
18252        
18253        // Show/hide button based on support
18254        if (status === 'not-supported') {
18255            btn.style.display = 'none';
18256        } else {
18257            btn.style.display = 'block';
18258        }
18259        
18260        // Update SVG icon (bell vs bell-slash)
18261        const svg = btn.querySelector('svg');
18262        if (svg) {
18263            if (status === 'subscribed') {
18264                // Bell icon (active)
18265                svg.innerHTML = '<path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/>';
18266            } else {
18267                // Bell slash icon (inactive/blocked)
18268                svg.innerHTML = '<path d="M20 18.69L7.84 6.14 5.27 3.49 4 4.76l2.8 2.8v.01c-.52.99-.8 2.16-.8 3.42v5l-2 2v1h13.73l2 2L21 19.72l-1-1.03zM12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-7.32V11c0-3.08-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68c-.15.03-.29.08-.42.12-.1.03-.2.07-.3.11h-.01c-.01 0-.01 0-.02.01-.23.09-.46.2-.68.31 0 0-.01 0-.01.01L18 14.68z"/>';
18269            }
18270        }
18271        
18272        // Update tooltip
18273        const tooltips = {
18274            'not-subscribed': 'Klik untuk aktifkan notifikasi',
18275            'subscribed': 'Notifikasi aktif',
18276            'blocked': 'Notifikasi diblokir'
18277        };
18278        
18279        if (tooltips[status]) {
18280            btn.title = tooltips[status];
18281        }
18282        
18283        // Hide info bar if subscribed or blocked
18284        if (status === 'subscribed' || status === 'blocked') {
18285            this.hideInfoBar();
18286        }
18287    },
18288    
18289    /**
18290     * Show info bar (if not dismissed)
18291     */
18292    showInfoBar() {
18293        if (localStorage.getItem('chat_notif_info_dismissed')) {
18294            return;
18295        }
18296        
18297        const infoBar = document.getElementById('chatNotifInfoBar');
18298        const popupBody = document.getElementById('chat-popup')?.querySelector('.chat-popup-body');
18299        
18300        if (infoBar) {
18301            infoBar.style.display = 'flex';
18302
18303            // Bind dismiss button here so it's always attached when bar is visible
18304            const btn = infoBar.querySelector('#chatNotifDismissBtn');
18305            if (btn && !btn._dismissBound) {
18306                btn._dismissBound = true;
18307                btn.addEventListener('click', () => {
18308                    infoBar.style.display = 'none';
18309                    localStorage.setItem('chat_notif_info_dismissed', '1');
18310                    if (popupBody) popupBody.classList.remove('has-info-bar');
18311                });
18312            }
18313        }
18314        
18315        if (popupBody) {
18316            popupBody.classList.add('has-info-bar');
18317        }
18318    },
18319    
18320    /**
18321     * Hide info bar
18322     */
18323    hideInfoBar() {
18324        const infoBar = document.getElementById('chatNotifInfoBar');
18325        const popupBody = document.getElementById('chat-popup')?.querySelector('.chat-popup-body');
18326        
18327        if (infoBar) {
18328            infoBar.style.display = 'none';
18329        }
18330        
18331        if (popupBody) {
18332            popupBody.classList.remove('has-info-bar');
18333        }
18334    },
18335    
18336    /**
18337     * Dismiss info bar permanently
18338     */
18339    dismissInfo() {
18340        this.hideInfoBar();
18341        localStorage.setItem('chat_notif_info_dismissed', '1');
18342    },
18343    
18344    /**
18345     * Handle bell icon click
18346     */
18347    async handleToggleClick() {
18348        if (this.status === 'not-subscribed') {
18349            await this.subscribe();
18350        } else if (this.status === 'subscribed') {
18351            await this.unsubscribe();
18352        } else if (this.status === 'blocked') {
18353            this.showUnblockHelp();
18354        }
18355    },
18356    
18357    /**
18358     * Subscribe to push notifications
18359     */
18360    async subscribe() {
18361        try {
18362            // Request permission
18363            const permission = await Notification.requestPermission();
18364            
18365            if (permission === 'denied') {
18366                this.updateUI('blocked');
18367                this.showToast('Notifikasi diblokir', 'error');
18368                return;
18369            }
18370            
18371            if (permission !== 'granted') {
18372                return;
18373            }
18374            
18375            // Get service worker registration
18376            const registration = await navigator.serviceWorker.ready;
18377            
18378            // Subscribe to push
18379            const subscription = await registration.pushManager.subscribe({
18380                userVisibleOnly: true,
18381                applicationServerKey: this.urlBase64ToUint8Array(this.vapidPublicKey)
18382            });
18383            
18384            // Send subscription to server
18385            const response = await fetch('/api/chat/notifications/subscribe', {
18386                method: 'POST',
18387                credentials: 'include',
18388                headers: {
18389                    'Content-Type': 'application/json',
18390                    'Accept': 'application/json'
18391                },
18392                body: JSON.stringify(subscription.toJSON())
18393            });
18394            
18395            if (!response.ok) {
18396                throw new Error('Failed to save subscription');
18397            }
18398            
18399            const result = await response.json();
18400            
18401            this.subscription = subscription;
18402            this.updateUI('subscribed');
18403            this.hideInfoBar();
18404            this.showToast('Notifikasi berhasil diaktifkan!', 'success');
18405            
18406            // Send test notification
18407            setTimeout(() => this.sendTestNotification(), 1000);
18408            
18409        } catch (error) {
18410            console.error('[ChatNotification] Subscribe error:', error);
18411            this.showToast('Gagal mengaktifkan notifikasi', 'error');
18412        }
18413    },
18414    
18415    /**
18416     * Unsubscribe from push notifications
18417     */
18418    async unsubscribe() {
18419        try {
18420            // Confirm first
18421            if (!confirm('Nonaktifkan notifikasi chat?')) {
18422                return;
18423            }
18424            
18425            // Get current subscription
18426            const registration = await navigator.serviceWorker.ready;
18427            const subscription = await registration.pushManager.getSubscription();
18428            
18429            if (subscription) {
18430                // Unsubscribe from browser
18431                await subscription.unsubscribe();
18432                
18433                // Tell server
18434                await fetch('/api/chat/notifications/unsubscribe', {
18435                    method: 'POST',
18436                    credentials: 'include',
18437                    headers: {
18438                        'Content-Type': 'application/json',
18439                        'Accept': 'application/json'
18440                    },
18441                    body: JSON.stringify(subscription.toJSON())
18442                });
18443            }
18444            
18445            this.subscription = null;
18446            this.updateUI('not-subscribed');
18447            this.showInfoBar();
18448            this.showToast('Notifikasi dinonaktifkan', 'info');
18449            
18450        } catch (error) {
18451            console.error('[ChatNotification] Unsubscribe error:', error);
18452            this.showToast('Gagal menonaktifkan notifikasi', 'error');
18453        }
18454    },
18455    
18456    /**
18457     * Send test notification
18458     */
18459    async sendTestNotification() {
18460        try {
18461            const response = await fetch('/api/chat/notifications/send_test', {
18462                method: 'POST',
18463                credentials: 'include',
18464                headers: {
18465                    'Accept': 'application/json'
18466                }
18467            });
18468            
18469            if (response.ok) {
18470                // Test notification sent successfully
18471            }
18472        } catch (error) {
18473            // Test notification failed silently
18474        }
18475    },
18476    
18477    /**
18478     * Show unblock help modal/message
18479     */
18480    showUnblockHelp() {
18481        const message = `
18482            Notifikasi diblokir oleh browser.\n\n
18483            Untuk mengaktifkan:\n
18484            1. Klik ikon gembok/info di address bar\n
18485            2. Cari "Notifications" atau "Notifikasi"\n
18486            3. Ubah menjadi "Allow" atau "Izinkan"\n
18487            4. Refresh halaman ini
18488        `.trim().replace(/\s+/g, ' ');
18489        
18490        alert(message);
18491    },
18492    
18493    /**
18494     * Show toast notification
18495     */
18496    showToast(message, type = 'info') {
18497        // Simple toast implementation
18498        const toast = document.createElement('div');
18499        toast.className = `chat-toast chat-toast-${type}`;
18500        toast.textContent = message;
18501        toast.style.cssText = `
18502            position: fixed;
18503            bottom: 80px;
18504            right: 20px;
18505            background: ${type === 'success' ? '#4caf50' : type === 'error' ? '#e2a642' : '#2196f3'};
18506            color: white;
18507            padding: 12px 20px;
18508            border-radius: 4px;
18509            box-shadow: 0 2px 8px rgba(0,0,0,0.2);
18510            z-index: 10000;
18511            animation: slideInUp 0.3s ease;
18512        `;
18513        
18514        document.body.appendChild(toast);
18515        
18516        setTimeout(() => {
18517            toast.style.animation = 'slideOutDown 0.3s ease';
18518            setTimeout(() => toast.remove(), 300);
18519        }, 3000);
18520    },
18521    
18522    /**
18523     * Convert VAPID key to Uint8Array
18524     */
18525    urlBase64ToUint8Array(base64String) {
18526        const padding = '='.repeat((4 - base64String.length % 4) % 4);
18527        const base64 = (base64String + padding)
18528            .replace(/\-/g, '+')
18529            .replace(/_/g, '/');
18530        
18531        const rawData = window.atob(base64);
18532        const outputArray = new Uint8Array(rawData.length);
18533        
18534        for (let i = 0; i < rawData.length; ++i) {
18535            outputArray[i] = rawData.charCodeAt(i);
18536        }
18537        return outputArray;
18538    },
18539    
18540    /**
18541     * Check if push notification is currently active
18542     * @returns {Promise<boolean>}
18543     */
18544    async isPushActive() {
18545        try {
18546            // Check browser subscription
18547            if (!('serviceWorker' in navigator)) return false;
18548            
18549            const registration = await navigator.serviceWorker.getRegistration();
18550            if (!registration) return false;
18551            
18552            const subscription = await registration.pushManager.getSubscription();
18553            if (!subscription) return false;
18554            
18555            // Also check server status
18556            const serverStatus = await this.checkSubscriptionStatus();
18557            
18558            return serverStatus;
18559        } catch (error) {
18560            return false;
18561        }
18562    },
18563    
18564    /**
18565     * Show logout confirmation if push notification is active
18566     * Call this before logout action
18567     * @returns {Promise<boolean>} true if user confirms logout, false if cancelled
18568     */
18569    async confirmLogoutWithPushNotification() {
18570        const isActive = await this.isPushActive();
18571        
18572        if (!isActive) {
18573            // No active push, proceed with logout
18574            return true;
18575        }
18576        
18577        // Show modal confirmation using modal.css
18578        return new Promise((resolve) => {
18579            // Create modal HTML
18580            const modalHTML = `
18581                <div class="modal-overlay" id="logoutConfirmModal">
18582                    <div class="modal-dialog">
18583                        <div class="modal-header">
18584                            <h3 class="modal-title">
18585                                <span class="modal-icon warning">⚠️</span>
18586                                Konfirmasi Logout
18587                            </h3>
18588                        </div>
18589                        <div class="modal-body">
18590                            <p class="modal-message">
18591                                Anda memiliki <strong>notifikasi push yang aktif</strong>.<br><br>
18592                                Setelah logout, Anda tidak akan menerima notifikasi chat lagi di perangkat ini.<br><br>
18593                                Apakah Anda yakin ingin melanjutkan logout?
18594                            </p>
18595                        </div>
18596                        <div class="modal-actions" style="padding: 0 20px 20px;">
18597                            <button class="modal-btn modal-btn-secondary" id="logoutCancelBtn">
18598                                Batal
18599                            </button>
18600                            <button class="modal-btn modal-btn-danger" id="logoutConfirmBtn">
18601                                Lanjutkan Logout
18602                            </button>
18603                        </div>
18604                    </div>
18605                </div>
18606            `;
18607            
18608            // Append to body
18609            const modalContainer = document.createElement('div');
18610            modalContainer.innerHTML = modalHTML;
18611            document.body.appendChild(modalContainer.firstElementChild);
18612            
18613            const modal = document.getElementById('logoutConfirmModal');
18614            const confirmBtn = document.getElementById('logoutConfirmBtn');
18615            const cancelBtn = document.getElementById('logoutCancelBtn');
18616            
18617            // Show modal with animation
18618            requestAnimationFrame(() => {
18619                modal.classList.add('show');
18620            });
18621            
18622            // Handle confirm
18623            const handleConfirm = () => {
18624                cleanup();
18625                resolve(true);
18626            };
18627            
18628            // Handle cancel
18629            const handleCancel = () => {
18630                cleanup();
18631                resolve(false);
18632            };
18633            
18634            // Cleanup function
18635            const cleanup = () => {
18636                modal.classList.remove('show');
18637                setTimeout(() => {
18638                    modal.remove();
18639                }, 200);
18640                confirmBtn.removeEventListener('click', handleConfirm);
18641                cancelBtn.removeEventListener('click', handleCancel);
18642                modal.removeEventListener('click', handleBackdropClick);
18643            };
18644            
18645            // Handle backdrop click
18646            const handleBackdropClick = (e) => {
18647                if (e.target === modal) {
18648                    handleCancel();
18649                }
18650            };
18651            
18652            // Add event listeners
18653            confirmBtn.addEventListener('click', handleConfirm);
18654            cancelBtn.addEventListener('click', handleCancel);
18655            modal.addEventListener('click', handleBackdropClick);
18656        });
18657    }
18658};
18659
18660// Add CSS animations
18661const chatNotifStyle = document.createElement('style');
18662chatNotifStyle.textContent = `
18663    @keyframes slideInUp {
18664        from {
18665            transform: translateY(100%);
18666            opacity: 0;
18667        }
18668        to {
18669            transform: translateY(0);
18670            opacity: 1;
18671        }
18672    }
18673    
18674    @keyframes slideOutDown {
18675        from {
18676            transform: translateY(0);
18677            opacity: 1;
18678        }
18679        to {
18680            transform: translateY(100%);
18681            opacity: 0;
18682        }
18683    }
18684`;
18685document.head.appendChild(chatNotifStyle);
18686
18687// Auto-initialize when DOM is ready
18688if (document.readyState === 'loading') {
18689    document.addEventListener('DOMContentLoaded', () => ChatNotification.init());
18690} else {
18691    ChatNotification.init();
18692}
18693
18694// Expose to window for access from other scripts
18695window.ChatNotification = ChatNotification;
18696
18697</script>
18697  
18698    <!-- Footer Collapse Toggle Script -->
18699  
18699<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
18700  document.addEventListener('DOMContentLoaded', function() {
18701    const ftTop = document.getElementById('ftTop');
18702    const expandBtn = document.getElementById('ftExpandBtn');
18703    
18704    if (ftTop && expandBtn) {
18705      const expandText = expandBtn.querySelector('.expand-text');
18706      
18707      expandBtn.addEventListener('click', function() {
18708        ftTop.classList.toggle('expanded');
18709        const isExpanded = ftTop.classList.contains('expanded');
18710        if (expandText) {
18711          expandText.textContent = isExpanded ? 'Sembunyikan' : 'Selengkapnya';
18712        }
18713      });
18714    }
18715  });
18716  </script>
18716
18717  </footer>
18718
18719<!-- Compare Sidebar Overlay -->
18720<div class="compare-overlay" id="compareOverlay"></div>
18721
18722<!-- Floating Compare Badge (Sticky) -->
18723<button class="compare-floating-badge" id="compareFloatingBadge" aria-label="Lihat produk yang dibandingkan" style="display:none;">
18724  <svg class="cfb-icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
18725    <path d="M9.01 14H2v2h7.01v3L13 15l-3.99-4v3zm5.98-1v-3H22V8h-7.01V5L11 9l3.99 4z"/>
18726  </svg>
18727  <span class="cfb-text">Bandingkan</span>
18728  <span class="cfb-count" id="cfbCount">0</span>
18729</button>
18730
18731<!-- AI Comparison Modal -->
18732<div class="ai-compare-modal" id="aiCompareModal">
18733  <div class="modal-backdrop"></div>
18734  <div class="modal-container">
18735    <div class="modal-header">
18736      <h3 class="modal-title">
18737        <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
18738          <path d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z"/>
18739        </svg>
18740        Analisis AI: Perbandingan Produk
18741      </h3>
18742      <button class="modal-close" id="aiCompareModalClose" aria-label="Tutup">
18743        <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18744          <path d="M18.3 5.7a1 1 0 0 0-1.4 0L12 10.6 7.1 5.7a1 1 0 1 0-1.4 1.4L10.6 12l-4.9 4.9a1 1 0 0 0 1.4 1.4L12 13.4l4.9 4.9a1 1 0 0 0 1.4-1.4L13.4 12l4.9-4.9a1 1 0 0 0 0-1.4z"/>
18745        </svg>
18746      </button>
18747    </div>
18748    
18749    <div class="modal-content">
18750      <!-- Loading State -->
18751      <div class="ai-loading" id="aiLoading">
18752        <div class="loading-animation">
18753          <div class="typing-indicator">
18754            <span></span>
18755            <span></span>
18756            <span></span>
18757          </div>
18758          <p>AI sedang menganalisis produk...</p>
18759          <small>Proses ini membutuhkan waktu 10-30 detik</small>
18760        </div>
18761      </div>
18762      
18763      <!-- Error State -->
18764      <div class="ai-error" id="aiError" hidden>
18765        <div class="error-icon">
18766          <svg width="48" height="48" viewBox="0 0 24 24" fill="#e2a642">
18767            <path d="M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z"/>
18768          </svg>
18769        </div>
18770        <h4>Analisis AI Gagal</h4>
18771        <p id="aiErrorMessage">Terjadi kesalahan saat memproses analisis.</p>
18772        <button class="btn btn-outline-primary" id="aiRetry">Coba Lagi</button>
18773      </div>
18774      
18775      <!-- Analysis Results -->
18776      <div class="ai-results" id="aiResults" hidden>
18777        <!-- Summary Section -->
18778        <div class="ai-summary-card">
18779          <h4>
18780            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18781              <path d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,16.5L6.5,12L7.91,10.59L11,13.67L16.59,8.09L18,9.5L11,16.5Z"/>
18782            </svg>
18783            Ringkasan Analisis
18784          </h4>
18785          <p id="aiSummary" class="summary-text"></p>
18786          <div class="winner-badge" id="winnerBadge">
18787            <strong>Rekomendasi Terbaik:</strong> <span id="winnerProduct"></span>
18788            <small id="winnerReason"></small>
18789          </div>
18790        </div>
18791        
18792        <!-- Comparison Table -->
18793        <div class="ai-comparison-table">
18794          <h4>
18795            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18796              <path d="M3,3H21V5H3V3M3,7H21V9H3V7M3,11H21V13H3V11M3,15H21V17H3V15M3,19H21V21H3V19Z"/>
18797            </svg>
18798            Perbandingan Detail
18799          </h4>
18800          <div id="comparisonTable" class="comparison-grid"></div>
18801        </div>
18802        
18803        <!-- Detailed Analysis -->
18804        <div class="ai-detailed-analysis">
18805          <h4>
18806            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18807              <path d="M14,2A8,8 0 0,1 22,10A8,8 0 0,1 14,18A8,8 0 0,1 6,10A8,8 0 0,1 14,2M14,4A6,6 0 0,0 8,10A6,6 0 0,0 14,16A6,6 0 0,0 20,10A6,6 0 0,0 14,4M14,6A4,4 0 0,1 18,10A4,4 0 0,1 14,14A4,4 0 0,1 10,10A4,4 0 0,1 14,6M14,8A2,2 0 0,0 12,10A2,2 0 0,0 14,12A2,2 0 0,0 16,10A2,2 0 0,0 14,8Z"/>
18808            </svg>
18809            Analisis Mendalam
18810          </h4>
18811          <div id="detailedAnalysis" class="products-analysis"></div>
18812        </div>
18813        
18814        <!-- Recommendations -->
18815        <div class="ai-recommendations">
18816          <h4>
18817            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18818              <path d="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"/>
18819            </svg>
18820            Rekomendasi Berdasarkan Kebutuhan
18821          </h4>
18822          <div class="recommendations-grid">
18823            <div class="recommendation-card">
18824              <h5>💰 Budget Conscious</h5>
18825              <p id="budgetRecommendation"></p>
18826            </div>
18827            <div class="recommendation-card">
18828              <h5>⚡ Performance Focused</h5>
18829              <p id="performanceRecommendation"></p>
18830            </div>
18831            <div class="recommendation-card">
18832              <h5>⚖️ Balanced Choice</h5>
18833              <p id="balancedRecommendation"></p>
18834            </div>
18835          </div>
18836        </div>
18837        
18838        <!-- Buying Tips -->
18839        <div class="ai-tips">
18840          <h4>
18841            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18842              <path d="M11,18H13V16H11V18M12,6A4,4 0 0,0 8,10H10A2,2 0 0,1 12,8A2,2 0 0,1 14,10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0 0,0 12,6
18842M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3Z"/>
18843            </svg>
18844            Tips Pembelian
18845          </h4>
18846          <ul id="buyingTips" class="tips-list"></ul>
18847        </div>
18848      </div>
18849    </div>
18850  </div>
18851</div>
18852
18853<!-- Cart Login Modal -->
18854<div class="modal-overlay" id="cartLoginModal">
18855  <div class="modal-dialog">
18856    <div class="modal-header">
18857      <h3 class="modal-title">
18858        <div class="modal-icon info">
18859          <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18860            <path d="M17,18A2,2 0 0,1 19,20A2,2 0 0,1 17,22C15.89,22 15,21.1 15,20C15,18.89 15.89,18 17,18M1,2H4.27L5.21,4H20A1,1 0 0,1 21,5C21,5.17 20.95,5.34 20.88,5.5L17.3,11.97C16.96,12.58 16.3,13 15.55,13H8.1L7.2,14.63L7.17,14.75A0.25,0.25 0 0,0 7.42,15H19V17H7C5.89,17 5,16.1 5,15C5,14.65 5.09,14.32 5.24,14.04L6.6,11.59L3,4H1V2M7,18A2,2 0 0,1 9,20A2,2 0 0,1 7,22C5.89,22 5,21.1 5,20C5,18.89 5.89,18 7,18M16,11L18.78,6H6.14L8.5,11H16Z"/>
18861          </svg>
18862        </div>
18863        Login untuk Melanjutkan
18864      </h3>
18865    </div>
18866    <div class="modal-body">
18867      <p class="modal-message" style="margin-bottom: 16px;">Silakan login atau daftar untuk mengakses keranjang belanja dan menyelesaikan pesanan Anda.</p>
18868      <div style="background: #f8fafc; border-radius: 8px; padding: 12px; margin-bottom: 20px;">
18869        <p style="font-size: 14px; color: #765212; margin: 0; line-height: 1.6;">
18870          <strong style="color: #2a2a0f;">Keuntungan dengan akun:</strong><br>
18871          ✓ Simpan keranjang belanja Anda<br>
18872          ✓ Lacak status pesanan secara real-time<br>
18873          ✓ Checkout lebih cepat dengan data tersimpan<br>
18874          ✓ Dapatkan notifikasi promo & diskon eksklusif
18875        </p>
18876      </div>
18877      <div class="modal-actions">
18878        <button class="modal-btn modal-btn-secondary" id="cartLoginCancel">Batal</button>
18879        <button class="modal-btn modal-btn-primary" id="cartLoginConfirm" style="background: #e2a642; border-color: #e2a642;">Login</button>
18880        <button class="modal-btn modal-btn-primary" id="cartRegisterBtn" style="background: #C10DFF; border-color: #C10DFF;">Daftar</button>
18881      </div>
18882    </div>
18883  </div>
18884</div>
18885
18886
18887<script data-inlined="app.js">
18888(function initLiveSearch(){
18889  function setup() {
18890    const searchInput = document.getElementById('searchInput');
18891    const suggest = document.getElementById('searchSuggest');
18892    const searchBtn = document.getElementById('searchBtn');
18893    const aiToggleBtn = document.getElementById('aiToggleBtn');
18894    
18895    if (!searchInput || !suggest || !aiToggleBtn) {
18896      console.log('[LiveSearch] Elemen belum ada, retry...');
18897      return false;
18898    }
18899
18900    let debounceTimer = null;
18901    let lastQuery = '';
18902    let currentIndex = -1;
18903    let currentAbortController = null;
18904    let isAIModeEnabled = false; // AI mode OFF by default
18905    let isAIProcessing = false; // Flag to prevent closing suggest box during AI processing
18906    
18907    // Trending cache
18908    let trendingCache = null;
18909    let trendingLoading = false;
18910    
18911    // Trending products cache
18912    let trendingProductsCache = null;
18913    let trendingProductsLoading = false;
18914    
18915    const MIN_LEN = 2;
18916    const DEBOUNCE = 300;
18917    const AI_TIMEOUT = 15000; // 15 seconds timeout for AI endpoint
18918
18919    // Load AI mode preference from localStorage
18920    const savedAIMode = localStorage.getItem('plazait_ai_search_mode');
18921    if (savedAIMode === 'enabled') {
18922      isAIModeEnabled = false;
18923      aiToggleBtn.classList.remove('active');
18924    }
18925    
18926    // Update placeholder based on AI mode
18927    function updatePlaceholder() {
18928      if (isAIModeEnabled) {
18929        searchInput.placeholder = '🤖 Coba: "laptop HP core i3 untuk kerja" atau "tinta epson l3210"';
18930      } else {
18931        // Static placeholder for typing animation
18932        searchInput.placeholder = '';
18933      }
18934    }
18935    
18936    // Typing animation for placeholder
18937    let typingTimeout = null;
18938    let currentProductIndex = 0;
18939    let currentCharIndex = 0;
18940    let isTyping = false;
18941    let isDeleting = false;
18942    
18943    const popularProducts = [
18944      'Oppo A6',
18945      'Asus Ryzen 3',
18946      'Starlink V4',
18947      'HP Victus',
18948      'Lenovo RTX 5060',
18949      'ROG Strix',
18950      'iPhone 13',
18951      'Xpad 20',
18952      'Xiaomi 14',
18953      'Epson L3210',
18954      'Realme C85',
18955      'Brother T720',
18956      'Mouse Logitech'
18957    ];
18958    
18959    function typeWriter() {
18960      if (isAIModeEnabled || document.activeElement === searchInput) {
18961        return; // Skip typing if AI mode is on or input is focused
18962      }
18963      
18964      const currentProduct = popularProducts[currentProductIndex];
18965      
18966      if (!isDeleting) {
18967        // Typing forward
18968        if (currentCharIndex < currentProduct.length) {
18969          searchInput.placeholder = currentProduct.substring(0, currentCharIndex + 1);
18970          currentCharIndex++;
18971          typingTimeout = setTimeout(typeWriter, 100); // Typing speed
18972        } else {
18973          // Finished typing, wait before deleting
18974          typingTimeout = setTimeout(() => {
18975            isDeleting = true;
18976            typeWriter();
18977          }, 2000); // Pause before deleting
18978        }
18979      } else {
18980        // Deleting backward
18981        if (currentCharIndex > 0) {
18982          searchInput.placeholder = currentProduct.substring(0, currentCharIndex - 1);
18983          currentCharIndex--;
18984          typingTimeout = setTimeout(typeWriter, 50); // Deleting speed (faster)
18985        } else {
18986          // Finished deleting, move to next product
18987          isDeleting = false;
18988          currentProductIndex = (currentProductIndex + 1) % popularProducts.length;
18989          typingTimeout = setTimeout(typeWriter, 500); // Pause before typing next
18990        }
18991      }
18992    }
18993    
18994    function startTypingAnimation() {
18995      if (!isAIModeEnabled && !typingTimeout) {
18996        currentCharIndex = 0;
18997        isDeleting = false;
18998        typeWriter();
18999      }
19000    }
19001    
19002    function stopTypingAnimation() {
19003      if (typingTimeout) {
19004        clearTimeout(typingTimeout);
19005        typingTimeout = null;
19006      }
19007    }
19008    
19009    // Set initial placeholder
19010    updatePlaceholder();
19011    
19012    // Start typing animation for normal mode
19013    if (!isAIModeEnabled) {
19014      startTypingAnimation();
19015    }
19016
19017    function esc(str){
19018      if (str === null || str === undefined) return '';
19019      return String(str)
19020        .replace(/&/g,'&amp;')
19021        .replace(/</g,'&lt;')
19022        .replace(/>/g,'&gt;')
19023        .replace(/"/g,'&quot;')
19024        .replace(/'/g,'&#39;');
19025    }
19026
19027    function clearSuggest(){
19028      suggest.innerHTML = '';
19029      suggest.hidden = true;
19030      suggest.classList.remove('open', 'trending-mode');
19031      currentIndex = -1;
19032    }
19033
19034    // ==================== TRENDING FUNCTIONS ====================
19035    
19036    async function fetchTrending() {
19037      if (trendingCache) return trendingCache;
19038      if (trendingLoading) return null;
19039      
19040      trendingLoading = true;
19041      
19042      try {
19043        const res = await fetch('/api/trending_searches.php?timeframe=7d&limit=8');
19044        const data = await res.json();
19045        
19046        if (data.ok && Array.isArray(data.data)) {
19047          trendingCache = data.data;
19048          console.log('[Trending] Loaded', trendingCache.length, 'items');
19049          return trendingCache;
19050        }
19051      } catch (err) {
19052        console.error('[Trending] Fetch error:', err);
19053      } finally {
19054        trendingLoading = false;
19055      }
19056      
19057      return null;
19058    }
19059    
19060    async function fetchTrendingProducts() {
19061      if (trendingProductsCache) return trendingProductsCache;
19062      if (trendingProductsLoading) return null;
19063      
19064      trendingProductsLoading = true;
19065      
19066      try {
19067        const res = await fetch('/api/trending_products.php?timeframe=7d&limit=6');
19068        const data = await res.json();
19069        
19070        if (data.ok && Array.isArray(data.data)) {
19071          trendingProductsCache = data.data;
19072          console.log('[Trending Products] Loaded', trendingProductsCache.length, 'items');
19073          return trendingProductsCache;
19074        }
19075      } catch (err) {
19076        console.error('[Trending Products] Fetch error:', err);
19077      } finally {
19078        trendingProductsLoading = false;
19079      }
19080      
19081      return null;
19082    }
19083    
19084    function renderTrendingSuggestions(trends, products) {
19085      if ((!trends || trends.length === 0) && (!products || products.length === 0)) {
19086        clearSuggest();
19087        return;
19088      }
19089      
19090      let html = `<div class="trending-container">`;
19091      
19092      // Left column: Trending searches
19093      if (trends && trends.length > 0) {
19094        html += `
19095          <div class="trending-column trending-searches">
19096            <div class="trending-header">
19097              <span class="trending-icon">🔥</span>
19098              <span class="trending-title">Pencarian Trending</span>
19099            </div>
19100        `;
19101        
19102        html += trends.map((t, idx) => {
19103          const categoryHint = t.category_hint ? `<span class="trending-category">${esc(t.category_hint)}</span>` : '';
19104          return `
19105            <div class="trending-item" data-query="${esc(t.query)}" data-index="${idx}">
19106              <span class="trending-rank">
19107                <img src="/assets/img/uptrend.image" alt="trending" width="16" height="16" style="vertical-align: middle;">
19108              </span>
19109              <span class="trending-query">${esc(t.query)}</span>
19110              ${categoryHint}
19111            </div>
19112          `;
19113        }).join('');
19114        
19115        html += `</div>`;
19116      }
19117      
19118      // Right column: Trending products
19119      if (products && products.length > 0) {
19120        html += `
19121          <div class="trending-column trending-products">
19122            <div class="trending-header">
19123              <span class="trending-icon">⭐</span>
19124              <span class="trending-title">Produk Populer</span>
19125            </div>
19126        `;
19127        
19128        html += products.map((p, idx) => {
19129          return `
19130            <div class="trending-product-item" data-product-id="${esc(p.id)}" data-slug="${esc(p.slug)}" data-query="${esc(p.top_query || '')}" data-index="${idx}">
19131              <img src="${esc(p.image)}" alt="${esc(p.name)}" class="trending-product-thumb">
19132              <div class="trending-product-info">
19133                <div class="trending-product-name">${esc(p.name)}</div>
19134                <div class="trending-product-price">${esc(p.price_label)}</div>
19135              </div>
19136            </div>
19137          `;
19138        }).join('');
19139        
19140        html += `</div>`;
19141      }
19142      
19143      html += `</div>`;
19144      
19145      suggest.innerHTML = html;
19146      suggest.hidden = false;
19147      suggest.classList.add('open', 'trending-mode');
19148      suggest.style.display = 'block';
19149      
19150      console.log('[Trending] Rendered', trends?.length || 0, 'searches and', products?.length || 0, 'products');
19151    }
19152    
19153    function trackSearchClick(query, productId) {
19154      fetch('/api/track_search_click.php', {
19155        method: 'POST',
19156        headers: { 'Content-Type': 'application/json' },
19157        body: JSON.stringify({
19158          query: query,
19159          product_id: productId,
19160          click_type: 'suggestion'
19161        })
19162      }).catch(() => {}); // silent fail
19163    }
19164    
19165    function trackTrendingClick(query) {
19166      fetch('/api/track_search_click.php', {
19167        method: 'POST',
19168        headers: { 'Content-Type': 'application/json' },
19169        body: JSON.stringify({
19170          query: query,
19171          click_type: 'trending'
19172        })
19173      }).catch(() => {});
19174    }
19175    
19176    function trackTrendingProductClick(productId, query) {
19177      fetch('/api/track_search_click.php', {
19178        method: 'POST',
19179        headers: { 'Content-Type': 'application/json' },
19180        body: JSON.stringify({
19181          query: query || 'trending_product',
19182          product_id: productId,
19183          click_type: 'trending_product'
19184        })
19185      }).catch(() => {});
19186    }
19187    
19188    function trackSearchSubmit(query) {
19189      fetch('/api/track_search_click.php', {
19190        method: 'POST',
19191        headers: { 'Content-Type': 'application/json' },
19192        body: JSON.stringify({
19193          query: query,
19194          click_type: 'submit'
19195        })
19196      }).catch(() => {});
19197    }
19198    
19199    function logFinalSearch(query) {
19200      // Log the final search to analytics (not during typing)
19201      fetch('/api/search_products.php?q=' + encodeURIComponent(query) + '&log=1', {
19202        headers: { 'Accept': 'application/json' }
19203      }).catch(() => {});
19204    }
19205
19206    // ==================== END TRENDING FUNCTIONS ====================
19207
19208    function clearSuggest(){
19209      suggest.innerHTML = '';
19210      suggest.hidden = true;
19211      suggest.classList.remove('open', 'trending-mode');
19212      currentIndex = -1;
19213    }
19214
19215    // AI Toggle Handler
19216    aiToggleBtn.addEventListener('click', (e) => {
19217      e.preventDefault();
19218      e.stopPropagation();
19219      
19220      isAIModeEnabled = !isAIModeEnabled;
19221      
19222      if (isAIModeEnabled) {
19223        aiToggleBtn.classList.add('active');
19224        localStorage.setItem('plazait_ai_search_mode', 'enabled');
19225        updatePlaceholder(); // Update placeholder text
19226        stopTypingAnimation(); // Stop typing animation in AI mode
19227        
19228        // Show AI prompt if there's a query
19229        const query = searchInput.value.trim();
19230        if (query.length >= MIN_LEN) {
19231          showAISearchPrompt(query);
19232        } else {
19233          showAIActivationMessage();
19234        }
19235      } else {
19236        aiToggleBtn.classList.remove('active');
19237        localStorage.setItem('plazait_ai_search_mode', 'disabled');
19238        updatePlaceholder(); // Update placeholder text
19239        startTypingAnimation(); // Start typing animation in normal mode
19240        clearSuggest();
19241        
19242        // Switch to normal search if there's a query
19243        const query = searchInput.value.trim();
19244        if (query.length >= MIN_LEN) {
19245          performSearch(query);
19246        }
19247      }
19248    });
19249
19250    function showAIActivationMessage() {
19251      suggest.innerHTML = `
19252        <div class="ai-progress-container">
19253          <div class="ai-progress-header">
19254            <div class="ai-progress-icon">✨</div>
19255            <div class="ai-progress-text">
19256              <div class="ai-progress-title">AI Search Mode Activated!</div>
19257              <div class="ai-progress-subtitle">Ketik query Anda untuk pencarian cerdas</div>
19258            </div>
19259          </div>
19260        </div>
19261      `;
19262      suggest.hidden = false;
19263      suggest.classList.add('open');
19264      suggest.style.display = 'block';
19265    }
19266
19267    function renderAIInsights(insights){
19268      if (!insights) return '';
19269      
19270      // Generate AI summary via backend endpoint
19271      const summary = insights.ai_summary || generateAISummary(insights);
19272      
19273      if (!summary) return '';
19274      
19275      return `
19276        <div class="ai-result-banner">
19277          <div class="ai-result-icon">✨</div>
19278          <div class="ai-result-text">
19279            <div class="ai-result-title">AI Assistant</div>
19280            <div class="ai-result-description">${summary}</div>
19281          </div>
19282        </div>
19283      `;
19284    }
19285    
19286    function generateAISummary(insights) {
19287      // Fallback client-side summary generation if backend doesn't provide ai_summary
19288      const productName = insights.product_name || '';
19289      const categoryPath = insights.category_path || '';
19290      const priceRange = insights.price_range || {};
19291      const intent = insights.intent || '';
19292      
19293      let summary = '';
19294      
19295      // Parse category for natural display
19296      let categoryDisplay = '';
19297      if (categoryPath) {
19298        const categoryParts = categoryPath.split('/');
19299        categoryDisplay = categoryParts[categoryParts.length - 1] || categoryParts[categoryParts.length - 2] || categoryPath;
19300      }
19301      
19302      // Intent-based opening
19303      if (intent === 'cek_stok') {
19304        summary = `Berikut ketersediaan stok untuk <strong>${esc(productName)}</strong>. `;
19305      } else if (intent === 'rekomendasi_produk') {
19306        summary = `Berikut rekomendasi produk <strong>${esc(categoryDisplay || productName)}</strong> yang kami pilihkan untuk Anda. `;
19307      } else {
19308        // Default: cari_harga or general
19309        if (productName && categoryDisplay) {
19310          summary = `Kami menemukan <strong>${esc(productName)}</strong> di kategori ${esc(categoryDisplay)}`;
19311        } else if (productName) {
19312          summary = `Kami menemukan produk <strong>${esc(productName)}</strong>`;
19313        } else if (categoryDisplay) {
19314          summary = `Kami menemukan produk ${esc(categoryDisplay)}`;
19315        }
19316        
19317        // Add price context
19318        if (priceRange.min > 0 && priceRange.max > 0) {
19319          const minPrice = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(priceRange.min);
19320          const maxPrice = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(priceRange.max);
19321          summary += ` dengan kisaran harga ${minPrice} - ${maxPrice}`;
19322        } else if (priceRange.min > 0) {
19323          const minPrice = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(priceRange.min);
19324          summary += ` dengan harga mulai ${minPrice}`;
19325        } else if (priceRange.max > 0) {
19326          const maxPrice = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(priceRange.max);
19327          summary += ` dengan harga maksimal ${maxPrice}`;
19328        }
19329        
19330        summary += ' yang sesuai dengan kebutuhan Anda.';
19331      }
19332      
19333      return summary || esc(insights.explanation || '');
19334    }
19335
19336    function showAIProgressSteps() {
19337      return `
19338        <div class="ai-progress-container">
19339          <div class="ai-progress-header">
19340            <div class="ai-progress-icon">🤖</div>
19341            <div class="ai-progress-text">
19342              <div class="ai-progress-title">AI sedang menganalisis...</div>
19343              <div class="ai-progress-subtitle">Memproses query Anda dengan kecerdasan buatan</div>
19344            </div>
19345          </div>
19346          <div class="ai-progress-steps">
19347            <div class="ai-progress-step active">
19348              <div class="ai-step-icon">1</div>
19349              <div class="ai-step-text">Memahami maksud pencarian...</div>
19350            </div>
19351            <div class="ai-progress-step pending">
19352              <div class="ai-step-icon">2</div>
19353              <div class="ai-step-text">Mengekstrak parameter pencarian...</div>
19354            </div>
19355            <div class="ai-progress-step pending">
19356              <div class="ai-step-icon">3</div>
19357              <div class="ai-step-text">Mencari produk yang relevan...</div>
19358            </div>
19359          </div>
19360        </div>
19361      `;
19362    }
19363
19364    function updateAIProgressStep(step) {
19365      const steps = suggest.querySelectorAll('.ai-progress-step');
19366      
19367      steps.forEach((stepEl, idx) => {
19368        const icon = stepEl.querySelector('.ai-step-icon');
19369        
19370        if (idx < step) {
19371          stepEl.classList.remove('active', 'pending');
19372          stepEl.classList.add('completed');
19373          icon.textContent = '✓';
19374        } else if (idx === step) {
19375          stepEl.classList.remove('pending', 'completed');
19376          stepEl.classList.add('active');
19377        }
19378      });
19379    }
19380
19381    function renderSuggest(items, aiInsights){
19382      if (!items.length){
19383        clearSuggest();
19384        return;
19385      }
19386      
19387      let html = '';
19388      
19389      // Show AI insights if available
19390      if (aiInsights && aiInsights.explanation) {
19391        html += renderAIInsights(aiInsights);
19392      }
19393      
19394      // Section header for products with hint
19395      html += `
19396        <div class="suggest-section-header">
19397          <div class="suggest-header-main">
19398            <span class="suggest-header-icon">📦</span>
19399            <span class="suggest-header-text">Produk yang ditemukan (${items.length})</span>
19400          </div>
19401          <div class="suggest-header-hint">Tekan <kbd>Enter</kbd> untuk lihat semua hasil</div>
19402        </div>
19403      `;
19404      
19405      // Render product items
19406      html += items.map((it, idx)=>{
19407        // Normalisasi & fallback
19408        const id    = it.id != null ? it.id : '';
19409        const name  = esc(it.name);
19410        const slug  = esc(it.slug != null ? it.slug : it.id);
19411        const img   = esc(it.image || '/assets/img/no-image.png');
19412        const price = esc(it.price_label || '');
19413
19414       const url = '/product/' + encodeURIComponent(slug);
19415
19416        return `
19417          <div class="suggest-item" data-index="${idx}" data-url="${url}" data-id="${esc(id)}">
19418            <img src="${img}" alt="${name}" class="suggest-thumb">
19419            <div class="suggest-meta">
19420              <div class="suggest-name">${name}</div>
19421              <div class="suggest-price">${price}</div>
19422            </div>
19423          </div>
19424        `;
19425      }).join('');
19426      
19427      // Footer hint for more results - only show if 10 items (means there might be more)
19428      if (items.length >= 10) {
19429        html += `
19430          <div class="suggest-footer-hint">
19431            <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="vertical-align: -3px; margin-right: 6px;">
19432              <path d="M9.29 6.71a.996.996 0 0 0 0 1.41L13.17 12l-3.88 3.88a.996.996 0 1 0 1.41 1.41l4.59-4.59a.996.996 0 0 0 0-1.41L10.7 6.7c-.38-.39-1.02-.39-1.41.01z"/>
19433            </svg>
19434            <span>Lihat hasil lengkap dengan menekan <kbd>Enter</kbd> atau <a href="#" class="suggest-footer-link" id="footerSearchLink">klik di sini</a></span>
19435          </div>
19436        `;
19437      }
19438      
19439      suggest.innerHTML = html;
19440      suggest.hidden = false;
19441      suggest.classList.add('open');
19442      suggest.style.display = 'block';
19443      suggest.style.visibility = 'visible';
19444      suggest.style.opacity = '1';
19445    }
19446
19447    function fetchRemote(q, shouldLog = false){
19448      // Cancel previous request if any
19449      if (currentAbortController) {
19450        currentAbortController.abort();
19451      }
19452      currentAbortController = new AbortController();
19453
19454      // Always use standard search for normal mode
19455      // Only add log=1 parameter when shouldLog is true (intentional search)
19456      const logParam = shouldLog ? '&log=1' : '';
19457      const url = '/api/search_products.php?q=' + encodeURIComponent(q) + logParam;
19458      const fetchOptions = {
19459        headers: {'Accept':'application/json'},
19460        signal: currentAbortController.signal
19461      };
19462
19463      return fetch(url, fetchOptions)
19464        .then(r=>r.json())
19465        .then(j=>{
19466          if (j.ok && Array.isArray(j.data)) {
19467            return j;
19468          }
19469          return { ok: false, data: [] };
19470        })
19471        .catch(err=>{
19472          if (err.name === 'AbortError') {
19473            return { ok: false, data: [] };
19474          }
19475          return { ok:false, data:[] };
19476        });
19477    }
19478
19479    function showNoResults(q, aiInsights) {
19480      suggest.innerHTML = `
19481        <div style="padding: 32px 24px; text-align: center; color: #64748b;">
19482          <div style="font-size: 56px; margin-bottom: 16px; opacity: 0.6;">🔍</div>
19483          <div style="font-size: 16px; font-weight: 600; color: #334155; margin-bottom: 8px;">
19484            Tidak ada hasil untuk "${esc(q)}"
19485          </div>
19486          ${aiInsights && aiInsights.explanation ? `
19487            <div style="font-size: 13px; margin: 16px auto; padding: 12px 16px; background: #f1f5f9; border-radius: 8px; max-width: 400px; color: #475569;">
19488              <strong style="color: #667eea;">🤖 AI memahami:</strong><br>
19489              ${esc(aiInsights.explanation)}
19490            </div>
19491          ` : ''}
19492          <div style="font-size: 13px; margin-top: 16px; opacity: 0.8;">
19493            Coba kata kunci lain atau hubungi kami untuk bantuan
19494          </div>
19495        </div>
19496      `;
19497      suggest.hidden = false;
19498      suggest.classList.add('open');
19499      suggest.style.display = 'block';
19500    }
19501
19502    function showErrorMessage() {
19503      suggest.innerHTML = `
19504        <div style="padding: 32px 24px; text-align: center; color: #e2a642;">
19505          <div style="font-size: 48px; margin-bottom: 12px;">⚠️</div>
19506          <div style="font-size: 14px; font-weight: 600; margin-bottom: 8px;">
19507            Pencarian tidak tersedia
19508          </div>
19509          <div style="font-size: 12px; opacity: 0.8;">
19510            Silakan coba lagi dalam beberapa saat
19511          </div>
19512        </div>
19513      `;
19514      suggest.hidden = false;
19515      suggest.classList.add('open');
19516      suggest.style.display = 'block';
19517    }
19518
19519    function showAIErrorState(q) {
19520      suggest.innerHTML = `
19521        <div class="ai-progress-container">
19522          <div class="ai-progress-header">
19523            <div class="ai-progress-icon" style="background: linear-gradient(135deg, #C10DFF 0%, #C10DFF 100%);">⚡</div>
19524            <div class="ai-progress-text">
19525              <div class="ai-progress-title">AI sedang memproses...</div>
19526              <div class="ai-progress-subtitle">Menganalisis query: "${esc(q)}"</div>
19527            </div>
19528          </div>
19529          <div style="padding: 16px; margin-top: 12px; background: #fef3c7; border-radius: 8px; border-left: 3px solid #C10DFF;">
19530            <div style="font-size: 13px; color: #ae7e21; line-height: 1.6;">
19531              <strong style="display: block; margin-bottom: 4px;">💡 Sedang mencari produk terbaik untuk Anda</strong>
19532              AI kami sedang menganalisis database untuk menemukan hasil yang paling relevan.
19533              Mohon tunggu sebentar...
19534            </div>
19535          </div>
19536          <div style="padding: 12px 16px; margin-top: 12px; text-align: center;">
19537            <button onclick="location.reload()" style="padding: 8px 16px; background: #C10DFF; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600;">
19538              🔄 Refresh & Coba Lagi
19539            </button>
19540          </div>
19541        </div>
19542      `;
19543      suggest.hidden = false;
19544      suggest.classList.add('open');
19545      suggest.style.display = 'block';
19546    }
19547
19548    function triggerAISearch(q) {
19549      lastQuery = q; // Update lastQuery to prevent re-triggering
19550      isAIProcessing = true; // Set flag to prevent suggest box from closing
19551      
19552      // Show AI progress UI - keep suggest box visible
19553      suggest.innerHTML = showAIProgressSteps();
19554      suggest.hidden = false;
19555      suggest.classList.add('open');
19556      suggest.style.display = 'block';
19557      
19558      setTimeout(() => updateAIProgressStep(1), 1000);
19559      setTimeout(() => updateAIProgressStep(2), 2500);
19560
19561      // Cancel previous request if any
19562      if (currentAbortController) {
19563        currentAbortController.abort();
19564      }
19565      currentAbortController = new AbortController();
19566
19567      const url = '/api/search_ai.php?q=' + encodeURIComponent(q);
19568      const fetchOptions = {
19569        headers: {'Accept':'application/json'},
19570        signal: currentAbortController.signal
19571      };
19572      
19573      // Add timeout
19574      const timeoutId = setTimeout(() => {
19575        currentAbortController.abort();
19576        
19577        // Show fallback loading state
19578        suggest.innerHTML = `
19579          <div class="ai-progress-container">
19580            <div class="ai-progress-header">
19581              <div class="ai-progress-icon">⏳</div>
19582              <div class="ai-progress-text">
19583                <div class="ai-progress-title">AI timeout - switching to standard search</div>
19584                <div class="ai-progress-subtitle">Mencari dengan metode tradisional...</div>
19585              </div>
19586            </div>
19587          </div>
19588        `;
19589        
19590        // Fallback to traditional search
19591        fetch('/api/search_products.php?q=' + encodeURIComponent(q), {
19592          headers: { 'Accept': 'application/json' }
19593        })
19594          .then(r => r.json())
19595          .then(j => {
19596            isAIProcessing = false; // Reset flag
19597            if (j.ok && Array.isArray(j.data) && j.data.length) {
19598              renderSuggest(j.data, null);
19599            } else {
19600              showNoResults(q, null);
19601            }
19602          })
19603          .catch(() => {
19604            isAIProcessing = false; // Reset flag
19605            showAIErrorState(q);
19606          });
19607      }, AI_TIMEOUT);
19608
19609      fetch(url, fetchOptions)
19610        .then(r => r.json())
19611        .then(j => {
19612          clearTimeout(timeoutId);
19613          isAIProcessing = false; // Reset flag when done
19614          
19615          if (j.ok && Array.isArray(j.data)) {
19616            if (j.data.length > 0) {
19617              renderSuggest(j.data, j.ai_insights || null);
19618            } else {
19619              showNoResults(q, j.ai_insights || null);
19620            }
19621          } else {
19622            showAIErrorState(q);
19623          }
19624        })
19625        .catch(err => {
19626          clearTimeout(timeoutId);
19627          isAIProcessing = false; // Reset flag on error
19628          if (err.name !== 'AbortError') {
19629            showAIErrorState(q);
19630          }
19631        });
19632    }
19633
19634    function performSearch(q){
19635      if (q.length < MIN_LEN){
19636        clearSuggest();
19637        return;
19638      }
19639      if (q === lastQuery) return;
19640      lastQuery = q;
19641      
19642      // Normal mode only - AI mode uses triggerAISearch()
19643      fetchRemote(q).then(j=>{
19644        if (j.ok && Array.isArray(j.data)){
19645          if (j.data.length > 0) {
19646            renderSuggest(j.data, null);
19647          } else {
19648            showNoResults(q, null);
19649          }
19650        } else {
19651          clearSuggest();
19652        }
19653      }).catch(() => {
19654        clearSuggest();
19655      });
19656    }
19657
19658    function debounce(){
19659      const q = searchInput.value.trim();
19660      clearTimeout(debounceTimer);
19661      
19662      // If empty, show trending suggestions and products
19663      if (q.length === 0) {
19664        debounceTimer = setTimeout(async () => {
19665          const trends = trendingCache || await fetchTrending();
19666          const products = trendingProductsCache || await fetchTrendingProducts();
19667          if ((trends && trends.length > 0) || (products && products.length > 0)) {
19668            renderTrendingSuggestions(trends, products);
19669          } else {
19670            clearSuggest();
19671          }
19672        }, 100);
19673        return;
19674      }
19675      
19676      // For AI mode, just show search prompt (no auto search)
19677      if (isAIModeEnabled && q.length >= MIN_LEN) {
19678        showAISearchPrompt(q);
19679      } else if (!isAIModeEnabled && q.length >= MIN_LEN) {
19680        // Normal mode: auto search with debounce
19681        debounceTimer = setTimeout(()=> performSearch(q), DEBOUNCE);
19682      } else {
19683        clearSuggest();
19684      }
19685    }
19686
19687    function showAISearchPrompt(q) {
19688      suggest.innerHTML = `
19689        <div class="ai-search-prompt">
19690          <div class="ai-prompt-header">
19691            <div class="ai-prompt-icon">🤖</div>
19692            <div class="ai-prompt-text">
19693              <div class="ai-prompt-title">Pencarian AI Siap!</div>
19694              <div class="ai-prompt-subtitle">Klik tombol di bawah untuk mencari dengan AI</div>
19695            </div>
19696          </div>
19697          <div class="ai-prompt-query">
19698            <div class="ai-query-label">
19698Query Anda:</div>
19699            <div class="ai-query-text">"${esc(q)}"</div>
19700          </div>
19701          <button class="ai-search-trigger-btn" data-query="${esc(q)}">
19702            <span class="ai-btn-icon">✨</span>
19703            <span class="ai-btn-text">Cari dengan AI</span>
19704          </button>
19705          <div class="ai-prompt-footer">
19706            AI akan menganalisis query Anda untuk hasil yang lebih relevan
19707          </div>
19708        </div>
19709      `;
19710      suggest.hidden = false;
19711      suggest.classList.add('open');
19712      suggest.style.display = 'block';
19713    }
19714
19715    function moveHighlight(dir){
19716      if (suggest.hidden) return;
19717      const items = suggest.querySelectorAll('.suggest-item');
19718      if (!items.length) return;
19719      currentIndex += dir;
19720      if (currentIndex < 0) currentIndex = items.length - 1;
19721      if (currentIndex >= items.length) currentIndex = 0;
19722      items.forEach(it=> it.classList.remove('active'));
19723      const active = items[currentIndex];
19724      if (active){
19725        active.classList.add('active');
19726        active.scrollIntoView({block:'nearest'});
19727      }
19728    }
19729
19730    function activate(){
19731      const active = suggest.querySelector('.suggest-item.active');
19732      if (!active) return;
19733      window.location.href = active.getAttribute('data-url');
19734    }
19735
19736    searchInput.addEventListener('input', debounce);
19737    
19738    // Stop typing animation when focused & load trending if empty
19739    searchInput.addEventListener('focus', async () => {
19740      stopTypingAnimation();
19741      
19742      const q = searchInput.value.trim();
19743      
19744      // If empty, show trending suggestions and products
19745      if (q.length === 0) {
19746        const trends = trendingCache || await fetchTrending();
19747        const products = trendingProductsCache || await fetchTrendingProducts();
19748        if ((trends && trends.length > 0) || (products && products.length > 0)) {
19749          renderTrendingSuggestions(trends, products);
19750        }
19751        return;
19752      }
19753      
19754      // If AI mode and has query, show AI prompt
19755      if (isAIModeEnabled && q.length >= MIN_LEN && suggest.hidden) {
19756        showAISearchPrompt(q);
19757      }
19758    });
19759    
19760    // Resume typing animation when blurred (if suggest is closed)
19761    searchInput.addEventListener('blur', () => {
19762      // Add delay to allow click events to fire first
19763      setTimeout(() => {
19764        if (!isAIModeEnabled && suggest.hidden && searchInput.value.trim() === '') {
19765          startTypingAnimation();
19766        }
19767      }, 200);
19768    });
19769    
19770    searchInput.addEventListener('keydown', e=>{
19771      switch(e.key){
19772        case 'ArrowDown': moveHighlight(1); e.preventDefault(); break;
19773        case 'ArrowUp': moveHighlight(-1); e.preventDefault(); break;
19774        case 'Enter':
19775          if (!suggest.hidden){
19776            activate();
19777            e.preventDefault();
19778          } else {
19779            // Direct enter without suggestion - track search submit and log final search
19780            const q = searchInput.value.trim();
19781            if (q.length >= MIN_LEN) {
19782              trackSearchSubmit(q);
19783              logFinalSearch(q);
19784            }
19785          }
19786          break;
19787        case 'Escape': clearSuggest(); break;
19788      }
19789    });
19790
19791    suggest.addEventListener('click', e=>{
19792      // Check if footer link clicked
19793      const footerLink = e.target.closest('#footerSearchLink');
19794      if (footerLink) {
19795        e.preventDefault();
19796        const q = searchInput.value.trim();
19797        if (q.length >= MIN_LEN) {
19798          trackSearchSubmit(q);
19799          logFinalSearch(q);
19800          window.location.href = '/product/search/?q=' + encodeURIComponent(q);
19801        }
19802        return;
19803      }
19804      
19805      // Check if AI search button clicked
19806      const aiBtn = e.target.closest('.ai-search-trigger-btn');
19807      if (aiBtn) {
19808        const query = aiBtn.getAttribute('data-query');
19809        if (query) {
19810          triggerAISearch(query);
19811        }
19812        return;
19813      }
19814      
19815      // Handle trending item click
19816      const trendingItem = e.target.closest('.trending-item');
19817      if (trendingItem) {
19818        const query = trendingItem.getAttribute('data-query');
19819        if (query) {
19820          searchInput.value = query;
19821          trackTrendingClick(query);
19822          logFinalSearch(query); // Log final search when user clicks trending
19823          performSearch(query);
19824        }
19825        return;
19826      }
19827      
19828      // Handle trending product click
19829      const trendingProduct = e.target.closest('.trending-product-item');
19830      if (trendingProduct) {
19831        const productId = trendingProduct.getAttribute('data-product-id');
19832        const slug = trendingProduct.getAttribute('data-slug');
19833        const query = trendingProduct.getAttribute('data-query');
19834        
19835        if (productId && slug) {
19836          trackTrendingProductClick(productId, query);
19837          window.location.href = '/product/' + encodeURIComponent(slug);
19838        }
19839        return;
19840      }
19841      
19842      // Handle product item click
19843      const it = e.target.closest('.suggest-item');
19844      if (!it) return;
19845      
19846      const productId = it.getAttribute('data-id');
19847      trackSearchClick(lastQuery, productId);
19848      logFinalSearch(lastQuery); // Log final search when user clicks on suggestion
19849      
19850      window.location.href = it.getAttribute('data-url');
19851    });
19852
19853    document.addEventListener('click', e=>{
19854      // Don't close suggest box if AI is processing
19855      if (isAIProcessing) return;
19856      
19857      if (e.target === searchInput || e.target === aiToggleBtn || 
19858          suggest.contains(e.target) || aiToggleBtn.contains(e.target)) return;
19859      clearSuggest();
19860    });
19861
19862    if (searchBtn){
19863      searchBtn.addEventListener('click', ()=>{
19864        const q = searchInput.value.trim();
19865        if (q.length >= MIN_LEN){
19866          // Track search submit and log final search
19867          trackSearchSubmit(q);
19868          logFinalSearch(q);
19869          window.location.href = '/product/search/?q=' + encodeURIComponent(q);
19870        }
19871      });
19872    }
19873
19874    // Pastikan style dasar muncul
19875    if (!suggest.classList.contains('wired')) {
19876      suggest.classList.add('wired');
19877      suggest.style.position = 'absolute';
19878      suggest.style.zIndex = '9999';
19879      suggest.style.background = '#fff';
19880      suggest.style.border = '1px solid #ccc';
19881      suggest.style.boxShadow = '0 4px 12px rgba(0,0,0,0.12)';
19882      suggest.style.maxHeight = '360px';
19883      suggest.style.overflowY = 'auto';
19884    }
19885
19886    return true;
19887  }
19888
19889  if (!setup()) {
19890    if (document.readyState === 'loading') {
19891      document.addEventListener('DOMContentLoaded', setup);
19892    } else {
19893      setTimeout(setup, 80);
19894    }
19895  }
19896})();
19897</script>
vendor: 2 bytes, line 19897
19897
19898<script data-inlined="site-notice.js">
19899/**
19900 * Site Notice Banner - Interactive Behavior
19901 * Handles dismiss functionality with localStorage persistence
19902 * Supports dynamic notice IDs for different campaigns
19903 */
19904
19905(function() {
19906    'use strict';
19907
19908    const STORAGE_KEY = 'plazait_site_notice_dismissed';
19909    
19910    const banner = document.getElementById('holidayNotice');
19911    const closeBtn = document.getElementById('holidayNoticeClose');
19912    
19913    if (!banner || !closeBtn) return;
19914
19915    // Get notice ID from data attribute
19916    const noticeId = banner.dataset.noticeId;
19917    if (!noticeId) {
19918        console.warn('Notice ID not found. Banner will not persist dismiss state.');
19919    }
19920
19921    /**
19922     * Check if banner was previously dismissed
19923     */
19924    function wasDismissed() {
19925        if (!noticeId) return false;
19926        
19927        try {
19928            const dismissed = localStorage.getItem(STORAGE_KEY);
19929            return dismissed === noticeId;
19930        } catch (e) {
19931            console.warn('localStorage not available:', e);
19932            return false;
19933        }
19934    }
19935
19936    /**
19937     * Mark banner as dismissed
19938     */
19939    function markDismissed() {
19940        if (!noticeId) return;
19941        
19942        try {
19943            localStorage.setItem(STORAGE_KEY, noticeId);
19944        } catch (e) {
19945            console.warn('Could not save to localStorage:', e);
19946        }
19947    }
19948
19949    /**
19950     * Hide banner with animation
19951     */
19952    function hideBanner() {
19953        banner.classList.add('hidden');
19954        document.body.classList.remove('has-holiday-notice');
19955        
19956        // Remove from DOM after animation
19957        setTimeout(() => {
19958            banner.remove();
19959        }, 300);
19960        
19961        markDismissed();
19962        
19963        // Analytics tracking (optional)
19964        if (window.gtag) {
19965            window.gtag('event', 'banner_dismiss', {
19966                'event_category': 'Site Notice',
19967                'event_label': noticeId || 'unknown',
19968            });
19969        }
19970    }
19971
19972    /**
19973     * Initialize banner
19974     */
19975    function init() {
19976        // Check if already dismissed
19977        if (wasDismissed()) {
19978            banner.remove();
19979            return;
19980        }
19981        
19982        // Add body class for layout adjustment
19983        document.body.classList.add('has-holiday-notice');
19984        
19985        // Close button click
19986        closeBtn.addEventListener('click', hideBanner);
19987        
19988        // Keyboard support for close button
19989        closeBtn.addEventListener('keydown', (e) => {
19990            if (e.key === 'Enter' || e.key === ' ') {
19991                e.preventDefault();
19992                hideBanner();
19993            }
19994        });
19995        
19996        // Optional: Auto-hide after certain time (disabled by default)
19997        // setTimeout(hideBanner, 30000); // 30 seconds
19998    }
19999
20000    // Initialize when DOM is ready
20001    if (document.readyState === 'loading') {
20002        document.addEventListener('DOMContentLoaded', init);
20003    } else {
20004        init();
20005    }
20006
20007})();
20008
20009</script>
vendor: 2 bytes, line 20009
20009
20010<script data-inlined="guest-cart.js">
20011/**
20012 * Guest Cart Manager - localStorage based cart for guest users
20013 * 
20014 * Manages shopping cart items in browser localStorage for users who are not logged in.
20015 * Provides seamless transition to database when user logs in.
20016 */
20017
20018class GuestCart {
20019    constructor() {
20020        this.storageKey = 'plazait_guest_cart';
20021        this.apiBase = '/api/cart';
20022        
20023        // Initialize cart structure
20024        this.cart = this.loadFromStorage();
20025        
20026        // Bind methods
20027        this.add = this.add.bind(this);
20028        this.remove = this.remove.bind(this);
20029        this.update = this.update.bind(this);
20030        this.clear = this.clear.bind(this);
20031        this.getItems = this.getItems.bind(this);
20032        this.mergeToDatabase = this.mergeToDatabase.bind(this);
20033    }
20034    
20035    /**
20036     * Load cart from localStorage
20037     */
20038    loadFromStorage() {
20039        try {
20040            const stored = localStorage.getItem(this.storageKey);
20041            if (stored) {
20042                const parsed = JSON.parse(stored);
20043                if (parsed && typeof parsed === 'object' && parsed.items) {
20044                    return parsed;
20045                }
20046            }
20047        } catch (error) {
20048            // Silent error handling
20049        }
20050        
20051        return {
20052            items: {},
20053            updated_at: Date.now(),
20054            version: '1.0'
20055        };
20056    }
20057    
20058    /**
20059     * Save cart to localStorage
20060     */
20061    saveToStorage() {
20062        try {
20063            this.cart.updated_at = Date.now();
20064            localStorage.setItem(this.storageKey, JSON.stringify(this.cart));
20065            
20066            // Dispatch custom event for cart updates
20067            window.dispatchEvent(new CustomEvent('guestCartUpdated', {
20068                detail: {
20069                    itemsCount: this.getItemsCount(),
20070                    linesCount: this.getLinesCount(),
20071                    items: this.cart.items
20072                }
20073            }));
20074            
20075            return true;
20076        } catch (error) {
20077            return false;
20078        }
20079    }
20080    
20081    /**
20082     * Generate cart item key
20083     */
20084    generateKey(productId, variantId = 0) {
20085        return `p${productId}v${variantId}`;
20086    }
20087    
20088    /**
20089     * Add item to guest cart
20090     */
20091    add(productId, qty = 1, variantId = 0, meta = {}) {
20092        const key = this.generateKey(productId, variantId);
20093        
20094        // Validate inputs
20095        if (!productId || productId <= 0 || !qty || qty <= 0) {
20096            throw new Error('Invalid product ID or quantity');
20097        }
20098        
20099        qty = Math.max(1, Math.min(999, parseInt(qty)));
20100        
20101        // Initialize item if not exists
20102        if (!this.cart.items[key]) {
20103            this.cart.items[key] = {
20104                pid: parseInt(productId),
20105                vid: parseInt(variantId) || 0,
20106                qty: 0,
20107                name: meta.name || '',
20108                sku: meta.sku || '',
20109                price: parseInt(meta.price) || 0,
20110                image_url: meta.image_url || '',
20111                product_url: meta.product_url || '/product/' + parseInt(productId),
20112                added_at: Date.now()
20113            };
20114        }
20115        
20116        // Update quantity
20117        this.cart.items[key].qty = Math.max(1, Math.min(999, this.cart.items[key].qty + qty));
20118        
20119        // Update metadata if provided
20120        if (meta.name) this.cart.items[key].name = meta.name;
20121        if (meta.sku) this.cart.items[key].sku = meta.sku;
20122        if (meta.price) this.cart.items[key].price = parseInt(meta.price);
20123        if (meta.image_url) this.cart.items[key].image_url = meta.image_url;
20124        if (meta.product_url) this.cart.items[key].product_url = meta.product_url;
20125        
20126        this.saveToStorage();
20127        
20128        return {
20129            key,
20130            item: this.cart.items[key],
20131            itemsCount: this.getItemsCount(),
20132            linesCount: this.getLinesCount()
20133        };
20134    }
20135    
20136    /**
20137     * Remove item from guest cart
20138     */
20139    remove(productId, variantId = 0) {
20140        const key = this.generateKey(productId, variantId);
20141        
20142        if (this.cart.items[key]) {
20143            delete this.cart.items[key];
20144            this.saveToStorage();
20145            
20146            return true;
20147        }
20148        
20149        return false;
20150    }
20151    
20152    /**
20153     * Update item quantity
20154     */
20155    update(productId, qty, variantId = 0) {
20156        const key = this.generateKey(productId, variantId);
20157        
20158        if (!this.cart.items[key]) {
20159            return false;
20160        }
20161        
20162        qty = parseInt(qty);
20163        if (qty <= 0) {
20164            return this.remove(productId, variantId);
20165        }
20166        
20167        this.cart.items[key].qty = Math.max(1, Math.min(999, qty));
20168        this.saveToStorage();
20169        
20170        return true;
20171    }
20172    
20173    /**
20174     * Clear entire cart
20175     */
20176    clear() {
20177        this.cart = {
20178            items: {},
20179            updated_at: Date.now(),
20180            version: '1.0'
20181        };
20182        
20183        this.saveToStorage();
20184        
20185        return true;
20186    }
20187    
20188    /**
20189     * Get all cart items
20190     */
20191    getItems() {
20192        return this.cart.items;
20193    }
20194    
20195    /**
20196     * Get cart items as array
20197     */
20198    getItemsArray() {
20199        return Object.values(this.cart.items);
20200    }
20201    
20202    /**
20203     * Get total items count (sum of quantities)
20204     */
20205    getItemsCount() {
20206        return Object.values(this.cart.items).reduce((total, item) => total + (item.qty || 0), 0);
20207    }
20208    
20209    /**
20210     * Get lines count (number of different items)
20211     */
20212    getLinesCount() {
20213        return Object.keys(this.cart.items).length;
20214    }
20215    
20216    /**
20217     * Get cart summary
20218     */
20219    getSummary() {
20220        const itemsCount = this.getItemsCount();
20221        const linesCount = this.getLinesCount();
20222        const totalValue = Object.values(this.cart.items).reduce(
20223            (total, item) => total + ((item.qty || 0) * (item.price || 0)), 0
20224        );
20225        
20226        return {
20227            itemsCount,
20228            linesCount,
20229            totalValue,
20230            updatedAt: this.cart.updated_at
20231        };
20232    }
20233    
20234    /**
20235     * Check if cart has items
20236     */
20237    isEmpty() {
20238        return this.getLinesCount() === 0;
20239    }
20240    
20241    /**
20242     * Merge guest cart to database after user login
20243     */
20244    async mergeToDatabase() {
20245        const items = this.cart.items;
20246        
20247        if (Object.keys(items).length === 0) {
20248            return { ok: true, merged_count: 0, message: 'No items to merge' };
20249        }
20250        
20251        try {
20252            // Use new endpoint that handles localStorage format
20253            const response = await fetch(`${this.apiBase}/merge-localStorage.php`, {
20254                method: 'POST',
20255                headers: {
20256                    'Content-Type': 'application/json',
20257                },
20258                credentials: 'same-origin',
20259                body: JSON.stringify({
20260                    items: items  // Send localStorage format directly
20261                })
20262            });
20263            
20264            const result = await response.json();
20265            
20266            if (result.ok) {
20267                // Clear localStorage cart after successful merge
20268                this.clear();
20269                
20270                // Update cart counter
20271                if (window.updateCartCounter) {
20272                    try {
20273                        await window.updateCartCounter();
20274                    } catch (e) {
20275                        // Silent error handling
20276                    }
20277                }
20278                
20279                // Dispatch merge completion event
20280                window.dispatchEvent(new CustomEvent('guestCartMerged', {
20281                    detail: result
20282                }));
20283                
20284                return result;
20285            } else {
20286                throw new Error(result.error || 'Failed to merge cart');
20287            }
20288            
20289        } catch (error) {
20290            throw error;
20291        }
20292    }
20293    
20294    /**
20295     * Export cart data (for debugging or backup)
20296     */
20297    export() {
20298        return {
20299            ...this.cart,
20300            summary: this.getSummary()
20301        };
20302    }
20303    
20304    /**
20305     * Import cart data (for restore or testing)
20306     */
20307    import(cartData) {
20308        if (cartData && typeof cartData === 'object' && cartData.items) {
20309            this.cart = {
20310                items: cartData.items,
20311                updated_at: cartData.updated_at || Date.now(),
20312                version: cartData.version || '1.0'
20313            };
20314            
20315            this.saveToStorage();
20316            return true;
20317        }
20318        
20319        return false;
20320    }
20321}
20322
20323// Create global instance
20324window.guestCart = new GuestCart();
20325
20326// Auto-merge on page load if user is logged in
20327document.addEventListener('DOMContentLoaded', async function() {
20328    // Multiple ways to check if user is logged in
20329    const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
20330                      document.body.classList.contains('logged-in') ||
20331                      window.userLoggedIn === true ||
20332                      document.body.dataset.userLoggedIn === 'true';
20333    
20334    if (isLoggedIn && !window.guestCart.isEmpty()) {
20335        // Small delay to ensure page is fully loaded
20336        setTimeout(async () => {
20337            try {
20338                const result = await window.guestCart.mergeToDatabase();
20339            } catch (error) {
20340                // Silent error handling
20341            }
20342        }, 500);
20343    }
20344});
20345
20346// Also listen for login events (if login happens via AJAX)
20347window.addEventListener('userLoggedIn', async function() {
20348    if (!window.guestCart.isEmpty()) {
20349        try {
20350            await window.guestCart.mergeToDatabase();
20351        } catch (error) {
20352            // Silent error handling
20353        }
20354    }
20355});
20356</script>
vendor: 2 bytes, line 20356
20356
20357<script data-inlined="cart.js">
20358(function(){
20359  function $(s,ctx=document){ return ctx.querySelector(s); }
20360  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
20361  
20362  // CSRF token helper
20363  function getCsrfToken() {
20364    const metaTag = document.querySelector('meta[name="csrf-token"]');
20365    return metaTag ? metaTag.getAttribute('content') : '';
20366  }
20367  
20368  function guessPdMeta(){
20369    const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : ($('#pd-title')?.textContent || '');
20370    const sku  = (function(){ const el=$('#pd-sku'); return el ? el.textContent.trim() : (window.PD_SKU||''); })();
20371    // SECURITY FIX: Remove price from client-side metadata (will be validated server-side)
20372    const imgEl = $('#pd-main-img');
20373    const image_url = imgEl ? (imgEl.getAttribute('src') || '') : '';
20374    return { name, sku, image_url }; // price removed for security
20375  }
20376
20377  async function addToCart(productId, qty=1, variantId=0, metaOpt){
20378    const meta = Object.assign(guessPdMeta(), metaOpt||{});
20379    const payload = {
20380      product_id: parseInt(productId||0,10) || 0,
20381      qty: parseInt(qty||1,10) || 1,
20382      variant_id: parseInt(variantId||0,10) || 0,
20383      meta
20384    };
20385    
20386    // Check if user is logged in
20387    const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
20388                      document.body.classList.contains('logged-in') ||
20389                      window.userLoggedIn === true ||
20390                      document.body.dataset.userLoggedIn === 'true';
20391    
20392    // Skip localStorage untuk logged user - mereka pakai database
20393    if (!isLoggedIn && window.guestCart) {
20394      try {
20395        window.guestCart.add(payload.product_id, payload.qty, payload.variant_id, meta);
20396      } catch (e) {
20397        console.warn('Failed to add to localStorage cart:', e);
20398      }
20399    }
20400    
20401    const res = await fetch('/api/cart/add.php', {
20402      method: 'POST',
20403      headers: { 
20404        'Content-Type':'application/json',
20405        'X-CSRF-Token': getCsrfToken()
20406      },
20407      credentials: 'same-origin',
20408      body: JSON.stringify({
20409        ...payload,
20410        csrf_token: getCsrfToken()
20411      }),
20412    });
20413    const data = await res.json().catch(()=>({ ok:false, error:'Gagal menambahkan ke keranjang' }));
20414    if (!res.ok || !data.ok) {
20415      const msg = data && data.error ? data.error : 'Gagal menambahkan ke keranjang';
20416      throw new Error(msg);
20417    }
20418
20419    // Update badge di navbar dengan data dari response
20420    try {
20421      const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
20422      if (el && data.cart && typeof data.cart.items_count === 'number') {
20423        el.textContent = String(data.cart.items_count);
20424        el.classList.toggle('is-empty', data.cart.items_count <= 0);
20425      }
20426    } catch(_){}
20427
20428    // Siarkan event global (dipakai cart-badge.js dan halaman lain)
20429    try {
20430      document.dispatchEvent(new CustomEvent('cart:updated', { detail: data.cart || {} }));
20431    } catch(_){}
20432
20433    // Update CartBadge dengan data dari response (lebih reliable)
20434    try { 
20435      if (window.CartBadge && window.CartBadge.set) {
20436        window.CartBadge.set(data.cart.items_count||0);
20437      }
20438    } catch(_){}
20439
20440    // Tambahan: refresh cart counter setelah delay kecil untuk memastikan data tersinkronisasi
20441    setTimeout(() => {
20442      if (window.updateCartCounter) {
20443        window.updateCartCounter();
20444      }
20445    }, 100);
20446
20447    return data;
20448  }
20449
20450  // Function to update cart counter from server
20451  async function updateCartCounter(){
20452    try {
20453      const res = await fetch('/api/cart/get.php', { credentials:'same-origin' });
20454      const data = await res.json().catch(()=>({ok:false}));
20455      if (data.ok && typeof data.items_count === 'number') {
20456        const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
20457        if (el) {
20458          el.textContent = String(data.items_count);
20459          el.classList.toggle('is-empty', data.items_count <= 0);
20460        }
20461        // Dispatch event
20462        try {
20463          document.dispatchEvent(new CustomEvent('cart:updated', { detail: { items_count: data.items_count } }));
20464        } catch(_){}
20465      }
20466    } catch (err) {
20467      console.log('Could not update cart counter:', err);
20468    }
20469  }
20470
20471  // Function to manually merge guest cart (if needed)
20472  async function mergeGuestCart(){
20473    try {
20474      const res = await fetch('/api/cart/merge-guest.php', {
20475        method: 'POST',
20476        headers: { 
20477          'Content-Type': 'application/json',
20478          'X-CSRF-Token': getCsrfToken()
20479        },
20480        credentials: 'same-origin',
20481        body: JSON.stringify({ csrf_token: getCsrfToken() })
20482      });
20483      const data = await res.json().catch(()=>({ok:false}));
20484      
20485      if (data.ok && data.merged_items > 0) {
20486        console.log(`Guest cart merged: ${data.merged_items} items`);
20487        // Update cart counter after merge
20488        await updateCartCounter();
20489        return data;
20490      }
20491      return data;
20492    } catch (err) {
20493      console.log('Could not merge guest cart:', err);
20494      return { ok: false, error: err.message };
20495    }
20496  }
20497
20498  window.addToCart = addToCart;
20499  window.updateCartCounter = updateCartCounter;
20500  window.mergeGuestCart = mergeGuestCart;
20501
20502  // Optional: auto-bind untuk tombol apapun yang punya data-addtocart
20503  document.addEventListener('click', async (e)=>{
20504    const btn = e.target.closest('[data-addtocart]');
20505    if (!btn) return;
20506    e.preventDefault();
20507    if (btn.disabled) return;
20508
20509    const pid = parseInt(btn.getAttribute('data-pid')||'0',10);
20510    const vid = parseInt(btn.getAttribute('data-vid')||'0',10);
20511    const qty = parseInt(btn.getAttribute('data-qty')||'1',10) || 1;
20512
20513    try {
20514      const data = await addToCart(pid, qty, vid);
20515      if (window.showToast) {
20516        const meta = guessPdMeta();
20517        window.showToast({
20518          type:'success',
20519          title:'Berhasil ditambahkan',
20520          message:`<strong>${esc(meta.name)}</strong>${meta.sku?` · <em>SKU: ${esc(meta.sku)}</em>`:''}<br>Qty: ${qty}`,
20521          duration: 3800,
20522          actions: [
20523            { label:'Lihat Keranjang', onClick: ()=> { window.location.href='/keranjang'; } },
20524            { label:'Lanjut Belanja', variant:'alt', onClick: ()=>{} }
20525          ]
20526        });
20527      }
20528    } catch (err) {
20529      if (window.showToast) {
20530        window.showToast({ type:'warn', title:'Gagal', message: String(err?.message||'Gagal menambahkan'), duration: 4200 });
20531      } else {
20532        console.error(err);
20533      }
20534    }
20535  });
20536})();
20537</script>
vendor: 2 bytes, line 20537
20537
20538<script data-inlined="mega-menu-tiles.js">
20539// Tile Mega Menu — Enhanced 2025 (Stagger animations, product counts, loading states)
20540// Includes safe-area hover handling (no backdrop flicker)
20541
20542(function(){
20543  const trigger   = document.querySelector('[data-mm2="trigger"]');
20544  const panel     = document.querySelector('[data-mm2="panel"]');
20545  const topView   = document.querySelector('[data-mm2="top"]');
20546  const detailView= document.querySelector('[data-mm2="detail"]');
20547  const backdrop  = document.querySelector('[data-mm2="backdrop"]');
20548  if (!trigger || !panel || !topView || !detailView) return;
20549
20550  const apiUrl = '/api/categories_tree.php?top=16&child=18&grand=14';
20551  const isDesktop = () => window.matchMedia('(min-width: 961px)').matches;
20552
20553  let data = [];
20554  let hoverTimer, closeTimer;
20555  let isLoading = false;
20556
20557  const esc = s => (s+'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
20558  
20559  // Mobile close button HTML
20560  const mobileCloseBtn = () => `
20561    <button type="button" class="mm2-mobile-close" data-mm2="close" aria-label="Tutup menu">
20562      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
20563        <line x1="18" y1="6" x2="6" y2="18"></line>
20564        <line x1="6" y1="6" x2="18" y2="18"></line>
20565      </svg>
20566    </button>
20567  `;
20568  
20569  // Category-specific gradient colors (16 unique combinations)
20570  const categoryGradients = [
20571    { from: '#e0e7ff', to: '#fff', border: '#c7d2fe', color: '#4f46e5' },  // Indigo
20572    { from: '#dbeafe', to: '#fff', border: '#bfdbfe', color: '#1e40af' },  // Blue
20573    { from: '#d1fae5', to: '#fff', border: '#a7f3d0', color: '#059669' },  // Green
20574    { from: '#fef3c7', to: '#fff', border: '#fde68a', color: '#d97706' },  // Amber
20575    { from: '#fce7f3', to: '#fff', border: '#fbcfe8', color: '#db2777' },  // Pink
20576    { from: '#e9d5ff', to: '#fff', border: '#d8b4fe', color: '#9333ea' },  // Purple
20577    { from: '#ffedd5', to: '#fff', border: '#fed7aa', color: '#e2a642' },  // Orange
20578    { from: '#ccfbf1', to: '#fff', border: '#99f6e4', color: '#0d9488' },  // Teal
20579    { from: '#fef9c3', to: '#fff', border: '#fef08a', color: '#ca8a04' },  // Yellow
20580    { from: '#ffe4e6', to: '#fff', border: '#fecdd3', color: '#e2a642' },  // Rose
20581    { from: '#e0f2fe', to: '#fff', border: '#bae6fd', color: '#0284c7' },  // Sky
20582    { from: '#f3e8ff', to: '#fff', border: '#e9d5ff', color: '#a855f7' },  // Violet
20583    { from: '#dcfce7', to: '#fff', border: '#bbf7d0', color: '#16a34a' },  // Emerald
20584    { from: '#fef2f2', to: '#fff', border: '#fecaca', color: '#e2a642' },  // Red
20585    { from: '#eff6ff', to: '#fff', border: '#dbeafe', color: '#2563eb' },  // Blue-600
20586    { from: '#f5f3ff', to: '#fff', border: '#ede9fe', color: '#7c3aed' },  // Violet-600
20587  ];
20588  
20589  const icon = () => `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7h5l2 3h11v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><path d="M3 7V5a2 2 0 0 1 2-2h4l2 2h6a2 2 0 0 1 2 2v3"></path></svg>`;
20590
20591  // Recent (optional future use)
20592  const REC_KEY='mm2_recent';
20593  const saveRecent=(path,label)=>{ try{
20594    let list=JSON.parse(localStorage.getItem(REC_KEY)||'[]').filter(x=>x&&x.path!==path);
20595    list.unshift({path,label,t:Date.now()}); localStorage.setItem(REC_KEY, JSON.stringify(list.slice(0,8)));
20596  }catch{} };
20597
20598  function isToSafeArea(e){
20599    const t = e && e.relatedTarget ? e.relatedTarget : null;
20600    if (!t) return false;
20601    return (trigger.contains(t) || panel.contains(t) || (backdrop && backdrop.contains(t)));
20602  }
20603
20604  function open(){ 
20605    document.body.classList.add('mm2-open'); 
20606    showTop(); 
20607    // Add entrance animation class
20608    setTimeout(() => panel.classList.add('mm2-animated'), 10);
20609  }
20610  function close(){ 
20611    document.body.classList.remove('mm2-open');
20612    panel.classList.remove('mm2-animated');
20613  }
20614  function showTop(){ 
20615    topView.classList.add('is-active'); 
20616    detailView.classList.remove('is-active'); 
20617  }
20618  
20619  // Loading skeleton
20620  function showLoading() {
20621    const skeletons = Array.from({length: 12}, (_, i) => `
20622      <div class="mm2-card mm2-skeleton" style="animation-delay: ${i * 0.02}s">
20623        <span class="mm2-ico skeleton-box"></span>
20624        <span style="flex: 1;">
20625          <div class="skeleton-line" style="width: 60%; height: 16px; margin-bottom: 6px;"></div>
20626          <div class="skeleton-line" style="width: 40%; height: 12px;"></div>
20627        </span>
20628      </div>
20629    `).join('');
20630    topView.innerHTML = `<div class="mm2-top"><div class="mm2-grid">${skeletons}</div></div>`;
20631  }
20632  
20633  // Count total products in a category tree
20634  function countProducts(node) {
20635    let count = node.product_count || 0;
20636    if (Array.isArray(node.children)) {
20637      node.children.forEach(child => {
20638        count += countProducts(child);
20639      });
20640    }
20641    return count;
20642  }
20643  
20644  // Cache for fetched thumbnails to avoid duplicate requests
20645  const thumbnailCache = new Map();
20646  
20647  // Fetch product thumbnails for a category
20648  function fetchCategoryThumbnails(categoryPath) {
20649    // Check cache first
20650    if (thumbnailCache.has(categoryPath)) {
20651      injectThumbnails(categoryPath, thumbnailCache.get(categoryPath));
20652      return;
20653    }
20654    
20655    const url = `/api/category_products_preview.php?path=${encodeURIComponent(categoryPath)}&limit=2`;
20656    
20657    fetch(url)
20658      .then(r => {
20659        if (!r.ok) throw new Error('Network error');
20660        return r.json();
20661      })
20662      .then(data => {
20663        if (data.success && data.products && data.products.length > 0) {
20664          thumbnailCache.set(categoryPath, data.products);
20665          injectThumbnails(categoryPath, data.products);
20666        } else {
20667          // No products, remove skeleton
20668          removeThumbSkeletons(categoryPath);
20669        }
20670      })
20671      .catch(err => {
20672        console.warn('Failed to load thumbnails for', categoryPath, err);
20673        removeThumbSkeletons(categoryPath);
20674      });
20675  }
20676  
20677  // Inject thumbnails into the DOM
20678  function injectThumbnails(categoryPath, products) {
20679    const links = detailView.querySelectorAll(`[data-cat-path="${categoryPath}"]`);
20680    links.forEach(link => {
20681      const thumbsContainer = link.querySelector('.mm2-thumbs');
20682      if (!thumbsContainer) return;
20683      
20684      const thumbsHtml = products.slice(0, 2).map(p => 
20685        `<img src="${esc(p.img)}" alt="${esc(p.name)}" class="mm2-thumb" title="${esc(p.name)}" loading="lazy">`
20686      ).join('');
20687      
20688      thumbsContainer.innerHTML = thumbsHtml;
20689      thumbsContainer.removeAttribute('data-loading');
20690    });
20691  }
20692  
20693  // Remove skeleton loaders when no products
20694  function removeThumbSkeletons(categoryPath) {
20695    const links = detailView.querySelectorAll(`[data-cat-path="${categoryPath}"]`);
20696    links.forEach(link => {
20697      const thumbsContainer = link.querySelector('.mm2-thumbs');
20698      if (thumbsContainer) {
20699        thumbsContainer.remove();
20700      }
20701    });
20702  }
20703
20704  function showDetail(node){
20705    const title = node.label.split('/')[0];
20706    const children = Array.isArray(node.children) ? node.children : [];
20707    const hasChildren = children.length > 0;
20708
20709    const subnav = hasChildren ? `
20710      <nav class="mm2-subnav" role="tablist">
20711        ${children.map((c2,i)=>{
20712          const t = c2.label.split('/').slice(-1)[0];
20713          return `<button class="mm2-subitem${i===0?' active':''}" role="tab" data-idx="${i}" aria-selected="${i===0?'true':'false'}">${esc(t)}</button>`;
20714        }).join('')}
20715      </nav>` : '';
20716
20717    const subcontent = `<div class="mm2-subcontent" data-mm2="subcontent"></div>`;
20718
20719    detailView.innerHTML = `
20720      <div class="mm2-detail">
20721        <div class="mm2-detail-header">
20722          <button class="mm2-back" type="button" data-mm2="back" aria-label="Kembali">
20723            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 18 9 12 15 6"></polyline></svg>
20724          </button>
20725          <div class="mm2-detail-title">${esc(title)}</div>
20726        </div>
20727
20728        ${hasChildren ? `
20729          <div class="mm2-detail-layout">
20730            ${subnav}
20731            ${subcontent}
20732          </div>` : `
20733          <div class="mm2-links">
20734            <a class="mm2-link mm2-seeall" href="/product/kategori/?kategori_path=${encodeURIComponent(node.path)}" data-mm2="nav" data-path="${esc(node.path)}" data-label="${esc(node.label)}">
20735              <strong>Lihat semua ${esc(title)}</strong>
20736            </a>
20737          </div>
20738        `}
20739      </div>
20740    `;
20741
20742    topView.classList.remove('is-active');
20743    detailView.classList.add('is-active');
20744
20745    if (hasChildren) {
20746      const subEl  = detailView.querySelector('.mm2-subnav');
20747      const contEl = detailView.querySelector('[data-mm2="subcontent"]');
20748      let activeIdx = 0;
20749
20750      const renderSubgrid = (idx) => {
20751        const c2 = children[idx]; if (!c2) { contEl.innerHTML=''; return; }
20752        const title2 = c2.label.split('/').slice(-1)[0];
20753        const l3 = Array.isArray(c2.children) ? c2.children : [];
20754        const l3Html = l3.map(c3 => `
20755          <a class="mm2-link" href="/product/kategori/?kategori_path=${encodeURIComponent(c3.path)}" data-mm2="nav" data-path="${esc(c3.path)}" data-label="${esc(c3.label)}" data-cat-path="${esc(c3.path)}">
20756            <span class="mm2-link-text">${esc(c3.label.split('/').slice(-1)[0])}</span>
20757            <span class="mm2-thumbs" data-loading="true">
20758              <span class="mm2-thumb-skeleton"></span>
20759              <span class="mm2-thumb-skeleton"></span>
20760            </span>
20761          </a>`).join('');
20762
20763        contEl.innerHTML = `
20764          <div class="mm2-links">
20765            <a class="mm2-link mm2-seeall" href="/product/kategori/?kategori_path=${encodeURIComponent(c2.path)}" data-mm2="nav" data-path="${esc(c2.path)}" data-label="${esc(c2.label)}">
20766              <strong>Lihat semua ${esc(title2)}</strong>
20767            </a>
20768            ${l3Html}
20769          </div>
20770        `;
20771        
20772        // Fetch product thumbnails for each level-3 category (desktop only)
20773        if (isDesktop()) {
20774          setTimeout(() => {
20775            l3.forEach(c3 => {
20776              fetchCategoryThumbnails(c3.path);
20777            });
20778          }, 100);
20779        }
20780      };
20781
20782      renderSubgrid(activeIdx);
20783
20784      // Desktop: hover ganti konten; Mobile & Desktop: click pilih
20785      subEl.addEventListener('mousemove', (e)=>{
20786        if (!isDesktop()) return;
20787        const it = e.target.closest('.mm2-subitem'); if (!it) return;
20788        const idx = +it.dataset.idx; if (!Number.isInteger(idx) || idx===activeIdx) return;
20789        subEl.querySelectorAll('.mm2-subitem').forEach(el=>el.classList.remove('active'));
20790        it.classList.add('active');
20791        subEl.querySelectorAll('.mm2-subitem').forEach(el=>el.setAttribute('aria-selected', el===it ? 'true':'false'));
20792        activeIdx = idx;
20793        renderSubgrid(activeIdx);
20794      });
20795
20796      subEl.addEventListener('click', (e)=>{
20797        const it = e.target.closest('.mm2-subitem'); if (!it) return;
20798        const idx = +it.dataset.idx; if (!Number.isInteger(idx)) return;
20799        subEl.querySelectorAll('.mm2-subitem').forEach(el=>el.classList.remove('active'));
20800        it.classList.add('active');
20801        subEl.querySelectorAll('.mm2-subitem').forEach(el=>el.setAttribute('aria-selected', el===it ? 'true':'false'));
20802        activeIdx = idx;
20803        renderSubgrid(activeIdx);
20804      });
20805
20806      // Delegate link clicks to add recent
20807      detailView.addEventListener('click', (e)=>{
20808        const nav = e.target.closest('[data-mm2="nav"]');
20809        if (nav) { saveRecent(nav.dataset.path, nav.dataset.label); }
20810      });
20811    }
20812  }
20813
20814  function buildTop(){
20815    const html = data.map((n, idx) => {
20816      const title = n.label.split('/')[0];
20817      const childCount = (n.children||[]).length;
20818      const productCount = countProducts(n);
20819      const gradient = categoryGradients[idx % categoryGradients.length];
20820      
20821      // Dynamic subtitle based on content - shorter for mobile
20822      let sub = '';
20823      if (productCount > 0) {
20824        sub = `${productCount} produk`;
20825        if (childCount > 0) sub += ` · ${childCount} kat`;
20826      } else if (childCount > 0) {
20827        sub = `${childCount} kategori`;
20828      } else {
20829        sub = 'Lihat semua';
20830      }
20831      
20832      const iconStyle = `background: linear-gradient(135deg, ${gradient.from}, ${gradient.to}); border-color: ${gradient.border}; color: ${gradient.color};`;
20833      
20834      return `<a href="/product/kategori/?kategori_path=${encodeURIComponent(n.path)}" class="mm2-card" data-mm2="card" data-path="${esc(n.path)}" data-label="${esc(n.label)}">
20835        <span class="mm2-ico" style="${iconStyle}">${icon()}</span>
20836        <span style="flex: 1;">
20837          <div class="mm2-title">${esc(title)}</div>
20838          <div class="mm2-sub">${esc(sub)}</div>
20839        </span>
20840      </a>`;
20841    }).join('');
20842    const closeBtnHtml = !isDesktop() ? mobileCloseBtn() : '';
20843    topView.innerHTML = `<div class="mm2-top">${closeBtnHtml}<div class="mm2-grid">${html}</div></div>`;
20844  }
20845
20846  // Open/close with safe-area
20847  trigger.addEventListener('click', (e)=>{ e.preventDefault(); document.body.classList.contains('mm2-open') ? close() : open(); });
20848  trigger.addEventListener('pointerenter', ()=>{ if (isDesktop()) { clearTimeout(closeTimer); hoverTimer=setTimeout(open, 80); }});
20849  trigger.addEventListener('pointerleave', (e)=>{ if (isDesktop() && !isToSafeArea(e)) { clearTimeout(hoverTimer); closeTimer=setTimeout(close, 200); }});
20850  panel.addEventListener('pointerenter', ()=>{ if (isDesktop()) clearTimeout(closeTimer); });
20851  panel.addEventListener('pointerleave', (e)=>{ if (isDesktop() && !isToSafeArea(e)) closeTimer=setTimeout(close, 200); });
20852  backdrop?.addEventListener('pointerenter', ()=>{ clearTimeout(closeTimer); });
20853  backdrop?.addEventListener('click', close);
20854  document.addEventListener('keydown', (e)=>{ if (e.key==='Escape') close(); });
20855
20856  // Card click → open detail (unless cmd/ctrl to open link)
20857  panel.addEventListener('click', (e)=>{
20858    const closeBtn = e.target.closest('[data-mm2="close"]'); if (closeBtn) { e.preventDefault(); close(); return; }
20859    const back = e.target.closest('[data-mm2="back"]'); if (back) { e.preventDefault(); showTop(); return; }
20860    const card = e.target.closest('[data-mm2="card"]');
20861    if (card && !e.metaKey && !e.ctrlKey) {
20862      e.preventDefault();
20863      const n = data.find(d => d.path === card.dataset.path);
20864      if (n) showDetail(n);
20865    }
20866    const nav = e.target.closest('[data-mm2="nav"]');
20867    if (nav) { saveRecent(nav.dataset.path, nav.dataset.label); }
20868  });
20869
20870  // Swipe-down gesture for mobile (bottom sheet close)
20871  if (!isDesktop()) {
20872    let startY = 0, currentY = 0, isDragging = false, startTime = 0;
20873    const SWIPE_THRESHOLD = 80;
20874    const VELOCITY_THRESHOLD = 0.3;
20875
20876    panel.addEventListener('touchstart', (e) => {
20877      if (!document.body.classList.contains('mm2-open')) return;
20878      const touch = e.touches[0];
20879      const scrollableContent = e.target.closest('.mm2-top, .mm2-detail');
20880      
20881      // Prevent drag if content is scrolled
20882      if (scrollableContent && scrollableContent.scrollTop > 5) return;
20883      
20884      // Only start drag from top area or handle
20885      const rect = panel.getBoundingClientRect();
20886      if (touch.clientY - rect.top > 60) return;
20887      
20888      startY = touch.clientY;
20889      currentY = startY;
20890      startTime = Date.now();
20891      isDragging = true;
20892    }, { passive: true });
20893
20894    panel.addEventListener('touchmove', (e) => {
20895      if (!isDragging) return;
20896      const touch = e.touches[0];
20897      currentY = touch.clientY;
20898      const deltaY = currentY - startY;
20899      
20900      // Only allow downward swipe
20901      if (deltaY > 0) {
20902        const opacity = Math.max(0.4, 1 - (deltaY / 300));
20903        panel.style.transform = `translateX(0) translateY(${deltaY}px)`;
20904        panel.style.opacity = opacity;
20905      }
20906    }, { passive: true });
20907
20908    const endDrag = () => {
20909      if (!isDragging) return;
20910      const deltaY = currentY - startY;
20911      const deltaTime = Date.now() - startTime;
20912      const velocity = deltaY / deltaTime;
20913      
20914      // Close if swiped down enough or fast swipe
20915      if (deltaY > SWIPE_THRESHOLD || velocity > VELOCITY_THRESHOLD) {
20916        close();
20917      }
20918      
20919      // Reset position
20920      panel.style.transform = '';
20921      panel.style.opacity = '';
20922      isDragging = false;
20923    };
20924
20925    panel.addEventListener('touchend', endDrag, { passive: true });
20926    panel.addEventListener('touchcancel', endDrag, { passive: true });
20927  }
20928
20929  // Fetch data with loading state
20930  if (!isLoading) {
20931    isLoading = true;
20932    showLoading();
20933    
20934    fetch(apiUrl, { headers: { 'Accept': 'application/json' }})
20935      .then(r => {
20936        if (!r.ok) throw new Error('Network response was not ok');
20937        return r.json();
20938      })
20939      .then(j => { 
20940        data = Array.isArray(j.data) ? j.data : []; 
20941        if (!data.length) {
20942          topView.innerHTML = `<div class="mm2-top"><div style="text-align:center;padding:40px;color:var(--mm2-fg-muted);">
20943            <p>Tidak ada kategori tersedia</p>
20944          </div></div>`;
20945          return;
20946        }
20947        buildTop();
20948        isLoading = false;
20949      })
20950      .catch(err => {
20951        console.error('Failed to load categories:', err);
20952        topView.innerHTML = `<div class="mm2-top"><div style="text-align:center;padding:40px;color:var(--mm2-fg-muted);">
20953          <p>Gagal memuat kategori. Silakan coba lagi.</p>
20954        </div></div>`;
20955        isLoading = false;
20956      });
20957  }
20958})();
20959
20960// Add skeleton animation styles
20961const style = document.createElement('style');
20962style.textContent = `
20963.mm2-skeleton {
20964  pointer-events: none;
20965  animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
20966}
20967.skeleton-box, .skeleton-line {
20968  background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 50%, #f1f5f9 75%);
20969  background-size: 200% 100%;
20970  animation: shimmer 1.5s infinite;
20971  border-radius: 8px;
20972}
20973@keyframes pulse {
20974  0%, 100% { opacity: 1; }
20975  50% { opacity: 0.6; }
20976}
20977@keyframes shimmer {
20978  0% { background-position: 200% 0; }
20979  100% { background-position: -200% 0; }
20980}
20981`;
20982document.head.appendChild(style);
20983</script>
20983
20984    
20984<script data-inlined="benchmark.js">
20985/**
20986 * Benchmark Data Loader
20987 * Handles AJAX fetch and display of benchmark data
20988 */
20989
20990(function() {
20991    'use strict';
20992    
20993    // Wait for DOM ready
20994    if (document.readyState === 'loading') {
20995        document.addEventListener('DOMContentLoaded', initBenchmark);
20996    } else {
20997        initBenchmark();
20998    }
20999    
21000    function initBenchmark() {
21001        const section = document.querySelector('.pd-benchmark-section');
21002        if (!section) return;
21003        
21004        const button = document.getElementById('btn-load-benchmark');
21005        const content = document.getElementById('benchmark-content');
21006        const productId = section.dataset.productId;
21007        
21008        if (!button || !content || !productId) return;
21009        
21010        let isLoading = false;
21011        let dataLoaded = false;
21012        
21013        button.addEventListener('click', function() {
21014            if (isLoading || dataLoaded) return;
21015            
21016            loadBenchmarkData(productId, button, content);
21017        });
21018    }
21019    
21020    function loadBenchmarkData(productId, button, container) {
21021        // Update button state
21022        button.disabled = true;
21023        button.classList.add('loading');
21024        const originalText = button.querySelector('.btn-text').textContent;
21025        button.querySelector('.btn-text').textContent = 'Memuat data...';
21026        
21027        // Show loading state
21028        container.style.display = 'block';
21029        container.innerHTML = `
21030            <div class="benchmark-loading">
21031                <div class="benchmark-loading-spinner"></div>
21032                <p>Mengambil data benchmark...</p>
21033            </div>
21034        `;
21035        
21036        // Fetch data
21037        fetch('/api/benchmark/fetch.php?product_id=' + productId)
21038            .then(function(response) {
21039                if (!response.ok) {
21040                    throw new Error('HTTP error ' + response.status);
21041                }
21042                return response.json();
21043            })
21044            .then(function(result) {
21045                if (result.success && result.data) {
21046                    renderBenchmarkData(result.data, container);
21047                    button.style.display = 'none'; // Hide button after successful load
21048                } else {
21049                    throw new Error(result.error || 'Data tidak tersedia');
21050                }
21051            })
21052            .catch(function(error) {
21053                console.error('Benchmark fetch error:', error);
21054                container.innerHTML = `
21055                    <div class="benchmark-error">
21056                        <p><strong>⚠️ Data Benchmark Tidak Tersedia</strong></p>
21057                        <p style="font-size: 14px; margin-top: 8px;">
21058                            ${error.message || 'Terjadi kesalahan saat memuat data'}
21059                        </p>
21060                        <button type="button" onclick="location.reload()" 
21061                                style="margin-top: 16px; padding: 8px 20px; background: #667eea; color: #fff; border: none; border-radius: 6px; cursor: pointer;">
21062                            Coba Lagi
21063                        </button>
21064                    </div>
21065                `;
21066                
21067                // Reset button
21068                button.disabled = false;
21069                button.classList.remove('loading');
21070                button.querySelector('.btn-text').textContent = originalText;
21071            });
21072    }
21073    
21074    function renderBenchmarkData(data, container) {
21075        let html = '<div class="benchmark-data-wrapper">';
21076        
21077        // Chipset info if available
21078        if (data.chipset) {
21079            html += `
21080                <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 10px 16px; border-radius: 8px; margin-bottom: 12px;">
21081                    <div style="font-size: 11px; opacity: 0.85; margin-bottom: 2px;">Chipset</div>
21082                    <div style="font-size: 15px; font-weight: 600;">${escapeHtml(data.chipset)}</div>
21083                </div>
21084            `;
21085        }
21086        
21087        // Main benchmark scores
21088        if (data.antutu || data.geekbench_multi || data.overall_score) {
21089            html += '<div class="benchmark-data-grid">';
21090            
21091            if (data.antutu) {
21092                html += `
21093                    <div class="benchmark-card">
21094                        <div class="benchmark-card-label">ANTUTU</div>
21095                        <div class="benchmark-card-value large">${formatNumber(data.antutu)}</div>
21096                        ${getScoreBadge(data.antutu, 'antutu')}
21097                    </div>
21098                `;
21099            }
21100            
21101            if (data.geekbench_multi) {
21102                html += `
21103                    <div class="benchmark-card">
21104                        <div class="benchmark-card-label">GEEKBENCH MULTI</div>
21105                        <div class="benchmark-card-value">${formatNumber(data.geekbench_multi)}</div>
21106                        ${data.geekbench_single ? '<div class="benchmark-card-subtitle">Single: ' + formatNumber(data.geekbench_single) + '</div>' : ''}
21107                    </div>
21108                `;
21109            }
21110            
21111            if (data.gaming_score) {
21112                html += `
21113                    <div class="benchmark-card">
21114                        <div class="benchmark-card-label">GAMING</div>
21115                        <div class="benchmark-card-value">${data.gaming_score}<span style="font-size: 14px; opacity: 0.6;">/100</span></div>
21116                    </div>
21117                `;
21118            }
21119            
21120            if (data.battery_score) {
21121                html += `
21122                    <div class="benchmark-card">
21123                        <div class="benchmark-card-label">BATTERY</div>
21124                        <div class="benchmark-card-value">${data.battery_score}<span style="font-size: 14px; opacity: 0.6;">/100</span></div>
21125                    </div>
21126                `;
21127            }
21128            
21129            if (data.overall_score) {
21130                html += `
21131                    <div class="benchmark-card">
21132                        <div class="benchmark-card-label">OVERALL</div>
21133                        <div class="benchmark-card-value">${data.overall_score}<span style="font-size: 14px; opacity: 0.6;">/100</span></div>
21134                    </div>
21135                `;
21136            }
21137            
21138            html += '</div>';
21139        }
21140        
21141        // AI Performance Description
21142        if (data.notes) {
21143            html += `
21144                <div style="background: #f8f9fa; border-left: 3px solid #667eea; padding: 10px 14px; border-radius: 6px; margin-top: 12px;">
21145                    <div style="font-size: 11px; font-weight: 600; color: #667eea; margin-bottom: 6px;">💡 Analisis Performa</div>
21146                    <div style="font-size: 13px; color: #4a5568; line-height: 1.5;">${escapeHtml(data.notes)}</div>
21147                </div>
21148            `;
21149        }
21150        
21151        // Attribution
21152        html += `
21153            <div class="benchmark-attribution">
21154                <p style="font-size: 11px; margin: 8px 0 0 0; color: #888;">Data benchmark dianalisis menggunakan AI dari spesifikasi produk</p>
21155                ${data.fetched_at ? '<p style="font-size: 10px; margin: 4px 0 0 0; color: #aaa;">Terakhir diperbarui: ' + formatDate(data.fetched_at) + '</p>' : ''}
21156            </div>
21157        `;
21158        
21159        html += '</div>';
21160        
21161        container.innerHTML = html;
21162    }
21163    
21164    function formatNumber(num) {
21165        if (!num) return '-';
21166        return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.');
21167    }
21168    
21169    function escapeHtml(text) {
21170        if (!text) return '';
21171        const div = document.createElement('div');
21172        div.textContent = text;
21173        return div.innerHTML;
21174    }
21175    
21176    function formatDate(dateStr) {
21177        try {
21178            const date = new Date(dateStr);
21179            const options = { year: 'numeric', month: 'long', day: 'numeric' };
21180            return date.toLocaleDateString('id-ID', options);
21181        } catch (e) {
21182            return dateStr;
21183        }
21184    }
21185    
21186    function getScoreBadge(score, type) {
21187        let category = '';
21188        let label = '';
21189        
21190        if (type === 'antutu') {
21191            if (score >= 500000) {
21192                category = 'excellent';
21193                label = 'Flagship';
21194            } else if (score >= 350000) {
21195                category = 'good';
21196                label = 'High-End';
21197            } else if (score >= 200000) {
21198                category = 'average';
21199                label = 'Mid-Range';
21200            } else {
21201                category = 'average';
21202                label = 'Entry-Level';
21203            }
21204        }
21205        
21206        if (label) {
21207            return '<div class="benchmark-score-badge ' + category + '">' + label + '</div>';
21208        }
21209        
21210        return '';
21211    }
21212})();
21213
21214</script>
21214
21215  
21215<script data-inlined="product-compare.js">
21216(function() {
21217  'use strict';
21218  
21219  // State management
21220  let compareList = [];
21221  let isAnalyzing = false;
21222  let currentAnalysis = null;
21223  
21224  // DOM elements
21225  const compareBtn = document.getElementById('compareBtn');
21226  const compareSidebar = document.getElementById('compareSidebar');
21227  const compareOverlay = document.getElementById('compareOverlay');
21228  const compareClose = document.getElementById('compareClose');
21229  const compareModal = document.getElementById('aiCompareModal');
21230  const compareModalClose = document.getElementById('aiCompareModalClose');
21231  
21232  // Initialize
21233  document.addEventListener('DOMContentLoaded', async function() {
21234    await initializeCompare();
21235    setupEventListeners();
21236  });
21237  
21238  /**
21239   * Initialize compare functionality
21240   */
21241  async function initializeCompare() {
21242    // Load compare list first before setting up buttons
21243    await loadCompareList();
21244    
21245    // Setup product detail compare button if exists
21246    const productCompareBtn = document.getElementById('pd-compare-btn');
21247    
21248    if (productCompareBtn) {
21249      setupProductCompareButton(productCompareBtn);
21250    }
21251    
21252    // Check for existing compare list and update UI
21253    if (compareList.length > 0) {
21254      // Only update badge and buttons, no sticky bar
21255    }
21256  }
21257  
21258  /**
21259   * Setup event listeners
21260   */
21261  function setupEventListeners() {
21262    // Navigation compare button
21263    if (compareBtn) {
21264      compareBtn.addEventListener('click', toggleCompareSidebar);
21265    }
21266    
21267    // Sidebar controls
21268    if (compareClose) {
21269      compareClose.addEventListener('click', closeCompareSidebar);
21270    }
21271    
21272    if (compareOverlay) {
21273      compareOverlay.addEventListener('click', closeCompareSidebar);
21274    }
21275    
21276    // Sidebar actions
21277    const clearAllBtn = document.getElementById('compareClearAll');
21278    if (clearAllBtn) {
21279      clearAllBtn.addEventListener('click', clearAllProducts);
21280    }
21281    
21282    const analyzeBtn = document.getElementById('compareAnalyze');
21283    if (analyzeBtn) {
21284      analyzeBtn.addEventListener('click', startAIAnalysis);
21285    }
21286    
21287    // Modal controls
21288    if (compareModalClose) {
21289      compareModalClose.addEventListener('click', closeModal);
21290    }
21291    
21292    const modalBackdrop = compareModal?.querySelector('.modal-backdrop');
21293    if (modalBackdrop) {
21294      modalBackdrop.addEventListener('click', closeModal);
21295    }
21296    
21297    // Modal actions
21298    const retryBtn = document.getElementById('aiRetry');
21299    if (retryBtn) {
21300      retryBtn.addEventListener('click', startAIAnalysis);
21301    }
21302    
21303    // Removed share and add to cart buttons - replaced with AI disclaimer
21304  }
21305  
21306  /**
21307   * Setup product detail compare button
21308   */
21309  function setupProductCompareButton(button) {
21310    const productId = parseInt(button.dataset.productId);
21311    if (!productId) return;
21312    
21313    // Check if product is already in compare list
21314    updateProductCompareButtonState(button, productId);
21315    
21316    button.addEventListener('click', async function() {
21317      // Get current variant ID (if any)
21318      const variantIdElement = document.getElementById('pd-variant-id');
21319      const variantId = variantIdElement ? parseInt(variantIdElement.value) || 0 : 0;
21320      
21321      // Update button's variant data for current selection
21322      button.dataset.variantId = variantId.toString();
21323      
21324      await toggleProductInCompare(productId, button, variantId);
21325    });
21326  }
21327  
21328  /**
21329   * Toggle product in compare list
21330   */
21331  async function toggleProductInCompare(productId, button, variantId = 0) {
21332    // Check by product ID (variants of same product treated as same item)
21333    const isInCompare = compareList.some(p => parseInt(p.id) === parseInt(productId));
21334    
21335    try {
21336      if (isInCompare) {
21337        await removeFromCompare(productId);
21338        showToast('Produk dihapus dari perbandingan', 'success');
21339      } else {
21340        await addToCompare(productId, variantId);
21341        showToast('Produk ditambahkan ke perbandingan', 'success');
21342        
21343        // Trigger fly-to-compare animation
21344        triggerFlyToCompareAnimation(button);
21345      }
21346      
21347      updateProductCompareButtonState(button, productId);
21348      updateCompareBadge();
21349      updateAllCompareButtons();
21350      
21351    } catch (error) {
21352      showToast(error.message || 'Terjadi kesalahan', 'error');
21353    }
21354  }
21355  
21356  /**
21357   * Update product compare button state
21358   */
21359  function updateProductCompareButtonState(button, productId) {
21360    // Ensure both IDs are compared as integers
21361    const isInCompare = compareList.some(p => parseInt(p.id) === parseInt(productId));
21362    button.classList.toggle('added', isInCompare);
21363    
21364    // Handle visibility of text elements
21365    const compareText = button.querySelector('.compare-text');
21366    const compareRemove = button.querySelector('.compare-remove');
21367    const compareNote = document.getElementById('compare-note');
21368    
21369    if (compareText) {
21370      compareText.style.display = isInCompare ? 'none' : 'inline';
21371    }
21372    
21373    if (compareRemove) {
21374      if (isInCompare) {
21375        compareRemove.removeAttribute('hidden');
21376        compareRemove.style.display = 'flex';
21377      } else {
21378        compareRemove.setAttribute('hidden', '');
21379        compareRemove.style.display = 'none';
21380      }
21381    }
21382    
21383    // Update info note
21384    if (compareNote) {
21385      if (isInCompare) {
21386        const currentCount = compareList.length;
21387        compareNote.textContent = `Produk sudah ada di daftar perbandingan (${currentCount}/5). Klik tombol untuk menghapus.`;
21388      } else {
21389        compareNote.textContent = 'Bandingkan hingga 5 produk dengan bantuan AI';
21390      }
21391    }
21392    
21393    // Inline compare preview removed - keeping it simple
21394  }
21395  
21396  /**
21397   * Add product to compare list
21398   */
21399  async function addToCompare(productId, variantId = 0) {
21400    const requestData = { product_id: productId };
21401    if (variantId > 0) {
21402      requestData.variant_id = variantId;
21403    }
21404    
21405    const response = await fetch('/api/compare/add.php', {
21406      method: 'POST',
21407      headers: {
21408        'Content-Type': 'application/json',
21409      },
21410      body: JSON.stringify(requestData),
21411      credentials: 'same-origin'
21412    });
21413    
21414    const result = await response.json();
21415    
21416    if (!result.success) {
21417      throw new Error(result.error || 'Gagal menambahkan produk');
21418    }
21419    
21420    // Send Google Analytics event for add to compare
21421    if (typeof gtag === 'function') {
21422      gtag('event', 'add_to_compare', {
21423        event_category: 'Product Compare',
21424        event_label: 'Product Added to Compare',
21425        product_id: productId,
21426        variant_id: variantId || 0,
21427        custom_map: {
21428          custom_parameter_1: 'compare_add_product'
21429        }
21430      });
21431    }
21432    
21433    await loadCompareList();
21434    return result;
21435  }
21436  
21437  /**
21438   * Remove product from compare list
21439   */
21440  async function removeFromCompare(productId) {
21441    const response = await fetch('/api/compare/remove.php', {
21442      method: 'POST',
21443      headers: {
21444        'Content-Type': 'application/json',
21445      },
21446      body: JSON.stringify({ product_id: productId }),
21447      credentials: 'same-origin'
21448    });
21449    
21450    const result = await response.json();
21451    
21452    if (!result.success) {
21453      throw new Error(result.error || 'Gagal menghapus produk');
21454    }
21455    
21456    // Send Google Analytics event for remove from compare
21457    if (typeof gtag === 'function') {
21458      gtag('event', 'remove_from_compare', {
21459        event_category: 'Product Compare',
21460        event_label: 'Product Removed from Compare',
21461        product_id: productId,
21462        custom_map: {
21463          custom_parameter_1: 'compare_remove_product'
21464        }
21465      });
21466    }
21467    
21468    await loadCompareList();
21469    return result;
21470  }
21471  
21472  /**
21473   * Clear all products from compare list
21474   */
21475  async function clearAllProducts() {
21476    if (compareList.length === 0) return;
21477    
21478    // Show confirmation modal
21479    const confirmed = await showConfirmModal({
21480      title: 'Hapus Semua Produk',
21481      message: 'Apakah Anda yakin ingin menghapus semua produk dari daftar perbandingan?',
21482      confirmText: 'Hapus Semua',
21483      cancelText: 'Batal',
21484      type: 'danger'
21485    });
21486    
21487    if (!confirmed) return;
21488    
21489    try {
21490      const response = await fetch('/api/compare/clear.php', {
21491        method: 'POST',
21492        credentials: 'same-origin'
21493      });
21494      
21495      const result = await response.json();
21496      
21497      if (!result.success) {
21498        throw new Error(result.error || 'Gagal menghapus produk');
21499      }
21500      
21501      // Send Google Analytics event for clear all products
21502      if (typeof gtag === 'function') {
21503        gtag('event', 'compare_clear_all', {
21504          event_category: 'Product Compare',
21505          event_label: 'Clear All Products',
21506          products_count: compareList.length,
21507          custom_map: {
21508            custom_parameter_1: 'compare_clear_action'
21509          }
21510        });
21511      }
21512      
21513      await loadCompareList();
21514      showToast('Semua produk dihapus dari perbandingan', 'success');
21515      
21516    } catch (error) {
21517      showToast(error.message || 'Terjadi kesalahan', 'error');
21518    }
21519  }
21520  
21521  /**
21522   * Load compare list from server
21523   */
21524  async function loadCompareList() {
21525    try {
21526      const response = await fetch('/api/compare/get.php', {
21527        credentials: 'same-origin'
21528      });
21529      
21530      const result = await response.json();
21531      
21532      if (result.success) {
21533        compareList = result.products || [];
21534        updateCompareSidebar();
21535        updateCompareBadge();
21536        updateAllCompareButtons();
21537      }
21538      
21539    } catch (error) {
21540      // Silently handle error - compare functionality will work with empty list
21541    }
21542  }
21543  
21544  /**
21545   * Update compare sidebar content
21546   */
21547  function updateCompareSidebar() {
21548    const emptyState = document.getElementById('compareEmpty');
21549    const listContainer = document.getElementById('compareList');
21550    const actionsContainer = document.getElementById('compareActions');
21551    const countElement = document.getElementById('compareCountSidebar');
21552    
21553    if (!emptyState || !listContainer || !actionsContainer || !countElement) return;
21554    
21555    // Update count
21556    countElement.textContent = `(${compareList.length})`;
21557    
21558    if (compareList.length === 0) {
21559      emptyState.style.display = 'block';
21560      listContainer.style.display = 'none';
21561      actionsContainer.style.display = 'none';
21562    } else {
21563      emptyState.style.display = 'none';
21564      listContainer.style.display = 'block';
21565      actionsContainer.style.display = 'flex';
21566      
21567      // Render product list
21568      listContainer.innerHTML = compareList.map(product => {
21569        // Display variant name if available
21570        let displayName = product.name;
21571        if (product.combination_name) {
21572          displayName += ` (${product.combination_name})`;
21573        }
21574        
21575        // Use variant price if available
21576        let displayPrice = product.price;
21577        if (product.variant_price !== undefined && product.variant_price !== null) {
21578          displayPrice = product.variant_price;
21579        }
21580        
21581        return `
21582          <div class="compare-item" data-product-id="${product.id}">
21583            <div class="compare-item-image">
21584              <img src="${product.image_url}" alt="${escapeHtml(displayName)}" loading="lazy">
21585            </div>
21586            <div class="compare-item-info">
21587              <div class="compare-item-name">${escapeHtml(displayName)}</div>
21588              <div class="compare-item-price">${formatPrice(displayPrice)}</div>
21589            </div>
21590            <button class="compare-item-remove" data-product-id="${product.id}" aria-label="Hapus">
21591              <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
21592                <path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/>
21593              </svg>
21594            </button>
21595          </div>
21596        `;
21597      }).join('');
21598      
21599      // Setup event listeners for remove buttons
21600      setupRemoveButtons();
21601      
21602      // Update analyze button state
21603      const analyzeBtn = document.getElementById('compareAnalyze');
21604      if (analyzeBtn) {
21605        analyzeBtn.disabled = compareList.length < 2;
21606      }
21607    }
21608  }
21609  
21610  /**
21611   * Setup remove buttons event listeners
21612   */
21613  function setupRemoveButtons() {
21614    const removeButtons = document.querySelectorAll('.compare-item-remove');
21615    removeButtons.forEach(button => {
21616      button.addEventListener('click', async (e) => {
21617        e.preventDefault();
21618        const productId = parseInt(button.dataset.productId);
21619        if (productId) {
21620          await removeProduct(productId);
21621        }
21622      });
21623    });
21624  }
21625
21626  /**
21627   * Update all compare buttons on the page
21628   */
21629  function updateAllCompareButtons() {
21630    // Update product detail compare button
21631    const productBtn = document.getElementById('pd-compare-btn');
21632    if (productBtn) {
21633      const productId = parseInt(productBtn.dataset.productId);
21634      if (productId) {
21635        updateProductCompareButtonState(productBtn, productId);
21636      }
21637    }
21638    
21639    // Update any other compare buttons on the page
21640    const allCompareButtons = document.querySelectorAll('[data-product-id][class*="compare"]');
21641    allCompareButtons.forEach(button => {
21642      const productId = parseInt(button.dataset.productId);
21643      if (productId && button.id !== 'pd-compare-btn') {
21644        updateProductCompareButtonState(button, productId);
21645      }
21646    });
21647  }
21648
21649  /**
21650   * Update compare badge
21651   */
21652  function updateCompareBadge() {
21653    const badge = document.getElementById('compareCount');
21654    if (!badge) return;
21655    
21656    const oldCount = parseInt(badge.textContent) || 0;
21657    const newCount = compareList.length;
21658    
21659    badge.textContent = newCount;
21660    badge.classList.toggle('active', newCount > 0);
21661    
21662    // Animate badge if count increased
21663    if (newCount > oldCount) {
21664      animateCompareBadge();
21665      highlightCompareButton();
21666    }
21667    
21668    // Sticky bar removed - keeping only fly-to-navbar animation
21669  }
21670  
21671  /**
21672   * Animate compare badge when new product added
21673   */
21674  function animateCompareBadge() {
21675    const badge = document.getElementById('compareCount');
21676    if (!badge) return;
21677    
21678    badge.style.transform = 'scale(1.3)';
21679    badge.style.transition = 'transform 0.3s ease';
21680    
21681    setTimeout(() => {
21682      badge.style.transform = 'scale(1)';
21683    }, 300);
21684  }
21685  
21686  /**
21687   * Highlight compare button temporarily
21688   */
21689  function highlightCompareButton() {
21690    const compareBtn = document.getElementById('compareBtn');
21691    if (!compareBtn) return;
21692    
21693    compareBtn.classList.add('pulse-highlight');
21694    
21695    setTimeout(() => {
21696      compareBtn.classList.remove('pulse-highlight');
21697    }, 3000);
21698  }
21699  
21700  /**
21701   * Trigger fly-to-compare animation
21702   */
21703  function triggerFlyToCompareAnimation(sourceButton) {
21704    if (!sourceButton) {
21705      return;
21706    }
21707    
21708    // Check if mobile view (bottom nav) or desktop (top nav)
21709    const isMobile = window.innerWidth <= 768;
21710    
21711    // Get source position
21712    const sourceRect = sourceButton.getBoundingClientRect();
21713    
21714    // Calculate target position
21715    let targetTop, targetLeft;
21716    
21717    if (isMobile) {
21718      // Mobile: fly to fixed bottom position (bottom nav area)
21719      // Use fixed coordinates regardless of bottom nav visibility
21720      targetTop = window.innerHeight - 80; // Just above bottom nav area
21721      targetLeft = window.innerWidth / 2 - 30; // Center horizontally
21722    } else {
21723      // Desktop: fly to top navbar compare button
21724      const targetElement = document.getElementById('compareBtn');
21725      if (!targetElement) {
21726        return;
21727      }
21728      const targetRect = targetElement.getBoundingClientRect();
21729      targetTop = targetRect.top + 10;
21730      targetLeft = targetRect.left + 10;
21731    }
21732    
21733    // Get product image for animation - try multiple selectors
21734    let productImage = document.querySelector('#pd-main-img, .product-gallery img, .pd-image img, .product-image img, .product-main-image img, img[alt*="produk"], img[src*="product"]');
21735    
21736    // Create animated element
21737    const flyingElement = document.createElement('div');
21738    flyingElement.className = 'flying-product';
21739    
21740    if (productImage) {
21741      flyingElement.innerHTML = `<img src="${productImage.src}" alt="Flying product">`;
21742      flyingElement.querySelector('img').style.cssText = `
21743        width: 100%;
21744        height: 100%;
21745        object-fit: cover;
21746        border-radius: 8px;
21747        box-shadow: 0 4px 12px rgba(0,0,0,0.3);
21748      `;
21749    } else {
21750      // Fallback: colored div with icon
21751      flyingElement.innerHTML = `<div class="flying-fallback">📦</div>`;
21752      flyingElement.querySelector('.flying-fallback').style.cssText = `
21753        width: 100%;
21754        height: 100%;
21755        background: #3b82f6;
21756        border-radius: 8px;
21757        display: flex;
21758        align-items: center;
21759        justify-content: center;
21760        font-size: 24px;
21761        box-shadow: 0 4px 12px rgba(0,0,0,0.3);
21762      `;
21763    }
21764    
21765    flyingElement.style.cssText = `
21766      position: fixed;
21767      top: ${sourceRect.top}px;
21768      left: ${sourceRect.left}px;
21769      width: 60px;
21770      height: 60px;
21771      z-index: 9999;
21772      pointer-events: none;
21773      opacity: 1;
21774      transform: scale(1);
21775    `;
21776    
21777    document.body.appendChild(flyingElement);
21778    
21779    // Use Web Animations API for more reliable animation
21780    flyingElement.animate([
21781      // Keyframe 1: Initial state
21782      {
21783        top: `${sourceRect.top}px`,
21784        left: `${sourceRect.left}px`,
21785        transform: 'scale(1)',
21786        opacity: 1
21787      },
21788      // Keyframe 2: Final state
21789      {
21790        top: `${targetTop}px`,
21791        left: `${targetLeft}px`,
21792        transform: 'scale(0.3)',
21793        opacity: 0
21794      }
21795    ], {
21796      duration: 800,
21797      easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
21798      fill: 'forwards'
21799    });
21800    
21801    // Clean up after animation completes
21802    setTimeout(() => {
21803      if (flyingElement.parentNode) {
21804        flyingElement.parentNode.removeChild(flyingElement);
21805      }
21806    }, 850);
21807  }
21808  
21809  // Sticky compare bar functions removed - using only fly-to-navbar animation
21810  
21811  /**
21812   * Toggle compare sidebar
21813   */
21814  function toggleCompareSidebar() {
21815    if (!compareSidebar) return;
21816    
21817    const isOpen = compareSidebar.classList.contains('open');
21818    
21819    if (isOpen) {
21820      closeCompareSidebar();
21821    } else {
21822      openCompareSidebar();
21823    }
21824  }
21825  
21826  /**
21827   * Open compare sidebar
21828   */
21829  function openCompareSidebar() {
21830    if (!compareSidebar || !compareOverlay) return;
21831    
21832    compareSidebar.classList.add('open');
21833    compareOverlay.classList.add('active');
21834    document.body.style.overflow = 'hidden';
21835    
21836    // Send Google Analytics event for compare sidebar open
21837    if (typeof gtag === 'function') {
21838      gtag('event', 'compare_sidebar_open', {
21839        event_category: 'Product Compare',
21840        event_label: 'Compare Sidebar Opened',
21841        products_count: compareList.length,
21842        custom_map: {
21843          custom_parameter_1: 'compare_sidebar_view'
21844        }
21845      });
21846    }
21847  }
21848  
21849  /**
21850   * Close compare sidebar
21851   */
21852  function closeCompareSidebar() {
21853    if (!compareSidebar || !compareOverlay) return;
21854    
21855    compareSidebar.classList.remove('open');
21856    compareOverlay.classList.remove('active');
21857    document.body.style.overflow = '';
21858  }
21859  
21860  /**
21861   * Start AI analysis
21862   */
21863  async function startAIAnalysis() {
21864    if (compareList.length < 2) {
21865      showToast('Minimal 2 produk diperlukan untuk analisis AI', 'warning');
21866      return;
21867    }
21868    
21869    if (isAnalyzing) return;
21870    
21871    // Send Google Analytics event for AI Analysis start
21872    if (typeof gtag === 'function') {
21873      gtag('event', 'ai_analysis_start', {
21874        event_category: 'Product Compare',
21875        event_label: 'AI Analysis Button Click',
21876        products_count: compareList.length,
21877        product_ids: compareList.map(p => p.id).join(','),
21878        custom_map: {
21879          custom_parameter_1: 'compare_ai_analysis'
21880        }
21881      });
21882    }
21883    
21884    // Check if products are in similar categories
21885    const mismatchInfo = await checkProductCategoryMismatch();
21886    if (mismatchInfo.shouldWarn) {
21887      const categoryList = Array.from(mismatchInfo.categories).join(', ');
21888      const confirmed = await showConfirmModal({
21889        title: 'Produk Berbeda Kategori',
21890        message: `Produk yang Anda pilih berasal dari kategori yang berbeda: ${categoryList}. AI tetap bisa menganalisis, tapi perbandingan mungkin kurang relevan. Lanjutkan analisis?`,
21891        confirmText: 'Ya, Analisis Tetap',
21892        cancelText: 'Batal',
21893        type: 'warning'
21894      });
21895      
21896      if (!confirmed) return;
21897    }
21898    
21899    openModal();
21900    showLoadingState();
21901    isAnalyzing = true;
21902    
21903    try {
21904      const response = await fetch('/api/compare/ai-analysis.php', {
21905        method: 'POST',
21906        credentials: 'same-origin'
21907      });
21908      
21909      const result = await response.json();
21910      
21911      if (!result.success) {
21912        throw new Error(result.error || 'Analisis AI gagal');
21913      }
21914      
21915      currentAnalysis = result;
21916      showAnalysisResults(result);
21917      
21918      // Send Google Analytics event for successful AI Analysis
21919      if (typeof gtag === 'function') {
21920        gtag('event', 'ai_analysis_success', {
21921          event_category: 'Product Compare',
21922          event_label: 'AI Analysis Completed',
21923          products_count: compareList.length,
21924          analysis_type: 'product_comparison',
21925          custom_map: {
21926            custom_parameter_1: 'compare_ai_success'
21927          }
21928        });
21929      }
21930      
21931    } catch (error) {
21932      showErrorState(error.message);
21933      
21934      // Send Google Analytics event for failed AI Analysis
21935      if (typeof gtag === 'function') {
21936        gtag('event', 'ai_analysis_error', {
21937          event_category: 'Product Compare',
21938          event_label: 'AI Analysis Failed',
21939          products_count: compareList.length,
21940          error_message: error.message,
21941          custom_map: {
21942            custom_parameter_1: 'compare_ai_error'
21943          }
21944        });
21945      }
21946    } finally {
21947      isAnalyzing = false;
21948    }
21949  }
21950  
21951  /**
21952   * Show modal
21953   */
21954  function openModal() {
21955    if (!compareModal) return;
21956    compareModal.classList.add('open');
21957    document.body.style.overflow = 'hidden';
21958  }
21959  
21960  /**
21961   * Close modal
21962   */
21963  function closeModal() {
21964    if (!compareModal) return;
21965    compareModal.classList.remove('open');
21966    document.body.style.overflow = '';
21967  }
21968  
21969  /**
21970   * Show loading state
21971   */
21972  function showLoadingState() {
21973    const loading = document.getElementById('aiLoading');
21974    const error = document.getElementById('aiError');
21975    const results = document.getElementById('aiResults');
21976    
21977    if (loading) loading.style.display = 'block';
21978    if (error) error.hidden = true;
21979    if (results) results.hidden = true;
21980  }
21981  
21982  /**
21983   * Show error state
21984   */
21985  function showErrorState(message) {
21986    const loading = document.getElementById('aiLoading');
21987    const error = document.getElementById('aiError');
21988    const results = document.getElementById('aiResults');
21989    const errorMessage = document.getElementById('aiErrorMessage');
21990    
21991    if (loading) loading.style.display = 'none';
21992    if (error) error.hidden = false;
21993    if (results) results.hidden = true;
21994    if (errorMessage) errorMessage.textContent = message;
21995  }
21996  
21997  /**
21998   * Show analysis results
21999   */
22000  function showAnalysisResults(analysis) {
22001    const loading = document.getElementById('aiLoading');
22002    const error = document.getElementById('aiError');
22003    const results = document.getElementById('aiResults');
22004    
22005    if (loading) loading.style.display = 'none';
22006    if (error) error.hidden = true;
22007    if (results) results.hidden = false;
22008    
22009    const data = analysis.data;
22010    
22011    // Update summary
22012    const summaryElement = document.getElementById('aiSummary');
22013    if (summaryElement) summaryElement.textContent = data.summary || '';
22014    
22015    const winnerProduct = document.getElementById('winnerProduct');
22016    const winnerReason = document.getElementById('winnerReason');
22017    if (winnerProduct && winnerReason && data.winner_overall) {
22018      const winner = compareList.find(p => parseInt(p.id) === parseInt(data.winner_overall));
22019      winnerProduct.textContent = winner ? winner.name : 'Tidak diketa
22019hui';
22020      winnerReason.textContent = data.winner_reason || '';
22021    }
22022    
22023    // Update comparison table
22024    updateComparisonTable(data.comparison_table);
22025    
22026    // Update detailed analysis
22027    updateDetailedAnalysis(data.detailed_analysis);
22028    
22029    // Update recommendations
22030    updateRecommendations(data.recommendations);
22031    
22032    // Update buying tips
22033    updateBuyingTips(data.buying_tips);
22034  }
22035  
22036  /**
22037   * Update comparison table
22038   */
22039  function updateComparisonTable(comparisonData) {
22040    const container = document.getElementById('comparisonTable');
22041    if (!container || !comparisonData?.categories) return;
22042    
22043    // First, create product header row with names
22044    const productHeaderHtml = `
22045      <div class="comparison-category">
22046        <div class="category-header">Produk</div>
22047        <div class="category-products">
22048          ${compareList.map(product => `
22049            <div class="product-score">
22050              <div class="product-name-header">
22051                <strong>${escapeHtml(product.name)}</strong>
22052              </div>
22053              <div class="product-price">
22054                ${formatPrice(product.price)}
22055              </div>
22056            </div>
22057          `).join('')}
22058        </div>
22059      </div>
22060    `;
22061    
22062    // Then create comparison categories
22063    const categoriesHtml = comparisonData.categories.map(category => `
22064      <div class="comparison-category">
22065        <div class="category-header">${escapeHtml(category.name)}</div>
22066        <div class="category-products">
22067          ${compareList.map(product => {
22068            const productData = category.products?.[product.id];
22069            return `
22070              <div class="product-score">
22071                <div class="score-value">
22072                  <span class="score-stars">${'★'.repeat(productData?.score || 0)}${'☆'.repeat(5 - (productData?.score || 0))}</span>
22073                  <span>${productData?.score || 0}/5</span>
22074                </div>
22075                <div class="score-note">${escapeHtml(productData?.note || '')}</div>
22076              </div>
22077            `;
22078          }).join('')}
22079        </div>
22080      </div>
22081    `).join('');
22082    
22083    container.innerHTML = productHeaderHtml + categoriesHtml;
22084  }
22085  
22086  /**
22087   * Update detailed analysis
22088   */
22089  function updateDetailedAnalysis(analysisData) {
22090    const container = document.getElementById('detailedAnalysis');
22091    if (!container || !analysisData) return;
22092    
22093    container.innerHTML = compareList.map(product => {
22094      const productAnalysis = analysisData[product.id];
22095      if (!productAnalysis) return '';
22096      
22097      // Use product name from API response if available, fallback to local name
22098      const productName = productAnalysis.product_name || product.name;
22099      
22100      return `
22101        <div class="product-analysis">
22102          <h5>${escapeHtml(productName)}</h5>
22103          
22104          ${productAnalysis.strengths ? `
22105            <div class="analysis-section">
22106              <h6 class="strengths">✅ Kelebihan</h6>
22107              <ul>
22108                ${productAnalysis.strengths.map(strength => 
22109                  `<li>${escapeHtml(strength)}</li>`
22110                ).join('')}
22111              </ul>
22112            </div>
22113          ` : ''}
22114          
22115          ${productAnalysis.weaknesses ? `
22116            <div class="analysis-section">
22117              <h6 class="weaknesses">❌ Kekurangan</h6>
22118              <ul>
22119                ${productAnalysis.weaknesses.map(weakness => 
22120                  `<li>${escapeHtml(weakness)}</li>`
22121                ).join('')}
22122              </ul>
22123            </div>
22124          ` : ''}
22125          
22126          ${productAnalysis.best_for ? `
22127            <div class="analysis-section">
22128              <h6 class="best-for">🎯 Cocok Untuk</h6>
22129              <p class="best-for-text">${escapeHtml(productAnalysis.best_for)}</p>
22130            </div>
22131          ` : ''}
22132        </div>
22133      `;
22134    }).filter(Boolean).join('');
22135  }
22136  
22137  /**
22138   * Update recommendations
22139   */
22140  function updateRecommendations(recommendations) {
22141    if (!recommendations) return;
22142    
22143    const budgetEl = document.getElementById('budgetRecommendation');
22144    const performanceEl = document.getElementById('performanceRecommendation');
22145    const balancedEl = document.getElementById('balancedRecommendation');
22146    
22147    if (budgetEl && recommendations.budget_conscious) {
22148      const product = compareList.find(p => parseInt(p.id) === parseInt(recommendations.budget_conscious));
22149      budgetEl.textContent = product ? product.name : 'Tidak tersedia';
22150    }
22151    
22152    if (performanceEl && recommendations.performance_focused) {
22153      const product = compareList.find(p => parseInt(p.id) === parseInt(recommendations.performance_focused));
22154      performanceEl.textContent = product ? product.name : 'Tidak tersedia';
22155    }
22156    
22157    if (balancedEl && recommendations.balanced) {
22158      const product = compareList.find(p => parseInt(p.id) === parseInt(recommendations.balanced));
22159      balancedEl.textContent = product ? product.name : 'Tidak tersedia';
22160    }
22161  }
22162  
22163  /**
22164   * Check if products are from different categories
22165   */
22166  async function checkProductCategoryMismatch() {
22167    if (compareList.length < 2) {
22168      return { shouldWarn: false, categories: new Set() };
22169    }
22170    
22171    // Get the root categories from each product
22172    const rootCategories = new Set();
22173    const productCategories = [];
22174    
22175    for (const product of compareList) {
22176      // Extract root category from the product's category_path if available
22177      const rootCategory = await getProductRootCategory(product);
22178      if (rootCategory) {
22179        rootCategories.add(rootCategory);
22180        productCategories.push({
22181          product: product.name,
22182          category: rootCategory
22183        });
22184      }
22185    }
22186    
22187    // If we have more than 1 different root categories, show warning
22188    const shouldWarn = rootCategories.size > 1 && compareList.length >= 2;
22189    
22190    return {
22191      shouldWarn: shouldWarn,
22192      categories: rootCategories,
22193      productCategories: productCategories
22194    };
22195  }
22196  
22197  /**
22198   * Get product root category from category_path or fallback to name detection
22199   */
22200  async function getProductRootCategory(product) {
22201    // First try to get from category_path if available
22202    if (product.category_path) {
22203      const rootCategory = product.category_path.split('/')[0];
22204      return rootCategory;
22205    }
22206    
22207    // If no category_path, try to fetch from product detail or use name detection
22208    const detectedCategory = detectProductCategory(product.name);
22209    return detectedCategory;
22210  }
22211
22212  /**
22213   * Detect product category from name (fallback method)
22214   */
22215  function detectProductCategory(productName) {
22216    const name = productName.toLowerCase();
22217    
22218    // Map keywords to actual root categories from the API structure
22219    const categoryMappings = {
22220      // Komputer & Laptop
22221      'Komputer & Laptop': ['laptop', 'notebook', 'macbook', 'thinkpad', 'gaming laptop', 'desktop', 'pc', 'mini pc', 'all-in-one', 'server'],
22222      
22223      // Printer & Scanner  
22224      'Printer & Scanner': ['printer', 'epson printer', 'canon printer', 'hp printer', 'brother printer', 'scanner', 'laser printer', 'inkjet', 'dot matrix'],
22225      
22226      // Gadget
22227      'Gadget': ['smartphone', 'iphone', 'samsung phone', 'xiaomi', 'oppo', 'vivo', 'realme', 'tablet', 'ipad', 'android tablet'],
22228      
22229      // Periferal
22230      'Periferal': ['monitor', 'lcd monitor', 'led monitor', 'gaming monitor', 'keyboard', 'mouse', 'gaming keyboard', 'mechanical keyboard', 'webcam'],
22231      
22232      // Audio
22233      'Audio': ['headset', 'headphone', 'earphone', 'gaming headset', 'speaker', 'mikrofon', 'sound card'],
22234      
22235      // Jaringan
22236      'Jaringan': ['router', 'wifi', 'access point', 'switch', 'modem', 'network'],
22237      
22238      // Penyimpanan
22239      'Penyimpanan': ['ssd', 'hdd', 'nvme', 'solid state', 'hard disk', 'flash drive', 'usb drive', 'memory card'],
22240      
22241      // Komponen PC
22242      'Komponen PC': ['ram', 'memory', 'ddr4', 'ddr5', 'processor', 'cpu', 'intel', 'amd', 'motherboard', 'graphics card', 'gpu', 'psu', 'power supply', 'casing'],
22243      
22244      // UPS & Power
22245      'UPS & Power': ['ups', 'uninterruptible', 'stabilizer', 'power supply'],
22246      
22247      // Proyektor
22248      'Proyektor': ['proyektor', 'projector', 'layar proyektor'],
22249      
22250      // Security System
22251      'Security System': ['cctv', 'security camera', 'surveillance'],
22252      
22253      // Aksesoris
22254      'Aksesoris': ['charger laptop', 'adaptor', 'converter', 'dock', 'hub', 'kabel', 'cable', 'power bank', 'tas laptop'],
22255      
22256      // Software
22257      'Software': ['software', 'lisensi', 'license', 'antivirus'],
22258      
22259      // Perlengkapan Kantor
22260      'Perlengkapan Kantor': ['fingerprint', 'finger print', 'atk', 'alat tulis']
22261    };
22262    
22263    // Check which root category matches
22264    for (const [rootCategory, keywords] of Object.entries(categoryMappings)) {
22265      for (const keyword of keywords) {
22266        if (name.includes(keyword)) {
22267          return rootCategory;
22268        }
22269      }
22270    }
22271    
22272    return null;
22273  }
22274
22275  /**
22276   * Update buying tips
22277   */
22278  function updateBuyingTips(tips) {
22279    const container = document.getElementById('buyingTips');
22280    if (!container || !Array.isArray(tips)) return;
22281    
22282    container.innerHTML = tips.map(tip => 
22283      `<li>${escapeHtml(tip)}</li>`
22284    ).join('');
22285  }
22286  
22287  // Removed share and cart functions - replaced with AI disclaimer in UI
22288  
22289  /**
22290   * Remove product from compare (public method)
22291   */
22292  async function removeProduct(productId) {
22293    try {
22294      await removeFromCompare(productId);
22295      showToast('Produk dihapus dari perbandingan', 'success');
22296      
22297      // Update all compare buttons
22298      updateAllCompareButtons();
22299      
22300    } catch (error) {
22301      showToast(error.message || 'Terjadi kesalahan', 'error');
22302    }
22303  }
22304  
22305  /**
22306   * Utility functions
22307   */
22308  function escapeHtml(text) {
22309    const div = document.createElement('div');
22310    div.textContent = text;
22311    return div.innerHTML;
22312  }
22313  
22314  function formatPrice(price) {
22315    return 'Rp ' + parseInt(price).toLocaleString('id-ID');
22316  }
22317  
22318  /**
22319   * Show confirmation modal using the existing modal.css design
22320   */
22321  function showConfirmModal(options = {}) {
22322    return new Promise((resolve) => {
22323      const {
22324        title = 'Konfirmasi',
22325        message = 'Apakah Anda yakin?',
22326        confirmText = 'Ya',
22327        cancelText = 'Tidak',
22328        type = 'warning'
22329      } = options;
22330      
22331      // Create modal HTML
22332      const modalHTML = `
22333        <div class="modal-overlay" id="confirmModal">
22334          <div class="modal-dialog">
22335            <div class="modal-header">
22336              <h3 class="modal-title">
22337                <div class="modal-icon ${type}">
22338                  ${getModalIcon(type)}
22339                </div>
22340                ${escapeHtml(title)}
22341              </h3>
22342            </div>
22343            <div class="modal-body">
22344              <p class="modal-message">${escapeHtml(message)}</p>
22345              <div class="modal-actions">
22346                <button type="button" class="modal-btn modal-btn-secondary" id="confirmCancel">
22347                  ${escapeHtml(cancelText)}
22348                </button>
22349                <button type="button" class="modal-btn modal-btn-${type === 'danger' ? 'danger' : 'primary'}" id="confirmOk">
22350                  ${escapeHtml(confirmText)}
22351                </button>
22352              </div>
22353            </div>
22354          </div>
22355        </div>
22356      `;
22357      
22358      // Add to DOM
22359      document.body.insertAdjacentHTML('beforeend', modalHTML);
22360      const modal = document.getElementById('confirmModal');
22361      const cancelBtn = document.getElementById('confirmCancel');
22362      const okBtn = document.getElementById('confirmOk');
22363      
22364      // Show modal
22365      requestAnimationFrame(() => {
22366        modal.classList.add('show');
22367      });
22368      
22369      // Handle events
22370      function cleanup() {
22371        modal.remove();
22372      }
22373      
22374      function handleCancel() {
22375        modal.classList.remove('show');
22376        setTimeout(() => {
22377          cleanup();
22378          resolve(false);
22379        }, 200);
22380      }
22381      
22382      function handleConfirm() {
22383        modal.classList.remove('show');
22384        setTimeout(() => {
22385          cleanup();
22386          resolve(true);
22387        }, 200);
22388      }
22389      
22390      cancelBtn.addEventListener('click', handleCancel);
22391      okBtn.addEventListener('click', handleConfirm);
22392      
22393      // Close on overlay click
22394      modal.addEventListener('click', (e) => {
22395        if (e.target === modal) {
22396          handleCancel();
22397        }
22398      });
22399      
22400      // Close on escape
22401      document.addEventListener('keydown', function escapeHandler(e) {
22402        if (e.key === 'Escape') {
22403          document.removeEventListener('keydown', escapeHandler);
22404          handleCancel();
22405        }
22406      });
22407      
22408      // Focus on confirm button
22409      setTimeout(() => okBtn.focus(), 100);
22410    });
22411  }
22412  
22413  /**
22414   * Get icon for modal type
22415   */
22416  function getModalIcon(type) {
22417      const icons = {
22418        danger: '🚨 ',
22419        warning: '⚠️',
22420        success: '✅',
22421        info: 'ℹ️'
22422      };
22423      return icons[type] || icons.info;
22424    }
22425
22426
22427  function showToast(message, type = 'info') {
22428    // Integration with existing toast system or create simple one
22429    
22430    // Simple alert fallback - replace with proper toast system
22431    if (type === 'error') {
22432      alert('Error: ' + message);
22433    }
22434  }
22435  
22436  // Inline compare preview functions removed - keeping it simple and clean
22437  
22438  /**
22439   * Get current product ID from page
22440   */
22441  function getCurrentProductId() {
22442    const button = document.getElementById('pd-compare-btn');
22443    return button ? parseInt(button.dataset.productId) : null;
22444  }
22445  
22446  // hideStickyCompareBar function removed - no longer using sticky bar
22447
22448  // Public API
22449  window.productCompare = {
22450    removeProduct: removeProduct,
22451    addToCompare: addToCompare,
22452    loadCompareList: loadCompareList,
22453    getCompareList: function() { return compareList; },
22454    openSidebar: openCompareSidebar,
22455    startAnalysis: startAIAnalysis,
22456    // Helper function to add from external sources with variant support
22457    addProductWithVariant: async function(productId, variantId = 0) {
22458      return await addToCompare(productId, variantId);
22459    }
22460  };
22461
22462})();
22463
22464
22465
22466</script>
vendor: 2 bytes, line 22466
22466
22467<script data-inlined="product-card-actions.js">
22468/**
22469 * Product Card Quick Actions
22470 * Bottom sheet menu for quick add to cart & compare
22471 * Integrates with existing product-compare.js and cart.js
22472 */
22473
22474(function() {
22475  'use strict';
22476  
22477  let activeSheet = null;
22478  let activeProductData = null;
22479  
22480  // DOM elements (will be created dynamically)
22481  let overlay = null;
22482  let bottomSheet = null;
22483  
22484  /**
22485   * Initialize on DOM ready
22486   */
22487  document.addEventListener('DOMContentLoaded', function() {
22488    createBottomSheet();
22489    attachEventListeners();
22490  });
22491  
22492  /**
22493   * Create bottom sheet HTML structure
22494   */
22495  function createBottomSheet() {
22496    // Create overlay
22497    overlay = document.createElement('div');
22498    overlay.className = 'pca-overlay';
22499    overlay.addEventListener('click', closeBottomSheet);
22500    
22501    // Create bottom sheet
22502    bottomSheet = document.createElement('div');
22503    bottomSheet.className = 'pca-bottom-sheet';
22504    bottomSheet.innerHTML = `
22505      <div class="pca-sheet-handle"></div>
22506      <div class="pca-sheet-product" id="pca-product-preview">
22507        <!-- Product info will be inserted here -->
22508      </div>
22509      <div class="pca-sheet-actions">
22510        <button type="button" class="pca-action-item cart" id="pca-add-cart">
22511          <svg class="pca-action-icon" viewBox="0 0 24 24" fill="currentColor">
22512            <path d="M17,18A2,2 0 0,1 19,20A2,2 0 0,1 17,22C15.89,22 15,21.1 15,20C15,18.89 15.89,18 17,18M1,2H4.27L5.21,4H20A1,1 0 0,1 21,5C21,5.17 20.95,5.34 20.88,5.5L17.3,11.97C16.96,12.58 16.3,13 15.55,13H8.1L7.2,14.63L7.17,14.75A0.25,0.25 0 0,0 7.42,15H19V17H7C5.89,17 5,16.1 5,15C5,14.65 5.09,14.32 5.24,14.04L6.6,11.59L3,4H1V2M7,18A2,2 0 0,1 9,20A2,2 0 0,1 7,22C5.89,22 5,21.1 5,20C5,18.89 5.89,18 7,18M16,11L18.78,6H6.14L8.5,11H16Z"/>
22513          </svg>
22514          <div class="pca-action-content">
22515            <div class="pca-action-label">Tambah ke Keranjang</div>
22516            <div class="pca-action-desc">Lanjut belanja produk lain</div>
22517          </div>
22518        </button>
22519        
22520        <button type="button" class="pca-action-item compare" id="pca-add-compare">
22521          <svg class="pca-action-icon" viewBox="0 0 24 24" fill="currentColor">
22522            <path d="M21,9L17,5V8H10V10H17V13M7,11L3,15L7,19V16H14V14H7V11Z"/>
22523          </svg>
22524          <div class="pca-action-content">
22525            <div class="pca-action-label">Bandingkan Produk</div>
22526            <div class="pca-action-desc">Analisis dengan AI</div>
22527          </div>
22528          <span class="pca-action-badge compare" id="pca-compare-count" style="display:none;">0</span>
22529        </button>
22530      </div>
22531    `;
22532    
22533    // Append to body
22534    document.body.appendChild(overlay);
22535    document.body.appendChild(bottomSheet);
22536    
22537    // Setup action buttons
22538    document.getElementById('pca-add-cart').addEventListener('click', handleAddToCart);
22539    document.getElementById('pca-add-compare').addEventListener('click', handleAddToCompare);
22540    
22541    // Prevent clicks inside bottom sheet from closing it
22542    bottomSheet.addEventListener('click', function(e) {
22543      e.stopPropagation();
22544    });
22545  }
22546  
22547  /**
22548   * Attach event listeners to all 3-dot buttons
22549   */
22550  function attachEventListeners() {
22551    // Use event delegation for dynamically loaded product cards
22552    document.addEventListener('click', function(e) {
22553      const btn = e.target.closest('.prod-card-actions-btn');
22554      if (btn) {
22555        e.preventDefault();
22556        e.stopPropagation();
22557        handleActionsButtonClick(btn);
22558      }
22559    });
22560    
22561    // Handle ESC key
22562    document.addEventListener('keydown', function(e) {
22563      if (e.key === 'Escape' && activeSheet) {
22564        closeBottomSheet();
22565      }
22566    });
22567  }
22568  
22569  /**
22570   * Handle 3-dot button click
22571   */
22572  function handleActionsButtonClick(button) {
22573    // Get product ID directly from button
22574    const productId = parseInt(button.dataset.productId) || 0;
22575    if (!productId) {
22576      console.error('Product ID not found on button:', button);
22577      showToast('Data produk tidak ditemukan', 'error');
22578      return;
22579    }
22580    
22581    // Find parent card for additional data
22582    const card = button.closest('.prod-card, .pa-card');
22583    if (!card) {
22584      console.error('Product card not found');
22585      showToast('Data produk tidak ditemukan', 'error');
22586      return;
22587    }
22588    
22589    // Extract product data from card
22590    const productData = extractProductData(card, productId);
22591    if (!productData) {
22592      showToast('Data produk tidak ditemukan', 'error');
22593      return;
22594    }
22595    
22596    activeProductData = productData;
22597    openBottomSheet(productData);
22598  }
22599  
22600  /**
22601   * Extract product data from card element
22602   */
22603  function extractProductData(card, productId) {
22604    try {
22605      // Product ID already passed from button
22606      if (!productId) {
22607        console.error('Product ID not provided');
22608        return null;
22609      }
22610      // Product ID already passed from button
22611      if (!productId) {
22612        console.error('Product ID not provided');
22613        return null;
22614      }
22615      
22616      const id = productId;
22617      
22618      // Get product name
22619      const nameEl = card.querySelector('.prod-name, .title');
22620      const name = nameEl ? nameEl.textContent.trim() : 'Produk';
22621      
22622      // Get product image
22623      const imgEl = card.querySelector('.thumb img, img');
22624      const image = imgEl ? imgEl.src : '/assets/img/placeholder.png';
22625      
22626      // Get product price and detect variants
22627      const priceEl = card.querySelector('.prod-price-final, .final, .prod-price');
22628      let price = 0;
22629      let hasVariants = false;
22630      let priceDisplay = '';
22631      
22632      if (priceEl) {
22633        // Get text content and clean it
22634        let priceText = priceEl.textContent.trim();
22635        priceDisplay = priceText;
22636        
22637        // Check if it's a range (indicates product has variants)
22638        if (priceText.includes('-')) {
22639          hasVariants = true;
22640          // Take the first price for calculation
22641          priceText = priceText.split('-')[0].trim();
22642        }
22643        
22644        // Remove all non-numeric characters
22645        priceText = priceText.replace(/[^0-9]/g, '');
22646        price = parseInt(priceText) || 0;
22647      }
22648      
22649      // Get stock status
22650      const stockDot = card.querySelector('.stock-dot');
22651      let stockStatus = 'unknown';
22652      let stockLabel = 'Cek stok';
22653      
22654      if (stockDot) {
22655        if (stockDot.classList.contains('ready')) {
22656          stockStatus = 'in';
22657          stockLabel = 'Stok tersedia';
22658        } else if (stockDot.classList.contains('low')) {
22659          stockStatus = 'low';
22660          stockLabel = 'Stok terbatas';
22661        } else if (stockDot.classList.contains('out')) {
22662          stockStatus = 'out';
22663          stockLabel = 'Stok habis';
22664        }
22665      }
22666      
22667      // Get category path (if available)
22668      const categoryPath = card.dataset.category || '';
22669      
22670      // Get URL
22671      let url = '#';
22672      if (card.tagName === 'A') {
22673        url = card.href;
22674      } else {
22675        const link = card.querySelector('a[href*="/product/"]');
22676        if (link) url = link.href;
22677      }
22678      
22679      return {
22680        id: id,
22681        name: name,
22682        image: image,
22683        price: price,
22684        priceDisplay: priceDisplay,
22685        hasVariants: hasVariants,
22686        stockStatus: stockStatus,
22687        stockLabel: stockLabel,
22688        categoryPath: categoryPath,
22689        url: url
22690      };
22691    } catch (error) {
22692      console.error('Error extracting product data:', error);
22693      return null;
22694    }
22695  }
22696  
22697  /**
22698   * Open bottom sheet
22699   */
22700  function openBottomSheet(productData) {
22701    if (!overlay || !bottomSheet) return;
22702    
22703    // Update product preview
22704    updateProductPreview(productData);
22705    
22706    // Update cart button based on variant status
22707    updateCartButtonForVariant(productData);
22708    
22709    // Update compare button state
22710    updateCompareButtonState(productData.id);
22711    
22712    // Show overlay and sheet
22713    overlay.classList.add('active');
22714    bottomSheet.classList.add('active');
22715    document.body.style.overflow = 'hidden';
22716    
22717    activeSheet = true;
22718    
22719    // Google Analytics
22720    if (typeof gtag === 'function') {
22721      gtag('event', 'quick_actions_open', {
22722        event_category: 'Product Card',
22723        event_label: 'Quick Actions Menu Opened',
22724        product_id: productData.id,
22725        product_name: productData.name
22726      });
22727    }
22728  }
22729  
22730  /**
22731   * Close bottom sheet
22732   */
22733  function closeBottomSheet() {
22734    if (!overlay || !bottomSheet) return;
22735    
22736    overlay.classList.remove('active');
22737    bottomSheet.classList.remove('active');
22738    document.body.style.overflow = '';
22739    
22740    activeSheet = false;
22741    activeProductData = null;
22742  }
22743  
22744  /**
22745   * Update product preview in bottom sheet
22746   */
22747  function updateProductPreview(productData) {
22748    const preview = document.getElementById('pca-product-preview');
22749    if (!preview) return;
22750    
22751    // Use full price display (shows range for variants)
22752    const priceDisplay = productData.priceDisplay || formatPrice(productData.price);
22753    
22754    preview.innerHTML = `
22755      <img src="${escapeHtml(productData.image)}" 
22756           alt="${escapeHtml(productData.name)}" 
22757           class="pca-product-image"
22758           loading="lazy">
22759      <div class="pca-product-info">
22760        <h4 class="pca-product-name">${escapeHtml(productData.name)}</h4>
22761        <p class="pca-product-price">${escapeHtml(priceDisplay)}</p>
22762        <p class="pca-product-stock ${productData.stockStatus}">${productData.stockLabel}</p>
22763      </div>
22764    `;
22765  }
22766  
22767  /**
22768   * Update cart button for variant products
22769   */
22770  function updateCartButtonForVariant(productData) {
22771    const cartBtn = document.getElementById('pca-add-cart');
22772    if (!cartBtn) return;
22773    
22774    if (productData.hasVariants) {
22775      // Product has variants - change to "View Detail"
22776      cartBtn.dataset.hasVariants = 'true';
22777      cartBtn.dataset.productUrl = productData.url;
22778      
22779      // Update icon to eye/external
22780      const iconSvg = cartBtn.querySelector('svg');
22781      if (iconSvg) {
22782        iconSvg.innerHTML = '<path d="M12,9A3,3 0 0,0 9,12A3,3 0 0,0 12,15A3,3 0 0,0 15,12A3,3 0 0,0 12,9M12,17A5,5 0 0,1 7,12A5,5 0 0,1 12,7A5,5 0 0,1 17,12A5,5 0 0,1 12,17M12,4.5C7,4.5 2.73,7.61 1,12C2.73,16.39 7,19.5 12,19.5C17,19.5 21.27,16.39 23,12C21.27,7.61 17,4.5 12,4.5Z"/>';
22783      }
22784      
22785      // Update text
22786      const label = cartBtn.querySelector('.pca-action-label');
22787      const desc = cartBtn.querySelector('.pca-action-desc');
22788      if (label) label.textContent = 'Lihat Detail Produk';
22789      if (desc) desc.textContent = 'Pilih varian terlebih dahulu';
22790      
22791    } else {
22792      // Single product - normal cart button
22793      cartBtn.dataset.hasVariants = 'false';
22794      
22795      // Restore cart icon
22796      const iconSvg = cartBtn.querySelector('svg');
22797      if (iconSvg) {
22798        iconSvg.innerHTML = '<path d="M17,18A2,2 0 0,1 19,20A2,2 0 0,1 17,22C15.89,22 15,21.1 15,20C15,18.89 15.89,18 17,18M1,2H4.27L5.21,4H20A1,1 0 0,1 21,5C21,5.17 20.95,5.34 20.88,5.5L17.3,11.97C16.96,12.58 16.3,13 15.55,13H8.1L7.2,14.63L7.17,14.75A0.25,0.25 0 0,0 7.42,15H19V17H7C5.89,17 5,16.1 5,15C5,14.65 5.09,14.32 5.24,14.04L6.6,11.59L3,4H1V2M7,18A2,2 0 0,1 9,20A2,2 0 0,1 7,22C5.89,22 5,21.1 5,20C5,18.89 5.89,18 7,18M16,11L18.78,6H6.14L8.5,11H16Z"/>';
22799      }
22800      
22801      // Restore text
22802      const label = cartBtn.querySelector('.pca-action-label');
22803      const desc = cartBtn.querySelector('.pca-action-desc');
22804      if (label) label.textContent = 'Tambah ke Keranjang';
22805      if (desc) desc.textContent = 'Lanjut belanja produk lain';
22806    }
22807  }
22808  
22809  /**
22810   * Update compare button state
22811   */
22812  function updateCompareButtonState(productId) {
22813    const compareBtn = document.getElementById('pca-add-compare');
22814    const compareBadge = document.getElementById('pca-compare-count');
22815    
22816    if (!compareBtn) return;
22817    
22818    // Check if product is in compare list (using global compareList if available)
22819    let isInCompare = false;
22820    let compareCount = 0;
22821    
22822    // Try to get compare list from product-compare.js via window.productCompare
22823    if (typeof window.productCompare !== 'undefined' && typeof window.productCompare.getCompareList === 'function') {
22824      const compareList = window.productCompare.getCompareList();
22825      compareCount = compareList.length;
22826      isInCompare = compareList.some(p => parseInt(p.id) === parseInt(productId));
22827    } else {
22828      // Fallback: check badge count
22829      const mainBadge = document.getElementById('compareCount');
22830      if (mainBadge) {
22831        compareCount = parseInt(mainBadge.textContent) || 0;
22832      }
22833    }
22834    
22835    // Update button state
22836    if (isInCompare) {
22837      compareBtn.classList.add('active');
22838      compareBtn.querySelector('.pca-action-label').textContent = 'Hapus dari Perbandingan';
22839      compareBtn.querySelector('.pca-action-desc').textContent = 'Sudah ada di daftar';
22840    } else {
22841      compareBtn.classList.remove('active');
22842      compareBtn.querySelector('.pca-action-label').textContent = 'Bandingkan Produk';
22843      compareBtn.querySelector('.pca-action-desc').textContent = 'Analisis dengan AI';
22844    }
22845    
22846    // Update badge
22847    if (compareBadge && compareCount > 0) {
22848      compareBadge.textContent = compareCount;
22849      compareBadge.style.display = 'inline-flex';
22850    } else if (compareBadge) {
22851      compareBadge.style.display = 'none';
22852    }
22853  }
22854  
22855  /**
22856   * Handle add to cart
22857   */
22858  async function handleAddToCart() {
22859    if (!activeProductData) return;
22860    
22861    const button = document.getElementById('pca-add-cart');
22862    if (!button) return;
22863    
22864    // If product has variants, redirect to detail page
22865    if (activeProductData.hasVariants) {
22866      window.location.href = activeProductData.url;
22867      return;
22868    }
22869    
22870    // Check stock
22871    if (activeProductData.stockStatus === 'out') {
22872      showToast('Produk sedang habis', 'warning');
22873      return;
22874    }
22875    
22876    // Show loading state
22877    button.classList.add('loading');
22878    button.disabled = true;
22879    
22880    // CSRF Token Helper
22881    function getCsrfToken() {
22882      return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
22883    }
22884    
22885    try {
22886      // Call cart API with proper format
22887      const response = await fetch('/api/cart/add.php', {
22888        method: 'POST',
22889        headers: {
22890          'Content-Type': 'application/json',
22891          'X-CSRF-Token': getCsrfToken()
22892        },
22893        body: JSON.stringify({
22894          csrf_token: getCsrfToken(),
22895          product_id: activeProductData.id,
22896          qty: 1, // Use 'qty' not 'quantity'
22897          variant_id: 0, // Default variant
22898          meta: {
22899            name: activeProductData.name,
22900            image_url: activeProductData.image
22901          }
22902        }),
22903        credentials: 'same-origin'
22904      });
22905      
22906      const result = await response.json();
22907      
22908      // Check both response status and result.ok
22909      if (!response.ok || !result.ok) {
22910        throw new Error(result.error || result.message || 'Gagal menambahkan ke keranjang');
22911      }
22912      
22913      // Success animation
22914      button.classList.remove('loading');
22915      button.classList.add('success');
22916      
22917      // Update cart badge
22918      updateCartBadge();
22919
22920      // PlazaAnalytics tracking
22921      if (window.PlazaAnalytics) {
22922        const _slug = (activeProductData.url || '').replace(/.*\/product\//, '');
22923        window.PlazaAnalytics.track('add_to_cart', { slug: _slug });
22924      }
22925
22926      // Show success toast
22927      showToast('Produk ditambahkan ke keranjang', 'success');
22928      
22929      // Google Analytics
22930      if (typeof gtag === 'function') {
22931        gtag('event', 'add_to_cart', {
22932          event_category: 'Ecommerce',
22933          event_label: 'Quick Add to Cart',
22934          items: [{
22935            item_id: activeProductData.id,
22936            item_name: activeProductData.name,
22937            price: activeProductData.price,
22938            quantity: 1
22939          }]
22940        });
22941      }
22942      
22943      // Close sheet after short delay
22944      setTimeout(() => {
22945        closeBottomSheet();
22946        button.classList.remove('success');
22947        button.disabled = false;
22948      }, 1000);
22949      
22950    } catch (error) {
22951      console.error('Add to cart error:', error);
22952      button.classList.remove('loading');
22953      button.disabled = false;
22954      showToast(error.message || 'Terjadi kesalahan', 'error');
22955    }
22956  }
22957  
22958  /**
22959   * Handle add to compare
22960   */
22961  async function handleAddToCompare() {
22962    if (!activeProductData) return;
22963    
22964    const button = document.getElementById('pca-add-compare');
22965    if (!button) return;
22966    
22967    // Check if already in compare
22968    const isActive = button.classList.contains('active');
22969    
22970    // Check 5-product limit before adding
22971    if (!isActive) {
22972      const mainBadge = document.getElementById('compareCount');
22973      const currentCount = mainBadge ? parseInt(mainBadge.textContent) || 0 : 0;
22974      
22975      if (currentCount >= 5) {
22976        showToast('Maksimal 5 produk untuk perbandingan', 'warning');
22977        return;
22978      }
22979    }
22980    
22981    // Show loading state
22982    button.classList.add('loading');
22983    button.disabled = true;
22984    
22985    try {
22986      if (isActive) {
22987        // Remove from compare
22988        await removeFromCompare(activeProductData.id);
22989        showToast('Produk dihapus dari perbandingan', 'success');
22990      } else {
22991        // Add to compare
22992        await addToCompare(activeProductData.id);
22993        showToast('Produk ditambahkan ke perbandingan', 'success');
22994        
22995        // Trigger fly animation
22996        triggerFlyToCompareAnimation();
22997      }
22998      
22999      // Success animation
23000      button.classList.remove('loading');
23001      button.classList.add('success');
23002      
23003      // Update compare button state
23004      updateCompareButtonState(activeProductData.id);
23005      
23006      // Close sheet after short delay
23007      setTimeout(() => {
23008        closeBottomSheet();
23009        button.classList.remove('success');
23010        button.disabled = false;
23011      }, 800);
23012      
23013    } catch (error) {
23014      console.error('Compare error:', error);
23015      button.classList.remove('loading');
23016      button.disabled = false;
23017      showToast(error.message || 'Terjadi kesalahan', 'error');
23018    }
23019  }
23020  
23021  /**
23022   * Add to compare (calls existing API)
23023   */
23024  async function addToCompare(productId) {
23025    const response = await fetch('/api/compare/add.php', {
23026      method: 'POST',
23027      headers: {
23028        'Content-Type': 'application/json',
23029      },
23030      body: JSON.stringify({
23031        product_id: productId,
23032        variant_id: 0
23033      }),
23034      credentials: 'same-origin'
23035    });
23036    
23037    const result = await response.json();
23038    
23039    if (!result.success) {
23040      throw new Error(result.error || 'Gagal menambahkan produk');
23041    }
23042    
23043    // Trigger compare list reload if function exists
23044    if (typeof window.productCompare !== 'undefined' && typeof window.productCompare.loadCompareList === 'function') {
23045      await window.productCompare.loadCompareList();
23046    }
23047    
23048    return result;
23049  }
23050  
23051  /**
23052   * Remove from compare (calls existing API)
23053   */
23054  async function removeFromCompare(productId) {
23055    const response = await fetch('/api/compare/remove.php', {
23056      method: 'POST',
23057      headers: {
23058        'Content-Type': 'application/json',
23059      },
23060      body: JSON.stringify({
23061        product_id: productId
23062      }),
23063      credentials: 'same-origin'
23064    });
23065    
23066    const result = await response.json();
23067    
23068    if (!result.success) {
23069      throw new Error(result.error || 'Gagal menghapus produk');
23070    }
23071    
23072    // Trigger compare list reload if function exists
23073    if (typeof window.productCompare !== 'undefined' && typeof window.productCompare.loadCompareList === 'function') {
23074      await window.productCompare.loadCompareList();
23075    }
23076    
23077    return result;
23078  }
23079  
23080  /**
23081   * Update cart badge
23082   */
23083  function updateCartBadge() {
23084    // Try to call global cart update function if exists
23085    if (typeof window.updateCartBadge === 'function') {
23086      window.updateCartBadge();
23087    } else {
23088      // Fallback: increment badge manually
23089      const badge = document.getElementById('cartCount');
23090      if (badge) {
23091        const currentCount = parseInt(badge.textContent) || 0;
23092        badge.textContent = currentCount + 1;
23093        badge.classList.add('active');
23094      }
23095    }
23096  }
23097  
23098  /**
23099   * Trigger fly to compare animation
23100   */
23101  function triggerFlyToCompareAnimation() {
23102    const compareBtn = document.getElementById('compareBtn');
23103    if (!compareBtn || !activeProductData) return;
23104    
23105    // Create flying element
23106    const flyingElement = document.createElement('div');
23107    flyingElement.className = 'flying-product';
23108    flyingElement.style.cssText = `
23109      position: fixed;
23110      width: 60px;
23111      height: 60px;
23112      z-index: 99999;
23113      pointer-events: none;
23114    `;
23115    
23116    flyingElement.innerHTML = `
23117      <img src="${activeProductData.image}" 
23118           style="width:100%;height:100%;object-fit:cover;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.3);">
23119    `;
23120    
23121    // Position at bottom sheet
23122    const sheetRect = bottomSheet.getBoundingClientRect();
23123    const targetRect = compareBtn.getBoundingClientRect();
23124    
23125    flyingElement.style.top = `${sheetRect.top + 20}px`;
23126    flyingElement.style.left = `${sheetRect.left + 20}px`;
23127    
23128    document.body.appendChild(flyingElement);
23129    
23130    // Animate to target
23131    requestAnimationFrame(() => {
23132      flyingElement.style.transition = 'all 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94)';
23133      flyingElement.style.top = `${targetRect.top + 10}px`;
23134      flyingElement.style.left = `${targetRect.left + 10}px`;
23135      flyingElement.style.transform = 'scale(0.3)';
23136      flyingElement.style.opacity = '0';
23137    });
23138    
23139    // Clean up
23140    setTimeout(() => {
23141      if (flyingElement.parentNode) {
23142        flyingElement.parentNode.removeChild(flyingElement);
23143      }
23144    }, 800);
23145  }
23146  
23147  /**
23148   * Format price as Rupiah
23149   */
23150  function formatPrice(price) {
23151    if (!price || price === 0) return 'Rp 0';
23152    return 'Rp ' + parseInt(price).toLocaleString('id-ID');
23153  }
23154  
23155  /**
23156   * Escape HTML
23157   */
23158  function escapeHtml(text) {
23159    const div = document.createElement('div');
23160    div.textContent = text;
23161    return div.innerHTML;
23162  }
23163  
23164  /**
23165   * Show toast notification (uses global showToast if available)
23166   */
23167  function showToast(message, type = 'info') {
23168    if (typeof window.showToast === 'function') {
23169      window.showToast({
23170        title: type === 'success' ? 'Berhasil' : type === 'error' ? 'Gagal' : 'Info',
23171        message: message,
23172        type: type,
23173        duration: 3000
23174      });
23175    } else {
23176      // Fallback: console log
23177      console.log(`[${type.toUpperCase()}] ${message}`);
23178    }
23179  }
23180  
23181  /**
23182   * Expose functions for external access
23183   */
23184  window.ProductCardActions = {
23185    openSheet: openBottomSheet,
23186    closeSheet: closeBottomSheet,
23187    updateCompareState: updateCompareButtonState
23188  };
23189  
23190})();
23191
23192</script>
23192
23193    
23193<script data-inlined="product-reviews.js">
23194/**
23195 * Product Reviews - Frontend JavaScript
23196 * Handles review display, submission, and helpful voting
23197 */
23198
23199const ProductReviews = (function() {
23200  'use strict';
23201
23202  let currentPage = 1;
23203  let currentSort = 'helpful';
23204  let productId = null;
23205  let isLoading = false;
23206
23207  /**
23208   * Public API: Initialize reviews for a product
23209   */
23210  function init(pid) {
23211    productId = pid;
23212    
23213    if (!productId) {
23214      console.warn('Product ID not provided to ProductReviews.init()');
23215      return;
23216    }
23217
23218    initializeReviewTab();
23219    loadReviews();
23220  }
23221
23222  /**
23223   * Initialize review tab functionality
23224   */
23225  function initializeReviewTab() {
23226    // Tab switching
23227    const tabButtons = document.querySelectorAll('.tab-btn');
23228    tabButtons.forEach(btn => {
23229      btn.addEventListener('click', function() {
23230        const tabName = this.dataset.tab;
23231        switchTab(tabName);
23232      });
23233    });
23234
23235    // Sort dropdown
23236    const sortSelect = document.getElementById('review-sort');
23237    if (sortSelect) {
23238      sortSelect.addEventListener('change', function() {
23239        currentSort = this.value;
23240        currentPage = 1;
23241        loadReviews();
23242      });
23243    }
23244
23245    // Load more button
23246    const loadMoreBtn = document.getElementById('load-more-reviews');
23247    if (loadMoreBtn) {
23248      loadMoreBtn.addEventListener('click', function() {
23249        currentPage++;
23250        loadReviews(true); // Append mode
23251      });
23252    }
23253
23254    // Write review button
23255    const writeReviewBtn = document.getElementById('write-review-btn');
23256    if (writeReviewBtn) {
23257      writeReviewBtn.addEventListener('click', showReviewModal);
23258    }
23259  }
23260
23261  /**
23262   * Switch between product tabs
23263   */
23264  function switchTab(tabName) {
23265    // Update button states
23266    document.querySelectorAll('.tab-btn').forEach(btn => {
23267      btn.classList.toggle('active', btn.dataset.tab === tabName);
23268    });
23269
23270    // Update content visibility
23271    document.querySelectorAll('.tab-content').forEach(content => {
23272      content.style.display = content.id === `${tabName}-tab` ? 'block' : 'none';
23273    });
23274  }
23275
23276  /**
23277   * Load reviews from API
23278   */
23279  async function loadReviews(append = false) {
23280    if (isLoading) return;
23281    isLoading = true;
23282
23283    const container = document.getElementById('reviews-list');
23284    const loadMoreBtn = document.getElementById('load-more-reviews');
23285
23286    // Check if review section exists (product might not have reviews section)
23287    if (!container) {
23288      console.log('Review section not found - product may not have reviews yet');
23289      isLoading = false;
23290      return;
23291    }
23292
23293    try {
23294      if (!append) {
23295        container.innerHTML = '<div class="review-loading">Memuat ulasan...</div>';
23296      } else {
23297        loadMoreBtn.disabled = true;
23298        loadMoreBtn.textContent = 'Memuat...';
23299      }
23300
23301      const response = await fetch(`/api/reviews/list.php?product_id=${productId}&sort=${currentSort}&page=${currentPage}&limit=10`);
23302      const data = await response.json();
23303
23304      if (!data.success) {
23305        throw new Error(data.message || 'Gagal memuat ulasan');
23306      }
23307
23308      // Update statistics (only on initial load)
23309      if (!append && data.stats) {
23310        updateReviewStats(data.stats);
23311      }
23312
23313      // Render reviews
23314      if (!append) {
23315        container.innerHTML = '';
23316      }
23317
23318      const reviews = data.reviews || [];
23319      
23320      if (reviews.length === 0 && !append) {
23321        container.innerHTML = '<div class="no-reviews">Belum ada ulasan untuk produk ini. Jadilah yang pertama!</div>';
23322      } else {
23323        reviews.forEach(review => {
23324          container.appendChild(createReviewElement(review));
23325        });
23326      }
23327
23328      // Update load more button
23329      if (loadMoreBtn) {
23330        if (data.pagination.has_more) {
23331          loadMoreBtn.style.display = 'block';
23332          loadMoreBtn.disabled = false;
23333          loadMoreBtn.textContent = 'Muat Lebih Banyak';
23334        } else {
23335          loadMoreBtn.style.display = 'none';
23336        }
23337      }
23338
23339    } catch (error) {
23340      console.error('Load reviews error:', error);
23341      container.innerHTML = '<div class="review-error">Gagal memuat ulasan. Silakan coba lagi.</div>';
23342    } finally {
23343      isLoading = false;
23344    }
23345  }
23346
23347  /**
23348   * Update review statistics display
23349   */
23350  function updateReviewStats(stats) {
23351    const avgRatingEl = document.getElementById('avg-rating');
23352    const totalReviewsEl = document.getElementById('total-reviews');
23353    const ratingBarsContainer = document.getElementById('rating-bars');
23354
23355    if (avgRatingEl) {
23356      avgRatingEl.textContent = stats.average_rating.toFixed(1);
23357    }
23358
23359    if (totalReviewsEl) {
23360      totalReviewsEl.textContent = `${stats.total_reviews} ulasan`;
23361    }
23362
23363    // Update rating breakdown bars
23364    if (ratingBarsContainer) {
23365      ratingBarsContainer.innerHTML = '';
23366      stats.rating_breakdown.forEach(item => {
23367        const bar = document.createElement('div');
23368        bar.className = 'rating-bar';
23369        bar.innerHTML = `
23370          <span class="rating-label">${item.rating}★</span>
23371          <div class="bar-container">
23372            <div class="bar-fill" style="width: ${item.percentage}%"></div>
23373          </div>
23374          <span class="rating-count">${item.count}</span>
23375        `;
23376        ratingBarsContainer.appendChild(bar);
23377      });
23378    }
23379
23380    // Update tab count
23381    const reviewTab = document.querySelector('[data-tab="reviews"]');
23382    if (reviewTab && stats.total_reviews > 0) {
23383      reviewTab.textContent = `Ulasan (${stats.total_reviews})`;
23384    }
23385  }
23386
23387  /**
23388   * Create review element
23389   */
23390  function createReviewElement(review) {
23391    const div = document.createElement('div');
23392    div.className = 'review-item';
23393    div.dataset.reviewId = review.ID;
23394
23395    // Build stars
23396    const stars = '★'.repeat(review.RATING) + '☆'.repeat(5 - review.RATING);
23397
23398    // Build images if available
23399    let imagesHtml = '';
23400    if (review.images && review.images.length > 0) {
23401      imagesHtml = '<div class="review-images">';
23402      review.images.forEach(img => {
23403        imagesHtml += `<img src="${escapeHtml(img)}" alt="Review image" class="review-img" loading="lazy">`;
23404      });
23405      imagesHtml += '</div>';
23406    }
23407
23408    // Verified purchase badge
23409    const verifiedBadge = review.IS_VERIFIED_PURCHASE === 'Y' 
23410      ? '<span class="verified-badge">✓ Pembelian Terverifikasi</span>' 
23411      : '';
23412    
23413    // Use DISPLAY_NAME if available (for anonymous reviews), otherwise use FULL_NAME
23414    const displayName = review.DISPLAY_NAME || review.FULL_NAME || 'Anonymous';
23415    const anonymousIndicator = review.IS_ANONYMOUS === 'Y' 
23416      ? '<span class="anonymous-badge" title="Nama pengguna disamark
23416an untuk privasi">🔒</span>' 
23417      : '';
23418
23419    div.innerHTML = `
23420      <div class="review-header">
23421        <div class="review-author">
23422          <strong>${escapeHtml(displayName)}</strong>
23423          ${anonymousIndicator}
23424          ${verifiedBadge}
23425        </div>
23426        <div class="review-rating">
23427          <span class="stars">${stars}</span>
23428        </div>
23429      </div>
23430      <div class="review-date">${review.created_ago}</div>
23431      <div class="review-text">${escapeHtml(review.REVIEW_TEXT).replace(/\n/g, '<br>')}</div>
23432      ${imagesHtml}
23433      <div class="review-actions">
23434        <button class="btn-helpful" data-review-id="${review.ID}">
23435          <span class="helpful-icon">👍</span>
23436          <span class="helpful-text">Membantu (${review.HELPFUL_COUNT || 0})</span>
23437        </button>
23438      </div>
23439    `;
23440
23441    // Add helpful button listener
23442    const helpfulBtn = div.querySelector('.btn-helpful');
23443    helpfulBtn.addEventListener('click', () => markAsHelpful(review.ID));
23444
23445    return div;
23446  }
23447
23448  /**
23449   * Get CSRF token from meta tag
23450   */
23451  function getCsrfToken() {
23452    const metaTag = document.querySelector('meta[name="csrf-token"]');
23453    return metaTag ? metaTag.getAttribute('content') : '';
23454  }
23455
23456  /**
23457   * Mark review as helpful
23458   */
23459  async function markAsHelpful(reviewId) {
23460    // Check if logged in
23461    const isLoggedIn = document.body.classList.contains('logged-in') || 
23462                      document.querySelector('meta[name="user-logged-in"]')?.content === 'true';
23463
23464    if (!isLoggedIn) {
23465      showModal({
23466        title: 'Login Diperlukan',
23467        message: 'Silakan login terlebih dahulu untuk memberikan vote pada ulasan ini.',
23468        icon: 'info',
23469        buttons: [
23470          { text: 'Batal', className: 'modal-btn-secondary' },
23471          { text: 'Login', className: 'modal-btn-primary', callback: () => window.location.href = '/login' }
23472        ]
23473      });
23474      return;
23475    }
23476
23477    // Get current button element
23478    const reviewItem = document.querySelector(`[data-review-id="${reviewId}"]`);
23479    const helpfulBtn = reviewItem?.querySelector('.btn-helpful');
23480    
23481    if (!helpfulBtn) return;
23482    
23483    // Disable button during request
23484    helpfulBtn.disabled = true;
23485    const originalText = helpfulBtn.querySelector('.helpful-text').textContent;
23486
23487    try {
23488      const response = await fetch('/api/reviews/helpful.php', {
23489        method: 'POST',
23490        headers: { 
23491          'Content-Type': 'application/json',
23492          'X-CSRF-Token': getCsrfToken()
23493        },
23494        credentials: 'same-origin',
23495        body: JSON.stringify({ 
23496          csrf_token: getCsrfToken(),
23497          review_id: reviewId 
23498        })
23499      });
23500
23501      const data = await response.json();
23502
23503      if (data.success) {
23504        // Update button count immediately based on action
23505        const currentCount = parseInt(originalText.match(/\d+/)?.[0] || 0);
23506        const newCount = data.action === 'added' ? currentCount + 1 : currentCount - 1;
23507        helpfulBtn.querySelector('.helpful-text').textContent = `Membantu (${newCount})`;
23508        
23509        // Show success message
23510        showToast(data.message, 'success');
23511        
23512        // Re-enable button
23513        helpfulBtn.disabled = false;
23514      } else {
23515        showToast(data.message, 'error');
23516        helpfulBtn.disabled = false;
23517      }
23518
23519    } catch (error) {
23520      console.error('Mark helpful error:', error);
23521      showToast('Gagal memberikan vote. Silakan coba lagi.', 'error');
23522      helpfulBtn.disabled = false;
23523    }
23524  }
23525
23526  /**
23527   * Show review submission modal (placeholder)
23528   */
23529  function showReviewModal() {
23530    // TODO: Implement review modal
23531    // For now, redirect to account page
23532    window.location.href = '/account/orders';
23533  }
23534
23535  /**
23536   * Show toast notification
23537   */
23538  function showToast(message, type = 'info') {
23539    const toast = document.createElement('div');
23540    toast.className = `review-toast toast-${type}`;
23541    toast.textContent = message;
23542    
23543    document.body.appendChild(toast);
23544    
23545    setTimeout(() => {
23546      toast.classList.add('show');
23547    }, 100);
23548    
23549    setTimeout(() => {
23550      toast.classList.remove('show');
23551      setTimeout(() => toast.remove(), 300);
23552    }, 3000);
23553  }
23554
23555  /**
23556   * Show modal dialog
23557   * @param {Object} options - Modal options
23558   * @param {string} options.title - Modal title
23559   * @param {string} options.message - Modal message
23560   * @param {string} options.icon - Icon type (info, success, warning, danger)
23561   * @param {Array} options.buttons - Array of button objects {text, className, callback}
23562   */
23563  function showModal(options) {
23564    const {
23565      title = 'Konfirmasi',
23566      message = '',
23567      icon = 'info',
23568      buttons = [{ text: 'OK', className: 'modal-btn-primary' }]
23569    } = options;
23570
23571    // Icon emoji mapping
23572    const iconEmojis = {
23573      info: 'ℹ️',
23574      success: '✓',
23575      warning: '⚠️',
23576      danger: '✕'
23577    };
23578
23579    // Create modal HTML
23580    const modalHTML = `
23581      <div class="modal-overlay" id="helpful-vote-modal" style="display: flex !important; align-items: center !important; justify-content: center !important;">
23582        <div class="modal-dialog">
23583          <div class="modal-header">
23584            <h3 class="modal-title">
23585              <span class="modal-icon ${icon}">${iconEmojis[icon] || iconEmojis.info}</span>
23586              ${escapeHtml(title)}
23587            </h3>
23588          </div>
23589          <div class="modal-body">
23590            <p class="modal-message">${escapeHtml(message)}</p>
23591            <div class="modal-actions">
23592              ${buttons.map((btn, index) => 
23593                `<button class="modal-btn ${btn.className || 'modal-btn-secondary'}" data-index="${index}">${escapeHtml(btn.text)}</button>`
23594              ).join('')}
23595            </div>
23596          </div>
23597        </div>
23598      </div>
23599    `;
23600
23601    // Append to body
23602    const tempDiv = document.createElement('div');
23603    tempDiv.innerHTML = modalHTML;
23604    const modal = tempDiv.firstElementChild;
23605    document.body.appendChild(modal);
23606
23607    // Add event listeners to buttons
23608    buttons.forEach((btn, index) => {
23609      const btnElement = modal.querySelector(`[data-index="${index}"]`);
23610      btnElement.addEventListener('click', () => {
23611        closeModal(modal);
23612        if (btn.callback && typeof btn.callback === 'function') {
23613          btn.callback();
23614        }
23615      });
23616    });
23617
23618    // Close on overlay click
23619    modal.addEventListener('click', (e) => {
23620      if (e.target === modal) {
23621        closeModal(modal);
23622      }
23623    });
23624
23625    // Show modal with animation
23626    setTimeout(() => modal.classList.add('show'), 10);
23627  }
23628
23629  /**
23630   * Close modal dialog
23631   */
23632  function closeModal(modal) {
23633    modal.classList.remove('show');
23634    setTimeout(() => modal.remove(), 200);
23635  }
23636
23637  /**
23638   * Escape HTML for XSS protection
23639   */
23640  function escapeHtml(text) {
23641    const div = document.createElement('div');
23642    div.textContent = text;
23643    return div.innerHTML;
23644  }
23645
23646  /**
23647   * Get product ID from URL
23648   */
23649  function getProductIdFromUrl() {
23650    const match = window.location.pathname.match(/\/product\/(\d+)/);
23651    return match ? match[1] : null;
23652  }
23653
23654  // Public API
23655  return {
23656    init: init
23657  };
23658
23659})();
23660
23661</script>
23661
23662    
23662<script data-inlined="review-submission.js">
23663/**
23664 * Review Submission Modal & Handler
23665 * Handles review form display, validation, and submission
23666 */
23667
23668(function() {
23669    'use strict';
23670
23671    let currentProductId = null;
23672    let currentOrderId = null;
23673    let currentSoRefId = null;
23674    let selectedRating = 0;
23675    let uploadedImages = [];
23676
23677    // Get CSRF token from meta tag
23678    function getCsrfToken() {
23679        const metaTag = document.querySelector('meta[name="csrf-token"]');
23680        return metaTag ? metaTag.getAttribute('content') : '';
23681    }
23682
23683    // Initialize
23684    function init() {
23685        createReviewModal();
23686        attachEventListeners();
23687        loadReviewableOrders();
23688    }
23689
23690    // Create review modal HTML
23691    function createReviewModal() {
23692        const modalHTML = `
23693            <div class="modal-overlay" id="review-modal-overlay" style="display:none;">
23694                <div class="modal-content review-modal">
23695                    <div class="modal-header">
23696                        <h3 class="modal-title">Tulis Ulasan</h3>
23697                        <button type="button" class="modal-close" id="review-modal-close" aria-label="Tutup">
23698                            <svg width="20" height="20" viewBox="0 0 24 24">
23699                                <path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
23700                            </svg>
23701                        </button>
23702                    </div>
23703                    
23704                    <div class="modal-body">
23705                        <form id="review-form" class="review-form">
23706                            <!-- Product Info -->
23707                            <div class="review-product-info" id="review-product-info">
23708                                <img id="review-product-image" src="" alt="" class="review-product-thumb">
23709                                <div class="review-product-details">
23710                                    <h4 id="review-product-name"></h4>
23711                                    <p id="review-product-sku"></p>
23712                                </div>
23713                            </div>
23714
23715                            <!-- Rating -->
23716                            <div class="form-group">
23717                                <label class="form-label">Rating <span style="color:#e2a642;">*</span></label>
23718                                <div class="star-rating-input" id="star-rating-input">
23719                                    <button type="button" class="star-btn" data-rating="1">★</button>
23720                                    <button type="button" class="star-btn" data-rating="2">★</button>
23721                                    <button type="button" class="star-btn" data-rating="3">★</button>
23722                                    <button type="button" class="star-btn" data-rating="4">★</button>
23723                                    <button type="button" class="star-btn" data-rating="5">★</button>
23724                                </div>
23725                                <p class="rating-label" id="rating-label">Pilih rating Anda</p>
23726                            </div>
23727
23728                            <!-- Review Text -->
23729                            <div class="form-group">
23730                                <label class="form-label" for="review-text">Ulasan Anda <span style="color:#e2a642;">*</span></label>
23731                                <textarea 
23732                                    id="review-text" 
23733                                    name="review_text"
23734                                    class="form-control" 
23735                                    rows="6" 
23736                                    placeholder="Ceritakan pengalaman Anda dengan produk ini..."
23737                                    minlength="20"
23738                                    maxlength="5000"
23739                                    required></textarea>
23740                                <p class="form-hint">
23741                                    <span id="review-char-count">0</span>/5000 karakter
23742                                    <span style="color:#059669;font-weight:500;">
23743                                        💰 +1,000 point untuk ulasan ≥ 100 karakter
23744                                    </span>
23745                                </p>
23746                            </div>
23747
23748                            <!-- Image Upload -->
23749                            <div class="form-group">
23750                                <label class="form-label">Upload Foto (Opsional)</label>
23751                                <div class="image-upload-container">
23752                                    <input type="file" 
23753                                           id="review-images" 
23754                                           name="images[]" 
23755                                           accept="image/jpeg,image/jpg,image/png,image/webp"
23756                                           multiple
23757                                           style="display:none;">
23758                                    <button type="button" class="btn-upload-trigger" id="upload-trigger">
23759                                        <svg width="24" height="24" viewBox="0 0 24 24">
23760                                            <path fill="currentColor" d="M19 7v2.99s-1.99.01-2 0V7h-3s.01-1.99 0-2h3V2h2v3h3v2h-3zm-3 4V8h-3V5H5c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2v-8h-3zM5 19l3-4 2 3 3-4 4 5H5z"/>
23761                                        </svg>
23762                                        <span>Pilih Foto</span>
23763                                    </button>
23764                                    <p class="form-hint">
23765                                        Max 5 foto, format JPG/PNG/WebP, max 2MB per foto
23766                                        <span style="color:#059669;font-weight:500;">
23767                                            💰 +2,000 point untuk ulasan dengan foto
23768                                        </span>
23769                                    </p>
23770                                </div>
23771                                <div class="image-preview-grid" id="image-preview-grid"></div>
23772                            </div>
23773
23774                            <!-- Point Reward Info -->
23775                            <div class="point-reward-info">
23776                                <div class="pri-header">
23777                                    <span>🎁</span>
23778                                    <span>Dapatkan Point Reward</span>
23779                                </div>
23780                                <ul class="pri-list">
23781                                    <li>✅ Ulasan dasar: <strong>5,000 point</strong></li>
23782                                    <li id="pri-long-text" style="opacity:0.5;">📝 Ulasan panjang (≥100 kata): <strong>+1,000 point</strong></li>
23783                                    <li id="pri-images" style="opacity:0.5;">📸 Dengan foto: <strong>+2,000 point</strong></li>
23784                                </ul>
23785                                <div class="pri-total">
23786                                    Total Reward: <strong id="pri-total-points">5,000 point</strong>
23787                                </div>
23788                            </div>
23789
23790                            <!-- Privacy Option -->
23791                            <div class="form-group" style="margin-top:20px;padding:15px;background:#f8fafc;border-radius:8px;">
23792                                <label class="checkbox-label" style="display:flex;align-items:center;cursor:pointer;margin:0;">
23793                                    <input type="checkbox" 
23794                                           id="review-anonymous" 
23795                                           name="is_anonymous"
23796                                           style="margin-right:8px;width:18px;height:18px;cursor:pointer;">
23797                                    <span style="font-size:14px;">
23798                                        <strong>🔒 Sembunyikan nama saya</strong>
23799                                        <span style="color:#64748b;display:block;font-size:13px;margin-top:4px;" id="review-anonymous-preview">
23800                                            Nama Anda akan ditampilkan de
23800ngan disamarkan untuk menjaga privasi
23801                                        </span>
23802                                    </span>
23803                                </label>
23804                            </div>
23805
23806                            <!-- Error Alert -->
23807                            <div class="form-error" id="review-form-error" style="display:none;"></div>
23808
23809                            <!-- Submit Buttons -->
23810                            <div class="modal-footer">
23811                                <button type="button" class="btn btn-secondary" id="review-cancel">Batal</button>
23812                                <button type="submit" class="btn btn-primary" id="review-submit">
23813                                    Kirim Ulasan
23814                                </button>
23815                            </div>
23816                        </form>
23817                    </div>
23818                </div>
23819            </div>
23820        `;
23821
23822        document.body.insertAdjacentHTML('beforeend', modalHTML);
23823    }
23824
23825    // Attach event listeners
23826    function attachEventListeners() {
23827        // Modal triggers - event delegation
23828        document.addEventListener('click', function(e) {
23829            const btn = e.target.closest('.btn-write-review');
23830            if (btn) {
23831                e.preventDefault();
23832                openReviewModal(
23833                    btn.dataset.productId,
23834                    btn.dataset.orderId,
23835                    btn.dataset.productName,
23836                    btn.dataset.productImage,
23837                    btn.dataset.productSku,
23838                    btn.dataset.soRefId || null
23839                );
23840            }
23841        });
23842
23843        // Close modal
23844        setTimeout(() => {
23845            const closeBtn = document.getElementById('review-modal-close');
23846            const cancelBtn = document.getElementById('review-cancel');
23847            const overlay = document.getElementById('review-modal-overlay');
23848
23849            if (closeBtn) {
23850                closeBtn.addEventListener('click', closeReviewModal);
23851            }
23852            if (cancelBtn) {
23853                cancelBtn.addEventListener('click', closeReviewModal);
23854            }
23855            if (overlay) {
23856                overlay.addEventListener('click', function(e) {
23857                    if (e.target === overlay) closeReviewModal();
23858                });
23859            }
23860        }, 100);
23861
23862        // Star rating
23863        setTimeout(() => {
23864            const starButtons = document.querySelectorAll('.star-btn');
23865            starButtons.forEach(btn => {
23866                btn.addEventListener('click', function(e) {
23867                    e.preventDefault();
23868                    const rating = parseInt(this.dataset.rating);
23869                    setRating(rating);
23870                });
23871            });
23872        }, 100);
23873
23874        // Character count
23875        setTimeout(() => {
23876            const textarea = document.getElementById('review-text');
23877            if (textarea) {
23878                textarea.addEventListener('input', function() {
23879                    updateCharCount();
23880                    calculatePotentialPoints();
23881                });
23882            }
23883        }, 100);
23884
23885        // Image upload
23886        setTimeout(() => {
23887            const uploadTrigger = document.getElementById('upload-trigger');
23888            const fileInput = document.getElementById('review-images');
23889            
23890            if (uploadTrigger && fileInput) {
23891                uploadTrigger.addEventListener('click', () => fileInput.click());
23892                fileInput.addEventListener('change', handleImageSelection);
23893            }
23894        }, 100);
23895
23896        // Form submit
23897        setTimeout(() => {
23898            const form = document.getElementById('review-form');
23899            if (form) {
23900                form.addEventListener('submit', handleSubmit);
23901            }
23902        }, 100);
23903    }
23904
23905    // Open review modal
23906    function openReviewModal(productId, orderId, productName, productImage, productSku, soRefId = null) {
23907        const overlay = document.getElementById('review-modal-overlay');
23908        if (!overlay) {
23909            console.error('Modal overlay not found!');
23910            return;
23911        }
23912        
23913        currentProductId = productId;
23914        currentOrderId = orderId;
23915        currentSoRefId = soRefId;
23916        selectedRating = 0;
23917        uploadedImages = [];
23918
23919        // Set product info
23920        const nameEl = document.getElementById('review-product-name');
23921        const imageEl = document.getElementById('review-product-image');
23922        const skuEl = document.getElementById('review-product-sku');
23923        
23924        if (nameEl) nameEl.textContent = productName;
23925        if (imageEl) imageEl.src = productImage || '/assets/img/no-image.png';
23926        if (skuEl) skuEl.textContent = 'SKU: ' + productSku;
23927
23928        // Update privacy preview with actual user name
23929        updatePrivacyPreview();
23930
23931        // Reset form
23932        const form = document.getElementById('review-form');
23933        const previewGrid = document.getElementById('image-preview-grid');
23934        const errorEl = document.getElementById('review-form-error');
23935        
23936        if (form) form.reset();
23937        if (previewGrid) previewGrid.innerHTML = '';
23938        if (errorEl) errorEl.style.display = 'none';
23939        
23940        setRating(0);
23941        updateCharCount();
23942        calculatePotentialPoints();
23943
23944        // Show modal
23945        overlay.style.display = 'flex';
23946        overlay.classList.add('show');
23947        document.body.style.overflow = 'hidden';
23948    }
23949
23950    // Close review modal
23951    function closeReviewModal() {
23952        const overlay = document.getElementById('review-modal-overlay');
23953        if (overlay) {
23954            overlay.classList.remove('show');
23955            setTimeout(() => {
23956                overlay.style.display = 'none';
23957            }, 200); // Wait for fade animation
23958        }
23959        document.body.style.overflow = '';
23960    }
23961
23962    // Set star rating
23963    function setRating(rating) {
23964        selectedRating = rating;
23965        const stars = document.querySelectorAll('.star-btn');
23966        const labels = ['', 'Sangat Buruk', 'Buruk', 'Cukup', 'Bagus', 'Sangat Bagus'];
23967        
23968        stars.forEach((star, index) => {
23969            if (index < rating) {
23970                star.classList.add('active');
23971            } else {
23972                star.classList.remove('active');
23973            }
23974        });
23975
23976        document.getElementById('rating-label').textContent = labels[rating] || 'Pilih rating Anda';
23977    }
23978
23979    // Update character count
23980    function updateCharCount() {
23981        const textarea = document.getElementById('review-text');
23982        const count = textarea.value.length;
23983        document.getElementById('review-char-count').textContent = count;
23984    }
23985
23986    // Calculate potential points
23987    function calculatePotentialPoints() {
23988        const textarea = document.getElementById('review-text');
23989        if (!textarea) return;
23990        
23991        const charCount = textarea.value.trim().length;
23992        const hasImages = uploadedImages.length > 0;
23993
23994        let totalPoints = 5000;
23995        let longTextActive = charCount >= 100;
23996        let imagesActive = hasImages;
23997
23998        if (longTextActive) totalPoints += 1000;
23999        if (imagesActive) totalPoints += 2000;
24000
24001        // Update UI
24002        const longTextEl = document.getElementById('pri-long-text');
24003        const imagesEl = document.getElementById('pri-images');
24004        const totalEl = document.getElementById('pri-total-points');
24005        
24006        if (longTextEl) longTextEl.style.opacity = longTextActive ? '1' : '0.5';
24007        if (imagesEl) imagesEl.style.opacity = imagesActive ? '1' : '0.5';
24008        if (totalEl) totalEl.textContent = totalPoints.toLocaleString('id-ID') + ' point';
24009    }
24010
24011    // Handle image selection
24012    function handleImageSelection(e) {
24013        const files = Array.from(e.target.files);
24014        
24015        if (files.length > 5) {
24016            showError('Maksimal 5 foto yang dapat diupload');
24017            e.target.value = ''; // Reset input
24018            return;
24019        }
24020
24021        uploadedImages = [];
24022        const previewGrid = document.getElementById('image-preview-grid');
24023        previewGrid.innerHTML = '';
24024
24025        files.forEach((file, index) => {
24026            // Validate file type
24027            if (!file.type.match(/image\/(jpeg|jpg|png|webp)/)) {
24028                showError('Format file harus JPG, PNG, atau WebP');
24029                return;
24030            }
24031
24032            // Validate file size (2MB)
24033            if (file.size > 2 * 1024 * 1024) {
24034                showError('Ukuran file maksimal 2MB');
24035                return;
24036            }
24037
24038            uploadedImages.push(file);
24039
24040            // Create preview
24041            const reader = new FileReader();
24042            reader.onload = function(e) {
24043                const previewHTML = `
24044                    <div class="image-preview-item">
24045                        <img src="${e.target.result}" alt="Preview ${index + 1}">
24046                        <button type="button" class="remove-image" data-index="${index}" aria-label="Hapus">
24047                            <svg width="16" height="16" viewBox="0 0 24 24">
24048                                <path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
24049                            </svg>
24050                        </button>
24051                    </div>
24052                `;
24053                previewGrid.insertAdjacentHTML('beforeend', previewHTML);
24054            };
24055            reader.readAsDataURL(file);
24056        });
24057
24058        calculatePotentialPoints();
24059
24060        // Attach remove handlers
24061        setTimeout(() => {
24062            document.querySelectorAll('.remove-image').forEach(btn => {
24063                btn.addEventListener('click', function() {
24064                    const index = parseInt(this.dataset.index);
24065                    removeImage(index);
24066                });
24067            });
24068        }, 100);
24069    }
24070
24071    // Remove image
24072    function removeImage(index) {
24073        uploadedImages.splice(index, 1);
24074        
24075        const previewGrid = document.getElementById('image-preview-grid');
24076        previewGrid.innerHTML = '';
24077
24078        uploadedImages.forEach((file, idx) => {
24079            const reader = new FileReader();
24080            reader.onload = function(e) {
24081                const previewHTML = `
24082                    <div class="image-preview-item">
24083                        <img src="${e.target.result}" alt="Preview ${idx + 1}">
24084                        <button type="button" class="remove-image" data-index="${idx}" aria-label="Hapus">
24085                            <svg width="16" height="16" viewBox="0 0 24 24">
24086                                <path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
24087                            </svg>
24088                        </button>
24089                    </div>
24090                `;
24091                previewGrid.insertAdjacentHTML('beforeend', previewHTML);
24092            };
24093            reader.readAsDataURL(file);
24094        });
24095
24096        calculatePotentialPoints();
24097
24098        setTimeout(() => {
24099            document.querySelectorAll('.remove-image').forEach(btn => {
24100                btn.addEventListener('click', function() {
24101                    const idx = parseInt(this.dataset.index);
24102                    removeImage(idx);
24103                });
24104            });
24105        }, 100);
24106    }
24107
24108    // Handle form submission
24109    async function handleSubmit(e) {
24110        e.preventDefault();
24111
24112        // Validate
24113        if (selectedRating === 0) {
24114            showError('Silakan pilih rating terlebih dahulu');
24115            return;
24116        }
24117
24118        const reviewText = document.getElementById('review-text').value.trim();
24119        if (reviewText.length < 20) {
24120            showError('Ulasan minimal 20 karakter');
24121            return;
24122        }
24123
24124        const submitBtn = document.getElementById('review-submit');
24125        submitBtn.disabled = true;
24126        
24127        try {
24128            // Step 1: Upload images if any
24129            let imageUrls = [];
24130            if (uploadedImages.length > 0) {
24131                submitBtn.textContent = 'Mengupload foto...';
24132                imageUrls = await uploadImages();
24133                if (!imageUrls) {
24134                    // Upload failed
24135                    submitBtn.disabled = false;
24136                    submitBtn.textContent = 'Kirim Ulasan';
24137                    return;
24138                }
24139            }
24140
24141            // Step 2: Submit review with image URLs
24142            submitBtn.textContent = 'Mengirim ulasan...';
24143            
24144            const payload = {
24145                csrf_token: getCsrfToken(),
24146                product_id: currentProductId,
24147                order_id: currentOrderId || 0,
24148                so_ref_id: currentSoRefId || undefined,
24149                rating: selectedRating,
24150                review_text: reviewText,
24151                is_anonymous: document.getElementById('review-anonymous').checked,
24152                images: imageUrls
24153            };
24154
24155            const response = await fetch('/api/reviews/submit.php', {
24156                method: 'POST',
24157                headers: {
24158                    'Content-Type': 'application/json',
24159                    'X-CSRF-Token': getCsrfToken()
24160                },
24161                body: JSON.stringify(payload)
24162            });
24163
24164            const result = await response.json();
24165
24166            if (result.success) {
24167                showSuccess(result.message || 'Ulasan berhasil dikirim!');
24168                closeReviewModal();
24169                
24170                // Refresh reviewable orders if on account page
24171                if (typeof loadReviewableOrders === 'function') {
24172                    setTimeout(loadReviewableOrders, 500);
24173                }
24174            } else {
24175                showError(result.message || 'Gagal mengirim ulasan');
24176            }
24177        } catch (error) {
24178            console.error('Submit error:', error);
24179            showError('Terjadi kesalahan saat mengirim ulasan');
24180        } finally {
24181            submitBtn.disabled = false;
24182            submitBtn.textContent = 'Kirim Ulasan';
24183        }
24184    }
24185
24186    /**
24187     * Upload images to server
24188     * @returns {Promise<Array|null>} Array of image URLs or null if failed
24189     */
24190    async function uploadImages() {
24191        if (uploadedImages.length === 0) {
24192            return [];
24193        }
24194
24195        const formData = new FormData();
24196        uploadedImages.forEach(file => {
24197            formData.append('images[]', file);
24198        });
24199
24200        try {
24201            const response = await fetch('/api/reviews/upload_images.php', {
24202                method: 'POST',
24203                headers: {
24204                    'X-CSRF-Token': getCsrfToken()
24205                },
24206                body: formData
24207            });
24208
24209            // Check if response is JSON
24210            const contentType = response.headers.get('content-type');
24211            if (!contentType || !contentType.includes('application/json')) {
24212                const text = await response.text();
24213                console.error('Non-JSON response:', text);
24214                showError('Server error: Response bukan JSON. Cek browser console untuk detail.');
24215                return null;
24216            }
24217
24218            const result = await response.json();
24219
24220            if (result.success && result.images) {
24221                // Extract URLs from uploaded images
24222                return result.images.map(img => img.url);
24223            } else {
24224                showError(result.message || 'Gagal mengupload foto');
24225                return null;
24226            }
24227        } catch (error) {
24228            console.error('Upload images error:', error);
24229            showError('Terjadi kesalahan saat mengupload foto. Cek console untuk detail.');
24230            return null;
24231        }
24232    }
24233
24234    // Load reviewable orders (for account page)
24235    async function loadReviewableOrders() {
24236        // Only run if on account/profile page
24237        const container = document.getElementById('reviewable-orders-list');
24238        const loading = document.getElementById('reviewable-orders-loading');
24239        const empty = document.getElementById('reviewable-orders-empty');
24240        
24241        if (!container) {
24242            return;
24243        }
24244
24245        // Show loading
24246        if (loading) loading.style.display = 'block';
24247        if (empty) empty.style.display = 'none';
24248        container.innerHTML = '';
24249
24250        try {
24251            const response = await fetch('/api/reviews/reviewable.php');
24252            const result = await response.json();
24253
24254            if (loading) loading.style.display = 'none';
24255
24256            if (result.success && result.data.length > 0) {
24257                renderReviewableOrders(result.data);
24258                if (empty) empty.style.display = 'none';
24259            } else {
24260                if (empty) empty.style.display = 'block';
24261            }
24262        } catch (error) {
24263            console.error('Load reviewable orders error:', error);
24264            if (loading) loading.style.display = 'none';
24265            if (empty) {
24266                empty.innerHTML = '<p>Gagal memuat data. Silakan refresh halaman.</p>';
24267                empty.style.display = 'block';
24268            }
24269        }
24270    }
24271
24272    // Render reviewable orders
24273    function renderReviewableOrders(orders) {
24274        const container = document.getElementById('reviewable-orders-list');
24275        if (!container) return;
24276
24277        let html = '';
24278        
24279        orders.forEach(order => {
24280            html += `
24281                <div class="reviewable-order-item">
24282                    <img src="${escapeHtml(order.product_image || '/assets/img/no-image.png')}" alt="${escapeHtml(order.product_name)}" class="roi-image">
24283                    <div class="roi-info">
24284                        <h4>${escapeHtml(order.product_name)}</h4>
24285                        <p class="roi-meta">Order: ${escapeHtml(order.order_number)}</p>
24286                        <p class="roi-meta">Dibeli: ${escapeHtml(order.order_date)}</p>
24287                    </div>
24288                    <button type="button" 
24289                            class="btn btn-primary btn-write-review"
24290                            data-product-id="${order.product_id}"
24291                            data-order-id="${order.order_id || ''}"
24292                            data-so-ref-id="${escapeHtml(order.so_ref_id || '')}"
24293                            data-product-name="${escapeHtml(order.product_name)}"
24294                            data-product-image="${escapeHtml(order.product_image || '')}"
24295                            data-product-sku="${escapeHtml(order.product_sku || '')}">
24296                        ✍️ Tulis Ulasan
24297                    </button>
24298                </div>
24299            `;
24300        });
24301        
24302        container.innerHTML = html;
24303    }
24304
24305    // Escape HTML for XSS protection
24306    function escapeHtml(text) {
24307        const div = document.createElement('div');
24308        div.textContent = text;
24309        return div.innerHTML;
24310    }
24311
24312    // Show error
24313    function showError(message) {
24314        const errorEl = document.getElementById('review-form-error');
24315        errorEl.textContent = message;
24316        errorEl.style.display = 'block';
24317        
24318        setTimeout(() => {
24319            errorEl.style.display = 'none';
24320        }, 5000);
24321    }
24322
24323    // Show success
24324    function showSuccess(message) {
24325        // Create toast notification
24326        const toast = document.createElement('div');
24327        toast.className = 'toast-notification success';
24328        toast.textContent = message;
24329        toast.style.cssText = 'position:fixed;top:20px;right:20px;background:#059669;color:white;padding:16px 24px;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.15);z-index:10000;font-size:14px;font-weight:500;';
24330        
24331        document.body.appendChild(toast);
24332        
24333        setTimeout(() => {
24334            toast.style.opacity = '0';
24335            toast.style.transition = 'opacity 0.3s';
24336            setTimeout(() => toast.remove(), 300);
24337        }, 3000);
24338    }
24339
24340    // Mask user name for privacy preview (like PHP maskReviewerName)
24341    function maskUserName(fullName) {
24342        if (!fullName || fullName.trim() === '') {
24343            return 'Anonymous';
24344        }
24345        
24346        const words = fullName.trim().split(/\s+/);
24347        const maskedWords = words.map(word => {
24348            const len = word.length;
24349            if (len <= 1) return word;
24350            return word.charAt(0) + '*'.repeat(len - 1);
24351        });
24352        
24353        return maskedWords.join(' ');
24354    }
24355
24356    // Update privacy preview text with user's actual masked name
24357    function updatePrivacyPreview() {
24358        const previewEl = document.getElementById('review-anonymous-preview');
24359        if (!previewEl) return;
24360        
24361        const userName = window.userFullName || '';
24362        if (userName) {
24363            const maskedName = maskUserName(userName);
24364            previewEl.textContent = `Nama Anda akan ditampilkan sebagai "${maskedName}" untuk menjaga privasi`;
24365        } else {
24366            previewEl.textContent = 'Nama Anda akan ditampilkan de
24366ngan disamarkan untuk menjaga privasi';
24367        }
24368    }
24369
24370    // Initialize when DOM ready
24371    if (document.readyState === 'loading') {
24372        document.addEventListener('DOMContentLoaded', init);
24373    } else {
24374        init();
24375    }
24376
24377    // Export openReviewModal to global scope for use in other scripts
24378    window.openReviewModal = openReviewModal;
24379
24380})();
24381
24382</script>
24382
24383    <style>
24384/* =========================================================
24385   ⚡ FIXED FOOTER – #c9832a ELEGANT MODE
24386========================================================= */
24387
24388.sbo-top-fixed-footer{
24389      position: fixed;
24390    left: 0;
24391    right: 0;
24392    bottom: 0;
24393    z-index: 999;
24394    display: flex;
24395    align-items: center;
24396    justify-content: space-around;
24397    gap: 8px;
24398    padding: 11px 12px calc(11px + env(safe-area-inset-bottom));
24399    overflow: hidden;
24400    background: radial-gradient(circle at 50% 0%, #000000, rgb(0 0 0 / 92%) 58%, rgb(0 0 0 / 98%)), linear-gradient(180deg, rgba(25, 0, 0, 0.92), rgb(0 0 0 / 98%));
24401    backdrop-filter: blur(12px);
24402    border-top: 1px solid rgb(163,100,34);
24403    border-radius: 18px 18px 0 0;
24404    box-shadow: 0 -12px 30px #2b1b0e, inset 0 1px 0 rgba(255, 255, 255, .05);
24405    font-family: "Orbitron", "Poppins", system-ui, sans-serif;
24406}
24407
24408/* scanline */
24409.sbo-top-fixed-footer::before{
24410  content:"";
24411  position:absolute;
24412  inset:0;
24413  pointer-events:none;
24414  background:
24415    repeating-linear-gradient(
24416      0deg,
24417      rgba(255,255,255,.015),
24418      rgba(255,255,255,.015) 1px,
24419      transparent 1px,
24420      transparent 6px
24421    );
24422  opacity:.85;
24423  mix-blend-mode:overlay;
24424}
24425
24426/* shine sweep */
24427.sbo-top-fixed-footer::after{
24428  content:"";
24429  position:absolute;
24430  top:0;
24431  left:-160%;
24432  width:60%;
24433  height:100%;
24434  background: linear-gradient(120deg, transparent, rgba(226,166,66,0.25), transparent);
24435  transform: skewX(-25deg);
24436  animation: footerFinalShine 3.8s infinite;
24437  mix-blend-mode: screen;
24438  pointer-events:none;
24439}
24440
24441@keyframes footerFinalShine{
24442  0%{left:-160%;}
24443  68%{left:160%;}
24444  100%{left:160%;}
24445}
24446
24447/* =========================================================
24448   ITEM
24449========================================================= */
24450
24451.sbo-top-fixed-footer a{
24452  flex:1;
24453  max-width:110px;
24454
24455  display:flex;
24456  flex-direction:column;
24457  align-items:center;
24458  justify-content:center;
24459  gap:6px;
24460
24461  padding: 8px 6px;
24462
24463  text-decoration:none;
24464  text-align:center;
24465
24466  color: rgba(201,131,42,1);
24467  font-size: 12px;
24468  font-weight: 800;
24469  letter-spacing: .25px;
24470
24471  border-radius: 14px;
24472  position:relative;
24473  z-index:1;
24474
24475  transition: all .18s ease;
24476}
24477
24478.sbo-top-fixed-footer a:hover{
24479  color:#ffffff;
24480  background: rgba(226,166,66,0.08);
24481  filter: drop-shadow(0 0 12px rgba(226,166,66,0.15));
24482  transform: translateY(-1px);
24483}
24484
24485.sbo-top-fixed-footer a:active{
24486  transform: translateY(0) scale(.99);
24487}
24488
24489/* =========================================================
24490   ICON
24491========================================================= */
24492
24493.sbo-top-fixed-footer img{
24494  width:26px;
24495  height:26px;
24496  transition: .18s ease;
24497  filter: drop-shadow(0 0 8px rgba(226,166,66,0.35));
24498}
24499
24500.sbo-top-fixed-footer a:hover img{
24501  transform: scale(1.08);
24502  filter: drop-shadow(0 0 16px rgba(226,166,66,0.6));
24503}
24504
24505/* =========================================================
24506   HUD CORNERS
24507========================================================= */
24508
24509.sbo-top-fixed-footer .hud-corner{
24510  position:absolute;
24511  width:18px;height:18px;
24512  pointer-events:none;
24513  border: 1px solid rgba(226,166,66,0.5);
24514  filter: drop-shadow(0 0 10px rgba(226,166,66,0.2));
24515  opacity:.85;
24516}
24517
24518.sbo-top-fixed-footer .hud-tl{top:8px;left:10px;border-right:none;border-bottom:none;border-radius:10px 0 0 0;}
24519.sbo-top-fixed-footer .hud-tr{top:8px;right:10px;border-left:none;border-bottom:none;border-radius:0 10px 0 0;}
24520.sbo-top-fixed-footer .hud-bl{bottom:8px;left:10px;border-right:none;border-top:none;border-radius:0 0 0 10px;}
24521.sbo-top-fixed-footer .hud-br{bottom:8px;right:10px;border-left:none;border-top:none;border-radius:0 0 10px 0;}
24522
24523/* =========================================================
24524   CENTER CTA
24525========================================================= */
24526
24527.sbo-top-fixed-footer .tada{
24528      color: #ffffff;
24529    background: radial-gradient(circle at 50% 0%, #e2a642, rgba(0, 0, 0, 0) 70%), rgba(226,166,66,0.08);
24530    border: 1px solid rgba(226,166,66,0.35);
24531    box-shadow: 0 0 18px #e2a642, inset 0 0 16px rgba(226,166,66,0.15);
24532}
24533
24534.sbo-top-fixed-footer .tada img{
24535  width:30px;
24536  height:30px;
24537  filter: drop-shadow(0 0 16px rgba(226,166,66,0.65));
24538}
24539
24540/* pulse */
24541.sbo-top-fixed-footer .tada::before{
24542  content:"";
24543  position:absolute;
24544  inset:-12px;
24545  border-radius:999px;
24546  background:
24547    radial-gradient(circle, #000000, transparent 70%),
24548    radial-gradient(circle, rgba(226,166,66,0.08), transparent 72%);
24549  filter: blur(12px);
24550  opacity:.55;
24551  animation: corePulse 2.9s ease-in-out infinite;
24552  z-index:-1;
24553}
24554
24555@keyframes corePulse{
24556  0%,100%{opacity:.35; transform:scale(.99);}
24557  50%{opacity:.75; transform:scale(1.02);}
24558}
24559
24560/* ACTIVE */
24561.sbo-top-fixed-footer a.is-active{
24562  color:#ffffff;
24563  background: rgba(226,166,66,0.1);
24564  box-shadow: inset 0 0 0 1px rgba(226,166,66,0.25);
24565}
24566
24567.sbo-top-fixed-footer a.is-active img{
24568  filter: drop-shadow(0 0 18px rgba(226,166,66,0.8));
24569}
24570
24571/* RESPONSIVE */
24572@media (max-width:500px){
24573  .sbo-top-fixed-footer{
24574    padding: 10px 10px calc(10px + env(safe-area-inset-bottom));
24575    border-radius: 16px 16px 0 0;
24576    gap:6px;
24577  }
24578  .sbo-top-fixed-footer a{
24579    font-size:11px;
24580    padding:7px 5px;
24581    gap:5px;
24582    max-width:104px;
24583  }
24584  .sbo-top-fixed-footer img{width:22px;height:22px;}
24585  .sbo-top-fixed-footer .tada img{width:26px;height:26px;}
24586}
24587</style>
24588<div class="sbo-top-fixed-footer">
24589  <a href="https://sunny-401.sunnypork.com/" target="_blank" rel="nofollow noopener">
24590    <img src="https://sunnypork.com/assets/images/icon-nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.png" alt="BONUS NAGARP">
24591    Official
24592  </a>
24593  <a href="https://sunny-401.sunnypork.com/" target="_blank" rel="nofollow noopener">
24594    <img src="https://sunnypork.com/assets/images/icon-nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.png" alt="LOGIN NAGARP">
24595    Login
24596  </a>
24597  <a href="https://sunny-401.sunnypork.com/" target="_blank" rel="nofollow noopener">
24598    <img src="https://sunnypork.com/assets/images/icon-nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.png" alt="Daftar NAGARP">
24599    Daftar
24600  </a>
24601  <a href="https://sunny-401.sunnypork.com/" target="_blank" rel="nofollow noopener">
24602    <img src="https://sunnypork.com/assets/images/icon-nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.png" alt="Daftar NAGARP">
24603    Slot 
24604  </a>
24605  <a href="https://sunny-401.sunnypork.com/" target="_blank" rel="nofollow noopener">
24606    <img src="https://sunnypork.com/assets/images/icon-nagarp-x-sunny-pork-perubahan-teknologi-digital-membentuk-pengalaman-pla-674ab6.png" alt="Daftar NAGARP">
24607    Pengalaman Platform Interaktif
24608  </a>
24609
24610</div>
24611<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"51df3b17a746455f940c0b561e8c0c0b","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
vendor: 2 bytes, line 24611
24611
24612<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"51df3b17a746455f940c0b561e8c0c0b","r":1}' crossorigin="anonymous"></script>
24612
24613
24614<style id="NAGARP-theme-override">
24615
24616/* =========================================================
24617   NAGARP EARTHY LUXURY THEME
24618   Espresso + Saffron + Copper + Wine + Ivory
24619   Distinct from previous black/gold/red treatment.
24620========================================================= */
24621:root{
24622  --brand-50:#fff7e8;
24623  --brand-100:#f8e5b8;
24624  --brand-500:#d8a62e;
24625  --brand-600:#b87a22;
24626  --brand-700:#7a481e;
24627
24628  --gray-900:#f9ecd4;
24629  --gray-700:#ddc8a8;
24630  --gray-600:#bca382;
24631  --gray-500:#8f765f;
24632  --gray-300:#77481b;
24633  --gray-200:#4d2e14;
24634  --gray-100:#120c08;
24635  --border:#77481b;
24636
24637  --bt-espresso:#130c09;
24638  --bt-coffee:#120c08;
24639  --bt-panel:#2b1b0e;
24640  --bt-panel2:#2b1b0e;
24641
24642  --bt-wine:#6f481d;
24643  --bt-brick:#9a6324;
24644  --bt-copper:#c66f2a;
24645  --bt-saffron:#d8a62e;
24646  --bt-sand:#e8c96f;
24647  --bt-ivory:#fff1d5;
24648  --bt-cream:#f9e7c6;
24649}
24650
24651html{
24652  background:#130c09 !important;
24653}
24654
24655body{
24656  color:#f9e7c6 !important;
24657  background:
24658    radial-gradient(circle at 48% -8%,rgba(216,166,46,.16),transparent 30%),
24659    radial-gradient(circle at 8% 31%,rgba(198,111,42,.12),transparent 35%),
24660    radial-gradient(circle at 93% 55%,rgba(119,72,27,.10),transparent 37%),
24661    linear-gradient(180deg,#2b1b0e 0%,#120c08 46%,#120b08 100%) !important;
24662  background-attachment:fixed !important;
24663}
24664
24665.topbar{
24666  background:linear-gradient(90deg,#160d09,#4d2e14 50%,#160d09) !important;
24667  color:#e8c96f !important;
24668  border-bottom:1px solid rgba(216,166,46,.34) !important;
24669}
24670.topbar a{color:#f9e7c6 !important}
24671.topbar a:hover{color:#d8a62e !important}
24672
24673.navbar,
24674.navbar-logo{
24675  background:
24676    radial-gradient(circle at 50% 0%,rgba(216,166,46,.10),transparent 43%),
24677    linear-gradient(135deg,rgba(43,27,14,.98),rgba(20,12,9,.99) 54%,rgba(43,27,14,.98)) !important;
24678  border-bottom:1px solid rgba(216,166,46,.42) !important;
24679  box-shadow:
24680    0 5px 22px rgba(0,0,0,.38),
24681    0 0 24px rgba(198,111,42,.08) !important;
24682}
24683.navbar::after{
24684  background:linear-gradient(
24685    90deg,
24686    transparent,
24687    #77481b,
24688    #a36422,
24689    #c66f2a,
24690    #d8a62e,
24691    #e8c96f,
24692    #d8a62e,
24693    #c66f2a,
24694    #a36422,
24695    #77481b,
24696    transparent
24697  ) !important;
24698  background-size:220% 100% !important;
24699  opacity:.88 !important;
24700  animation:btEarthLine 5.6s linear infinite !important;
24701}
24702@keyframes btEarthLine{
24703  from{background-position:185% 0}
24704  to{background-position:-85% 0}
24705}
24706
24707/* Search */
24708.search,
24709.nav-inner .search{
24710  background:#fff8ed !important;
24711  border-color:#dcb65a !important;
24712  box-shadow:
24713    0 4px 14px rgba(0,0,0,.17),
24714    0 0 15px rgba(198,111,42,.07) !important;
24715}
24716.search:focus-within,
24717.nav-inner .search:focus-within{
24718  border-color:#c66f2a !important;
24719  box-shadow:
24720    0 0 0 3px rgba(216,166,46,.13),
24721    0 0 21px rgba(119,72,27,.08) !important;
24722}
24723.search input,
24724.nav-inner .search input{color:#2b1b0e !important}
24725.search input::placeholder,
24726.nav-inner .search input::placeholder{color:#957866 !important}
24727.search-icon-btn,
24728.nav-inner .search-icon-btn{color:#a36422 !important}
24729.search-icon-btn:hover{color:#c66f2a !important}
24730
24731/* Buttons / nav actions */
24732.icon-btn,
24733.login-btn,
24734.pd-sticky-share-btn,
24735.pd-sticky-btn-cart,
24736.pd-sticky-btn-chat{
24737  color:#e8c96f !important;
24738  border-color:rgba(216,166,46,.31) !important;
24739  background:linear-gradient(135deg,#2b1b0e,#130c09) !important;
24740  box-shadow:
24741    inset 0 1px 0 rgba(255,241,213,.05),
24742    0 3px 12px rgba(0,0,0,.27) !important;
24743}
24744.icon-btn:hover,
24745.login-btn:hover,
24746.pd-sticky-share-btn:hover,
24747.pd-sticky-btn-cart:hover,
24748.pd-sticky-btn-chat:hover{
24749  color:#fff !important;
24750  border-color:#d8a62e !important;
24751  background:linear-gradient(135deg,#4d2e14,#120c08) !important;
24752  box-shadow:0 0 17px rgba(198,111,42,.14) !important;
24753}
24754
24755/* Categories */
24756.mega-cats a{color:#ddc8a8 !important}
24757.mega-cats a:hover{
24758  color:#fff !important;
24759  background:linear-gradient(135deg,rgba(163,100,34,.28),rgba(216,166,46,.12)) !important;
24760}
24761.mega-cats a.active,
24762.mega-more-grid a.active,
24763.mega-cats-more.active > .mega-more-btn{
24764  color:#fff !important;
24765  background:linear-gradient(135deg,#77481b,#a36422 48%,#c66f2a 76%,#d8a62e) !important;
24766}
24767.mega-more-btn,
24768.mm2-trigger{
24769  color:#e8c96f !important;
24770  background:#120c08 !important;
24771  border-color:#77481b !important;
24772}
24773.mega-more-dropdown,
24774.mm2-panel{
24775  background:#120c08 !important;
24776  color:#f9e7c6 !important;
24777  border-color:#77481b !important;
24778}
24779
24780/* Main */
24781.hero{
24782  background:
24783    radial-gradient(circle at 10% 0%,rgba(216,166,46,.085),transparent 34%),
24784    linear-gradient(145deg,#2b1b0e,#140d0a) !important;
24785}
24786.usp-section,
24787.flash,
24788.products{
24789  background:
24790    radial-gradient(circle at 92% 0%,rgba(198,111,42,.05),transparent 32%),
24791    linear-gradient(180deg,#120c08,#120c09) !important;
24792  border-color:rgba(216,166,46,.15) !important;
24793}
24794
24795.product-single{
24796  --pd-border:#77481b;
24797  --pd-bg:#120c08;
24798  --pd-muted:#c9b08d;
24799  --pd-dark:#f9e7c6;
24800  --pd-accent:#d8a62e;
24801  --pd-soft:#2b1b0e;
24802  --pd-soft-border:#77481b;
24803  --pd-danger:#b77924;
24804  --pd-danger-bg:#493019;
24805  --pd-danger-text:#e4b767;
24806  --pd-shadow:0 8px 28px -10px rgba(0,0,0,.62);
24807  color:#f9e7c6 !important;
24808}
24809
24810.product-single--v3,
24811.pd-top-layout{background:transparent !important}
24812
24813.pd-sticky-header{
24814  background:rgba(18,12,8,.96) !important;
24815  border-bottom-color:rgba(216,166,46,.32) !important;
24816  box-shadow:
24817    0 5px 24px rgba(0,0,0,.45),
24818    0 0 18px rgba(198,111,42,.07) !important;
24819}
24820.pd-sticky-title{color:#f9e7c6 !important}
24821.pd-sticky-price{color:#e8c96f !important}
24822.pd-sticky-thumb{
24823  background:#120c08 !important;
24824  border-color:#77481b !important;
24825}
24826
24827/* Panels / cards */
24828.pd-media-card,
24829.pd-info-card,
24830.pd-buy-card,
24831.pd-side-card,
24832.pd-full-description,
24833.pd-reviews-section,
24834.pd-related-full,
24835.pd-installment-card,
24836.pd-installment-card2,
24837.pd-compare-section,
24838.pd-short-desc,
24839.pd-variants,
24840.prod-card,
24841.usp-item,
24842.hero-box,
24843.search-suggest,
24844.user-dropdown,
24845.mobile-user-sheet-content{
24846  background:
24847    radial-gradient(circle at 100% 0%,rgba(216,166,46,.055),transparent 36%),
24848    radial-gradient(circle at 0% 100%,rgba(163,100,34,.038),transparent 38%),
24849    linear-gradient(155deg,#2b1b0e,#140d0a) !important;
24850  color:#f9e7c6 !important;
24851  border-color:rgba(216,166,46,.18) !important;
24852  box-shadow:
24853    0 9px 28px rgba(0,0,0,.29),
24854    inset 0 1px 0 rgba(255,241,213,.024) !important;
24855}
24856.prod-card:hover,
24857.usp-item:hover,
24858.pd-media-card:hover,
24859.pd-info-card:hover{
24860  border-color:rgba(216,166,46,.36) !important;
24861  box-shadow:
24862    0 12px 30px rgba(0,0,0,.33),
24863    0 0 19px rgba(198,111,42,.075) !important;
24864}
24865.prod-card .img-box,
24866.pd-main-image,
24867.pd-mslide,
24868.pd-thumb-btn{
24869  background:#120c08 !important;
24870  border-color:#4d2e14 !important;
24871}
24872.pd-thumb-btn.active{
24873  border-color:#d8a62e !important;
24874  box-shadow:
24875    0 0 0 2px rgba(216,166,46,.12),
24876    0 0 16px rgba(198,111,42,.07) !important;
24877}
24878
24879/* Heading / price */
24880.pd-title,
24881.pd-full-desc-title,
24882.pd-related-title,
24883.hero-text h1,
24884.price strong,
24885.pd-price-current,
24886.text-brand,
24887.hero-text .text-brand{
24888  color:#e8c96f !important;
24889  text-shadow:0 0 11px rgba(216,166,46,.08) !important;
24890}
24891.pd-price-original{color:#a88c73 !important}
24892
24893/* Primary CTA */
24894.btn-primary,
24895.modal-btn-primary,
24896.review-form .btn-primary,
24897.pd-compare-btn,
24898.pd-sticky-btn-buy{
24899  background:linear-gradient(135deg,#77481b 0%,#a36422 40%,#c66f2a 72%,#d8a62e 100%) !important;
24900  border-color:#e8c96f !important;
24901  color:#fff !important;
24902  box-shadow:
24903    0 5px 18px rgba(119,72,27,.16),
24904    0 0 15px rgba(216,166,46,.06) !important;
24905}
24906.btn-primary:hover,
24907.modal-btn-primary:hover,
24908.review-form .btn-primary:hover,
24909.pd-compare-btn:hover,
24910.pd-sticky-btn-buy:hover{
24911  background:linear-gradient(135deg,#fff1d5,#e8c96f 48%,#c66f2a) !important;
24912  border-color:#fff1d5 !important;
24913  color:#2b1b0e !important;
24914}
24915
24916/* Badges */
24917.badge,
24918.bottom-nav-item-badge,
24919.pd-discount-badge,
24920.mini-disc,
24921.discount,
24922.disc,
24923.sale-badge,
24924.product-single .stock-badge.out{
24925  background:linear-gradient(135deg,#77481b,#a36422 52%,#c66f2a) !important;
24926  color:#fff !important;
24927  border-color:#d8a62e !important;
24928}
24929
24930/* Forms */
24931.product-single input,
24932.product-single select,
24933.product-single textarea,
24934.modal input,
24935.modal select,
24936.modal textarea{
24937  background:#120c08 !important;
24938  color:#f9e7c6 !important;
24939  border-color:#77481b !important;
24940}
24941.product-single input:focus,
24942.product-single select:focus,
24943.product-single textarea:focus{
24944  border-color:#d8a62e !important;
24945  box-shadow:0 0 0 3px rgba(216,166,46,.10) !important;
24946}
24947
24948/* NAGARP machine */
24949.Pengalaman-Platform-Interaktif-v3{
24950  background:
24951    radial-gradient(circle at 15% 0%,rgba(198,111,42,.11),transparent 46%),
24952    radial-gradient(circle at 85% 100%,rgba(119,72,27,.08),transparent 55%),
24953    linear-gradient(135deg,#2b1b0e,#130c09) !important;
24954  border-color:rgba(216,166,46,.28) !important;
24955  box-shadow:
24956    inset 0 0 20px rgba(163,100,34,.04),
24957    0 0 18px rgba(216,166,46,.055) !important;
24958}
24959.NAGARP-controls-v3{
24960  background:rgba(22,13,10,.74) !important;
24961  border-color:rgba(216,166,46,.24) !important;
24962}
24963.NAGARP-controls-v3 label{color:#e8c96f !important}
24964.NAGARP-controls-v3 select,
24965.NAGARP-controls-v3 input{
24966  background:#120c08 !important;
24967  color:#f9e7c6 !important;
24968  border-color:rgba(216,166,46,.28) !important;
24969}
24970#btnNAGARPRandom{
24971  background:linear-gradient(135deg,#77481b,#a36422 48%,#c66f2a 76%,#d8a62e) !important;
24972  color:#fff !important;
24973  border-color:#e8c96f !important;
24974}
24975
24976/* Footer */
24977.bottom-nav{
24978  background:rgba(20,12,9,.98) !important;
24979  border-top-color:#77481b !important;
24980  box-shadow:0 -8px 24px rgba(0,0,0,.45) !important;
24981}
24982.bottom-nav-item{color:#cfb28f !important}
24983.bottom-nav-item:hover{color:#e8c96f !important}
24984.bottom-nav-item.active{
24985  color:#fff !important;
24986  background:linear-gradient(135deg,#77481b,#a36422 48%,#c66f2a 76%,#d8a62e) !important;
24987}
24988
24989.sbo-top-fixed-footer{
24990  background:
24991    radial-gradient(circle at 50% 0%,rgba(216,166,46,.085),transparent 42%),
24992    linear-gradient(180deg,rgba(43,27,14,.98),rgba(18,11,8,.99)) !important;
24993  border-top-color:#8f5a22 !important;
24994  box-shadow:
24995    0 -12px 32px rgba(0,0,0,.50),
24996    inset 0 1px 0 rgba(232,201,111,.05) !important;
24997}
24998.sbo-top-fixed-footer::after{
24999  background:linear-gradient(
25000    120deg,
25001    transparent,
25002    rgba(216,166,46,.05),
25003    rgba(232,201,111,.20),
25004    rgba(198,111,42,.09),
25005    transparent
25006  ) !important;
25007}
25008.sbo-top-fixed-footer a{color:#e8c96f !important}
25009.sbo-top-fixed-footer a:hover,
25010.sbo-top-fixed-footer a.is-active{
25011  color:#fff !important;
25012  background:linear-gradient(135deg,rgba(119,72,27,.15),rgba(198,111,42,.075)) !important;
25013}
25014.sbo-top-fixed-footer img{
25015  filter:
25016    drop-shadow(0 0 7px rgba(216,166,46,.28))
25017    drop-shadow(0 0 11px rgba(198,111,42,.10)) !important;
25018}
25019.sbo-top-fixed-footer .hud-corner{
25020  border-color:rgba(216,166,46,.30) !important;
25021  filter:drop-shadow(0 0 9px rgba(198,111,42,.09)) !important;
25022}
25023.sbo-top-fixed-footer .tada{
25024  color:#fff !important;
25025  background:
25026    radial-gradient(circle at 50% 0%,rgba(198,111,42,.18),transparent 70%),
25027    rgba(119,72,27,.055) !important;
25028  border-color:rgba(216,166,46,.24) !important;
25029  box-shadow:
25030    0 0 15px rgba(198,111,42,.11),
25031    inset 0 0 14px rgba(216,166,46,.06) !important;
25032}
25033
25034</style>
25035
25036
25037<style id="NAGARP-balanced-adjust">
25038
25039/* =========================================================
25040   EARTHY FINISHING PASS
25041========================================================= */
25042:root{
25043  --brand-500:#d8a62e !important;
25044  --brand-600:#b87a22 !important;
25045  --brand-700:#7a481e !important;
25046}
25047
25048/* Existing animation retained, recolored and slowed slightly */
25049.pd-media-card,
25050.pd-info-card,
25051.sbo-top-fixed-footer,
25052.navbar{
25053  background-size:145% 145% !important;
25054  animation:btEarthShift 10s ease-in-out infinite alternate !important;
25055}
25056@keyframes btEarthShift{
25057  from{background-position:0% 20%}
25058  to{background-position:100% 80%}
25059}
25060
25061.sbo-top-fixed-footer .tada::before{
25062  background:
25063    radial-gradient(circle,rgba(216,166,46,.11),transparent 68%),
25064    radial-gradient(circle,rgba(119,72,27,.075),transparent 74%) !important;
25065  filter:blur(12px) !important;
25066  opacity:.48 !important;
25067  animation:btEarthPulse 4s ease-in-out infinite !important;
25068}
25069@keyframes btEarthPulse{
25070  0%,100%{opacity:.26;transform:scale(.985)}
25071  50%{opacity:.60;transform:scale(1.035)}
25072}
25073
25074button:hover,
25075a:hover{
25076  text-shadow:0 0 9px rgba(216,166,46,.20);
25077}
25078
25079@media(prefers-reduced-motion:reduce){
25080  .navbar::after,
25081  .pd-media-card,
25082  .pd-info-card,
25083  .sbo-top-fixed-footer,
25084  .navbar,
25085  .sbo-top-fixed-footer .tada::before{
25086    animation:none !important;
25087  }
25088}
25089
25090</style>
25091
25092<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"5aa68ce2394b457896bf8eb3d53f6f3d","r":1}' crossorigin="anonymous"></script>
vendor: 2 bytes, line 25092
25092
25093<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"7fbda7d5e9cb448b912aa8e936e771c8","r":1}' crossorigin="anonymous"></script>
vendor: 2 bytes, line 25093
25093
25094<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"de306536e07f47468f6bfb89ad0d03ca","r":1,"spa":2}' crossorigin="anonymous"></script>
25094
25095</body>
25096</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.