{"id":9456,"date":"2026-10-01T16:43:09","date_gmt":"2026-10-01T09:43:09","guid":{"rendered":"https:\/\/bingxueindonesia.co.id\/?page_id=9456"},"modified":"2026-10-01T17:23:52","modified_gmt":"2026-10-01T10:23:52","slug":"slot-machine","status":"publish","type":"page","link":"https:\/\/bingxueindonesia.co.id\/en\/slot-machine\/","title":{"rendered":"Slot Machine"},"content":{"rendered":"<div data-elementor-type=\"wp-page\" data-elementor-id=\"9456\" class=\"elementor elementor-9456\">\n\t\t\t\t<div class=\"elementor-element elementor-element-0a7a337 e-flex e-con-boxed e-con e-parent\" data-id=\"0a7a337\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-873c006 elementor-widget__width-inherit elementor-absolute elementor-widget elementor-widget-html\" data-id=\"873c006\" data-element_type=\"widget\" data-settings=\"{&quot;_position&quot;:&quot;absolute&quot;}\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!-- SLOT MACHINE GREEN GRAPE (pengganti Spinwheel.html) - Tempel di Elementor > HTML Widget -->\r\n<!--\r\n  Backend TIDAK berubah: tetap memakai Snippet 1 (Core), Snippet 2 (Admin) dan Snippet 3 (endpoint swe\/v1).\r\n  Pemenang SELALU dipilih dan disimpan oleh server (POST \/draw, random_int). Halaman ini hanya menganimasikan\r\n  nomor struk persis seperti yang dikirim server.\r\n\r\n  YANG BOLEH KAMU GANTI SENDIRI (cari tanda [GANTI]):\r\n  1. Background  -> blok \"[GANTI] BACKGROUND\" tepat di bawah ini (URL untuk desktop dan untuk HP \/ IG Live).\r\n  2. Logo        -> URL logo di tag <img class=\"bxsm-logo\"> (sudah diisi logo yang lama).\r\n  3. Teks        -> PUBLIC_STATUS \/ TAGLINE \/ PUBLIC_WINNERS_TEXT di bagian awal <script>.\r\n-->\r\n\r\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Montserrat:wght@500;600;700;800;900&family=Playfair+Display:wght@700;800&display=swap\" rel=\"stylesheet\">\r\n\r\n<style>\r\n\/* =====================================================================\r\n   [GANTI] BACKGROUND\r\n   Tulis URL gambar di dalam url('...'). Biarkan none = tanpa gambar (gradasi hijau muda).\r\n   - --bxsm-bg-desktop : layar lebar (laptop \/ OBS), disarankan gambar landscape mis. 1920x1080\r\n   - --bxsm-bg-mobile  : layar HP \/ IG Live, disarankan gambar potrait mis. 1080x1920.\r\n                         Kalau none, HP ikut memakai gambar desktop.\r\n   - --bxsm-bg-veil    : lapisan putih lembut di tengah agar logo & teks tetap terbaca (0 = mati, 1 = penuh)\r\n   Contoh:\r\n     --bxsm-bg-desktop: url('https:\/\/bingxueindonesia.co.id\/wp-content\/uploads\/2026\/10\/bg-desktop.jpg');\r\n   ===================================================================== *\/\r\n.bxsm-page {\r\n  --bxsm-bg-desktop: none;\r\n  --bxsm-bg-mobile: none;\r\n  --bxsm-bg-veil: 0.55;\r\n}\r\n\r\n\/* =====================================================================\r\n   SEMUA STYLE DI-SCOPE KE .bxsm  (tidak menyentuh tema \/ Elementor)\r\n   ===================================================================== *\/\r\n.bxsm {\r\n  --bxsm-font: 'Montserrat', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;\r\n  --bxsm-serif: 'Playfair Display', Georgia, 'Times New Roman', serif;\r\n  --bxsm-forest: #0f3a1d;\r\n  --bxsm-forest-2: #1d5a2b;\r\n  --bxsm-forest-3: #2f7a34;\r\n  --bxsm-lime: #b5e14f;\r\n  --bxsm-lime-2: #9ccf3c;\r\n  --bxsm-lime-soft: #eaf6cc;\r\n  --bxsm-cream: #fbfcf2;\r\n  --bxsm-pistachio: #eaf4d3;\r\n  --bxsm-ink-soft: rgba(15, 58, 29, 0.66);\r\n  --bxsm-line: #dbe8c2;\r\n  --bxsm-danger: #c63b2f;\r\n  --bxsm-warn-bg: #fff6e3;\r\n  --bxsm-warn-line: #e9ac43;\r\n  --bxsm-warn-ink: #6a4300;\r\n  font-family: var(--bxsm-font);\r\n  color: var(--bxsm-forest);\r\n  font-size: 16px;\r\n  line-height: 1.4;\r\n  text-align: left;\r\n  -webkit-font-smoothing: antialiased;\r\n  -moz-osx-font-smoothing: grayscale;\r\n}\r\n.bxsm, .bxsm *, .bxsm *::before, .bxsm *::after { box-sizing: border-box; }\r\n.bxsm [hidden] { display: none !important; }\r\n.bxsm svg { display: block; }\r\n.bxsm img { box-shadow: none; border: 0; }\r\n\r\n\/* ---------- reset tombol & input (tema sering memberi warna\/border sendiri) ---------- *\/\r\n.bxsm button.bxsm-btn,\r\n.bxsm button.bxsm-btn:hover,\r\n.bxsm button.bxsm-btn:focus,\r\n.bxsm button.bxsm-btn:active {\r\n  font-family: var(--bxsm-font);\r\n  text-transform: none;\r\n  text-decoration: none;\r\n  text-shadow: none;\r\n  letter-spacing: normal;\r\n  line-height: 1.2;\r\n  margin: 0;\r\n  min-height: 0;\r\n  width: auto;\r\n  outline: none;\r\n  -webkit-appearance: none;\r\n  appearance: none;\r\n}\r\n.bxsm button.bxsm-btn:focus-visible { outline: 3px solid var(--bxsm-lime); outline-offset: 3px; }\r\n.bxsm button.bxsm-btn:disabled { cursor: not-allowed; }\r\n\r\n\/* ---------- halaman ---------- *\/\r\n.bxsm-page {\r\n  position: relative;\r\n  isolation: isolate;\r\n  overflow: hidden;\r\n  width: 100%;\r\n  min-height: 100vh;\r\n  padding: clamp(10px, 2vh, 34px) 16px clamp(28px, 5vh, 56px);\r\n  background-color: var(--bxsm-pistachio);\r\n  background-image:\r\n    radial-gradient(ellipse 62% 48% at 50% 40%, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0) 72%),\r\n    radial-gradient(ellipse 55% 45% at 0% 0%, rgba(181, 225, 79, 0.34), rgba(181, 225, 79, 0) 62%),\r\n    radial-gradient(ellipse 55% 45% at 100% 100%, rgba(181, 225, 79, 0.3), rgba(181, 225, 79, 0) 62%),\r\n    linear-gradient(180deg, #f7fbea 0%, #e6f1cb 100%);\r\n}\r\n\r\n\/* gambar background: setinggi layar pertama (yang tampil di live), memudar ke bawah *\/\r\n.bxsm-bg {\r\n  position: absolute;\r\n  z-index: -1;\r\n  left: 0;\r\n  right: 0;\r\n  top: 0;\r\n  height: 100vh;\r\n  height: 100svh;\r\n  min-height: 520px;\r\n  pointer-events: none;\r\n  background-image: var(--bxsm-bg-desktop);\r\n  background-size: cover;\r\n  background-position: center;\r\n  background-repeat: no-repeat;\r\n  -webkit-mask-image: linear-gradient(180deg, #000 82%, rgba(0, 0, 0, 0) 100%);\r\n  mask-image: linear-gradient(180deg, #000 82%, rgba(0, 0, 0, 0) 100%);\r\n}\r\n.bxsm-bg::after {\r\n  content: '';\r\n  position: absolute;\r\n  inset: 0;\r\n  background: radial-gradient(ellipse 70% 55% at 50% 42%, rgba(255, 255, 255, var(--bxsm-bg-veil)) 0%, rgba(255, 255, 255, 0) 75%);\r\n}\r\n\r\n\/* ---------- panggung utama ---------- *\/\r\n.bxsm-stage {\r\n  position: relative;\r\n  z-index: 1;\r\n  width: 100%;\r\n  max-width: 1240px;\r\n  margin: 0 auto;\r\n  display: flex;\r\n  flex-direction: column;\r\n  align-items: center;\r\n  gap: clamp(8px, 1.6vh, 22px);\r\n}\r\n\r\n\/* header \/ logo *\/\r\n.bxsm-head { display: flex; flex-direction: column; align-items: center; gap: clamp(4px, 0.9vh, 12px); text-align: center; }\r\n.bxsm-logo-wrap { display: flex; justify-content: center; align-items: center; border-radius: 26px; transition: background 0.3s; }\r\n.bxsm-logo {\r\n  display: block;\r\n  width: auto;\r\n  height: auto;\r\n  max-height: clamp(54px, 12.5vh, 160px);\r\n  max-width: min(560px, 82vw);\r\n  filter: drop-shadow(0 2px 3px rgba(15, 58, 29, 0.22));\r\n}\r\n.bxsm-logo-wrap.is-plate {\r\n  background: radial-gradient(ellipse at 50% 30%, #2d7437 0%, #134220 75%);\r\n  padding: clamp(8px, 1.3vh, 16px) clamp(18px, 2.6vw, 34px);\r\n  box-shadow: 0 12px 26px -12px rgba(15, 58, 29, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.18);\r\n}\r\n.bxsm-title-fallback { display: none; }\r\n.bxsm-page.bxsm-no-logo .bxsm-logo-wrap { display: none; }\r\n.bxsm-page.bxsm-no-logo .bxsm-title-fallback { display: block; }\r\n.bxsm-tf-brand { font-weight: 700; letter-spacing: 0.42em; padding-left: 0.42em; font-size: clamp(11px, min(1.15vw, 1.8vh), 18px); color: var(--bxsm-forest); }\r\n.bxsm-tf-main {\r\n  font-family: var(--bxsm-serif);\r\n  font-weight: 800;\r\n  font-size: clamp(32px, min(5.6vw, 8.2vh), 96px);\r\n  line-height: 0.98;\r\n  text-transform: uppercase;\r\n  color: var(--bxsm-forest);\r\n  text-shadow: 0 3px 0 rgba(255, 255, 255, 0.9), 0 10px 22px rgba(31, 90, 42, 0.2);\r\n}\r\n.bxsm-tf-sub {\r\n  display: flex;\r\n  align-items: center;\r\n  justify-content: center;\r\n  gap: 0.7em;\r\n  margin-top: 0.15em;\r\n  font-weight: 800;\r\n  letter-spacing: 0.3em;\r\n  padding-left: 0.3em;\r\n  font-size: clamp(13px, min(1.8vw, 2.6vh), 30px);\r\n  color: var(--bxsm-forest);\r\n}\r\n.bxsm-tf-sub::before, .bxsm-tf-sub::after { content: ''; width: clamp(24px, 5vw, 110px); height: 2px; border-radius: 2px; background: rgba(15, 58, 29, 0.55); }\r\n\r\n.bxsm-live {\r\n  display: inline-flex;\r\n  align-items: center;\r\n  gap: 9px;\r\n  padding: clamp(4px, 0.6vh, 7px) 20px clamp(4px, 0.6vh, 7px) 14px;\r\n  border-radius: 999px;\r\n  background: linear-gradient(180deg, #d6f285 0%, #b5e14f 100%);\r\n  color: var(--bxsm-forest);\r\n  font-weight: 700;\r\n  font-size: clamp(11px, min(1.15vw, 1.9vh), 17px);\r\n  letter-spacing: 0.06em;\r\n  white-space: nowrap;\r\n  box-shadow: 0 5px 12px -5px rgba(31, 90, 42, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.75);\r\n}\r\n.bxsm-live svg { width: 1.25em; height: 1.25em; }\r\n\r\n\/* ---------- mesin slot ---------- *\/\r\n.bxsm-machine {\r\n  --bxsm-row: clamp(44px, min(10vh, 6.2vw), 128px);\r\n  --bxsm-gap: clamp(3px, 0.65vw, 10px);\r\n  --bxsm-wpad: clamp(5px, min(0.95vw, 1.3vh), 14px);\r\n  --bxsm-mpad: clamp(10px, min(1.9vw, 2.5vh), 28px);\r\n  position: relative;\r\n  \/* lebar panel mengikuti jumlah gulungan (diisi JS lewat --bxsm-units \/ --bxsm-cols) *\/\r\n  width: min(1180px, 100%, max(min(100%, 440px), calc(var(--bxsm-units, 13.2) * var(--bxsm-row) + (var(--bxsm-cols, 10) - 1) * var(--bxsm-gap) + 2 * var(--bxsm-wpad) + 2 * var(--bxsm-mpad) + 6px)));\r\n  transition: width 0.3s ease;\r\n  margin-top: clamp(14px, 2.4vh, 28px);\r\n  padding: var(--bxsm-mpad);\r\n  border-radius: clamp(26px, 3.3vw, 52px);\r\n  background: linear-gradient(180deg, #ffffff 0%, #f5f9e8 55%, #eaf2d3 100%);\r\n  border: 1px solid rgba(156, 207, 60, 0.38);\r\n  box-shadow:\r\n    inset 0 2px 0 #ffffff,\r\n    inset 0 -3px 0 rgba(156, 207, 60, 0.22),\r\n    0 30px 54px -26px rgba(29, 90, 43, 0.5),\r\n    0 10px 24px -12px rgba(29, 90, 43, 0.25);\r\n}\r\n.bxsm-tab {\r\n  position: absolute;\r\n  left: 50%;\r\n  top: 0;\r\n  z-index: 6;\r\n  transform: translate(-50%, -52%);\r\n  display: flex;\r\n  align-items: center;\r\n  gap: 0.9em;\r\n  padding: clamp(5px, 0.9vh, 12px) clamp(18px, 2.8vw, 42px);\r\n  border-radius: 999px;\r\n  background: linear-gradient(180deg, #fffef8 0%, #f0f5e1 100%);\r\n  border: 2px solid #dbeab6;\r\n  box-shadow: 0 8px 16px -8px rgba(29, 90, 43, 0.45);\r\n  font-weight: 800;\r\n  font-size: clamp(11px, min(1.4vw, 2.2vh), 22px);\r\n  letter-spacing: 0.22em;\r\n  white-space: nowrap;\r\n  color: var(--bxsm-forest);\r\n}\r\n.bxsm-tab::before, .bxsm-tab::after { content: ''; width: 0.42em; height: 0.42em; border-radius: 50%; background: var(--bxsm-lime-2); flex-shrink: 0; }\r\n.bxsm-window {\r\n  position: relative;\r\n  padding: var(--bxsm-wpad);\r\n  border-radius: clamp(18px, 2.4vw, 36px);\r\n  background: linear-gradient(180deg, #dcefb0 0%, #b9de6c 50%, #d3eaa0 100%);\r\n  box-shadow: inset 0 3px 7px rgba(29, 90, 43, 0.35), inset 0 -2px 4px rgba(255, 255, 255, 0.6);\r\n}\r\n.bxsm-reels-wrap { position: relative; }\r\n.bxsm-reels {\r\n  position: relative;\r\n  display: flex;\r\n  justify-content: center;\r\n  align-items: stretch;\r\n  gap: var(--bxsm-gap);\r\n  height: calc(var(--bxsm-row) * 3);\r\n  container-type: inline-size;\r\n}\r\n.bxsm-reels.is-enter { animation: bxsmFadeIn 0.18s ease-out; }\r\n.bxsm-reel {\r\n  position: relative;\r\n  flex: 1 1 0;\r\n  min-width: 0;\r\n  max-width: calc(var(--bxsm-row) * 1.32);\r\n  height: 100%;\r\n  overflow: hidden;\r\n  border-radius: clamp(8px, 1.3vw, 18px);\r\n  background: linear-gradient(180deg, #e2e9d0 0%, #fbfdf6 22%, #ffffff 50%, #fbfdf6 78%, #e2e9d0 100%);\r\n  box-shadow:\r\n    inset 0 0 0 1px rgba(29, 90, 43, 0.1),\r\n    inset 0 14px 18px -14px rgba(15, 58, 29, 0.45),\r\n    inset 0 -14px 18px -14px rgba(15, 58, 29, 0.45),\r\n    0 2px 0 rgba(255, 255, 255, 0.7);\r\n  container-type: inline-size;\r\n}\r\n.bxsm-reel.is-wide { flex: 1 1 auto; max-width: none; }\r\n\/* baris atas & bawah dibuat pudar *\/\r\n.bxsm-reel::before, .bxsm-reel::after { content: ''; position: absolute; left: 0; right: 0; height: var(--bxsm-row); z-index: 2; pointer-events: none; }\r\n.bxsm-reel::before { top: 0; background: linear-gradient(180deg, rgba(244, 248, 233, 0.97) 0%, rgba(255, 255, 255, 0.62) 100%); }\r\n.bxsm-reel::after { bottom: 0; background: linear-gradient(0deg, rgba(244, 248, 233, 0.97) 0%, rgba(255, 255, 255, 0.62) 100%); }\r\n.bxsm-strip { position: absolute; left: 0; right: 0; top: calc(var(--bxsm-row) * -1); height: calc(var(--bxsm-row) * 5); z-index: 1; will-change: transform; }\r\n.bxsm-cell {\r\n  display: flex;\r\n  align-items: center;\r\n  justify-content: center;\r\n  height: var(--bxsm-row);\r\n  font-family: var(--bxsm-font);\r\n  font-weight: 800;\r\n  font-size: var(--bxsm-cell-fs, min(calc(var(--bxsm-row) * 0.74), 70cqw));\r\n  line-height: 1;\r\n  color: var(--bxsm-forest);\r\n  font-variant-numeric: tabular-nums lining-nums;\r\n  white-space: nowrap;\r\n  overflow: hidden;\r\n  user-select: none;\r\n  -webkit-user-select: none;\r\n}\r\n.bxsm-reel.is-wide .bxsm-cell { font-size: var(--bxsm-wide-fs, calc(var(--bxsm-row) * 0.56)); letter-spacing: 0.03em; padding: 0 0.5em; }\r\n.bxsm-reel.is-fast .bxsm-cell { color: rgba(15, 58, 29, 0.8); text-shadow: 0 -0.17em 0 rgba(15, 58, 29, 0.2), 0 0.17em 0 rgba(15, 58, 29, 0.2); }\r\n.bxsm-reel.is-stopped { animation: bxsmStop 0.55s ease-out; }\r\n.bxsm-sep {\r\n  position: relative;\r\n  z-index: 1;\r\n  flex: 0 0 auto;\r\n  display: flex;\r\n  align-items: center;\r\n  justify-content: center;\r\n  width: min(calc(var(--bxsm-row) * 0.42), 5cqw);\r\n  font-weight: 800;\r\n  font-size: min(calc(var(--bxsm-row) * 0.62), 6.2cqw);\r\n  line-height: 1;\r\n  color: var(--bxsm-forest);\r\n}\r\n.bxsm-sep.is-space { width: min(calc(var(--bxsm-row) * 0.26), 3cqw); }\r\n.bxsm-payline {\r\n  position: absolute;\r\n  z-index: 4;\r\n  left: -4px;\r\n  right: -4px;\r\n  top: calc(var(--bxsm-row) - 4px);\r\n  height: calc(var(--bxsm-row) + 8px);\r\n  border: 3px solid var(--bxsm-lime);\r\n  border-radius: clamp(10px, 1.5vw, 20px);\r\n  box-shadow: 0 0 0 3px rgba(181, 225, 79, 0.22), 0 0 22px rgba(181, 225, 79, 0.55), inset 0 0 14px rgba(181, 225, 79, 0.28);\r\n  pointer-events: none;\r\n}\r\n.bxsm-wide-final {\r\n  display: none;\r\n  min-height: calc(var(--bxsm-row) * 3);\r\n  align-items: center;\r\n  justify-content: center;\r\n  padding: 14px 20px;\r\n  border-radius: clamp(8px, 1.3vw, 18px);\r\n  background: #ffffff;\r\n  box-shadow: inset 0 0 0 3px var(--bxsm-lime), 0 0 22px rgba(181, 225, 79, 0.45);\r\n  text-align: center;\r\n  font-weight: 800;\r\n  font-size: clamp(18px, 2.3vw, 36px);\r\n  line-height: 1.25;\r\n  color: var(--bxsm-forest);\r\n  overflow-wrap: anywhere;\r\n  word-break: normal;\r\n  white-space: pre-wrap;\r\n}\r\n.bxsm-reels-wrap.is-wrapped .bxsm-reels,\r\n.bxsm-reels-wrap.is-wrapped .bxsm-payline { display: none; }\r\n.bxsm-reels-wrap.is-wrapped .bxsm-wide-final { display: flex; }\r\n.bxsm-machine.is-waiting .bxsm-cell { opacity: 0.45; }\r\n\r\n\/* ---------- teks di bawah mesin ---------- *\/\r\n.bxsm-caption {\r\n  width: min(980px, 100%);\r\n  min-height: clamp(40px, 6vh, 72px);\r\n  display: flex;\r\n  flex-direction: column;\r\n  align-items: center;\r\n  justify-content: center;\r\n  text-align: center;\r\n}\r\n.bxsm-tagline {\r\n  display: flex;\r\n  align-items: center;\r\n  gap: 0.75em;\r\n  font-weight: 700;\r\n  font-size: clamp(15px, 2.4vh, 28px);\r\n  color: var(--bxsm-forest);\r\n}\r\n.bxsm-tagline::before, .bxsm-tagline::after {\r\n  content: '';\r\n  width: 0.95em;\r\n  height: 0.95em;\r\n  flex-shrink: 0;\r\n  background:\r\n    linear-gradient(var(--bxsm-lime-2), var(--bxsm-lime-2)) 50% 0 \/ 3px 45% no-repeat,\r\n    linear-gradient(var(--bxsm-lime-2), var(--bxsm-lime-2)) 0 70% \/ 45% 3px no-repeat,\r\n    linear-gradient(var(--bxsm-lime-2), var(--bxsm-lime-2)) 100% 70% \/ 45% 3px no-repeat;\r\n  opacity: 0.9;\r\n}\r\n.bxsm-tagline::before { transform: rotate(-20deg); }\r\n.bxsm-tagline::after { transform: rotate(20deg); }\r\n.bxsm-drawing { font-weight: 800; font-size: clamp(17px, 2.6vh, 30px); letter-spacing: 0.04em; color: var(--bxsm-forest-2); }\r\n.bxsm-dots span { display: inline-block; animation: bxsmDot 1.2s infinite ease-in-out; }\r\n.bxsm-dots span:nth-child(2) { animation-delay: 0.15s; }\r\n.bxsm-dots span:nth-child(3) { animation-delay: 0.3s; }\r\n.bxsm-result-label { font-weight: 800; letter-spacing: 0.16em; font-size: clamp(14px, 2.2vh, 24px); color: var(--bxsm-forest-3); }\r\n.bxsm-result-line { margin-top: 2px; font-weight: 600; font-size: clamp(14px, 2vh, 20px); color: var(--bxsm-ink-soft); max-width: 100%; }\r\n.bxsm-result-no { font-weight: 800; color: var(--bxsm-forest); overflow-wrap: anywhere; word-break: normal; white-space: pre-wrap; }\r\n\r\n\/* panel pemulihan (hasil undian belum pasti) *\/\r\n.bxsm-recovery {\r\n  width: min(780px, 100%);\r\n  padding: 16px 20px;\r\n  border-radius: 18px;\r\n  background: var(--bxsm-warn-bg);\r\n  border: 1.5px solid var(--bxsm-warn-line);\r\n  color: var(--bxsm-warn-ink);\r\n  text-align: left;\r\n  box-shadow: 0 10px 24px -14px rgba(106, 67, 0, 0.45);\r\n}\r\n.bxsm-rec-title { font-weight: 800; font-size: 1.06em; color: #5a3700; }\r\n.bxsm-rec-text { margin-top: 4px; font-size: 0.9em; line-height: 1.5; }\r\n.bxsm-rec-msg { margin-top: 8px; font-size: 0.9em; font-weight: 700; line-height: 1.45; }\r\n.bxsm-rec-msg:empty { display: none; }\r\n.bxsm-rec-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 12px; }\r\n.bxsm button.bxsm-rec-check,\r\n.bxsm button.bxsm-rec-check:hover,\r\n.bxsm button.bxsm-rec-check:focus {\r\n  padding: 11px 22px;\r\n  border: 0;\r\n  border-radius: 12px;\r\n  background: linear-gradient(180deg, #2a6e35 0%, #134520 100%);\r\n  color: #ffffff;\r\n  font-weight: 800;\r\n  font-size: 0.92em;\r\n  letter-spacing: 0.05em;\r\n  cursor: pointer;\r\n  box-shadow: 0 4px 0 #0a2c14;\r\n}\r\n.bxsm button.bxsm-rec-abandon,\r\n.bxsm button.bxsm-rec-abandon:hover,\r\n.bxsm button.bxsm-rec-abandon:focus {\r\n  padding: 10px 18px;\r\n  border: 1.5px solid #c9902c;\r\n  border-radius: 12px;\r\n  background: #ffffff;\r\n  color: #6a4300;\r\n  font-weight: 700;\r\n  font-size: 0.88em;\r\n  cursor: pointer;\r\n}\r\n.bxsm button.bxsm-rec-check:disabled, .bxsm button.bxsm-rec-abandon:disabled { opacity: 0.5; box-shadow: none; }\r\n.bxsm-rec-help { margin-top: 10px; font-size: 0.8em; line-height: 1.5; opacity: 0.9; }\r\n.bxsm-rec-meta { margin-top: 6px; font-size: 0.74em; opacity: 0.75; overflow-wrap: anywhere; }\r\n\r\n\/* ---------- tombol undi ---------- *\/\r\n.bxsm-controls { display: flex; flex-direction: column; align-items: center; gap: clamp(8px, 1.4vh, 14px); width: 100%; }\r\n.bxsm-meta-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 4px 10px; }\r\n.bxsm button.bxsm-draw,\r\n.bxsm button.bxsm-draw:hover,\r\n.bxsm button.bxsm-draw:focus,\r\n.bxsm button.bxsm-draw:active {\r\n  position: relative;\r\n  display: inline-flex;\r\n  align-items: center;\r\n  justify-content: center;\r\n  gap: 0.65em;\r\n  min-width: min(540px, 92vw);\r\n  padding: clamp(12px, 1.7vh, 22px) clamp(28px, 4vw, 56px);\r\n  border: 0;\r\n  border-radius: 999px;\r\n  background: linear-gradient(180deg, #2b7036 0%, #164b24 55%, #0e3a1b 100%);\r\n  color: #ffffff;\r\n  font-weight: 900;\r\n  font-size: clamp(17px, min(3vh, 2.4vw), 36px);\r\n  letter-spacing: 0.05em;\r\n  text-transform: uppercase;\r\n  line-height: 1.1;\r\n  cursor: pointer;\r\n  box-shadow: 0 6px 0 #0a2c14, 0 18px 30px -12px rgba(15, 58, 29, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.25);\r\n  transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.2s ease, opacity 0.2s ease;\r\n}\r\n.bxsm button.bxsm-draw:hover:not(:disabled) { transform: translateY(-2px); filter: brightness(1.07); }\r\n.bxsm button.bxsm-draw:active:not(:disabled) { transform: translateY(4px); box-shadow: 0 2px 0 #0a2c14, 0 8px 16px -10px rgba(15, 58, 29, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.25); }\r\n.bxsm button.bxsm-draw:disabled { opacity: 0.6; filter: saturate(0.55); transform: none; }\r\n.bxsm button.bxsm-draw.is-busy:disabled { opacity: 0.92; filter: none; }\r\n.bxsm-draw-icon {\r\n  display: inline-flex;\r\n  align-items: center;\r\n  justify-content: center;\r\n  width: 1.32em;\r\n  height: 1.32em;\r\n  border-radius: 50%;\r\n  background: var(--bxsm-lime);\r\n  color: var(--bxsm-forest);\r\n  flex-shrink: 0;\r\n}\r\n.bxsm-draw-icon svg { width: 62%; height: 62%; }\r\n.bxsm-draw.is-busy .bxsm-draw-icon svg { animation: bxsmSpin 0.9s linear infinite; }\r\n.bxsm-draw .bxsm-ic-busy { display: none; }\r\n.bxsm-draw.is-busy .bxsm-ic-busy { display: block; }\r\n.bxsm-draw.is-busy .bxsm-ic-go { display: none; }\r\n\r\n.bxsm-count {\r\n  display: inline-flex;\r\n  align-items: center;\r\n  gap: 10px;\r\n  padding: clamp(5px, 0.8vh, 8px) 20px;\r\n  border-radius: 999px;\r\n  background: rgba(255, 255, 255, 0.85);\r\n  border: 1px solid var(--bxsm-line);\r\n  font-weight: 600;\r\n  font-size: clamp(13px, 1.7vh, 18px);\r\n  color: var(--bxsm-forest);\r\n  box-shadow: 0 5px 14px -8px rgba(29, 90, 43, 0.3);\r\n}\r\n.bxsm-count svg { width: 1.2em; height: 1.2em; color: var(--bxsm-forest-2); }\r\n.bxsm-count strong { font-weight: 800; }\r\n.bxsm-status { min-height: 1.3em; max-width: 780px; text-align: center; font-size: clamp(12px, 1.6vh, 15px); font-weight: 600; color: var(--bxsm-ink-soft); }\r\n.bxsm-status.is-error { color: var(--bxsm-danger); }\r\n.bxsm-status.is-warn { color: #8a5a00; }\r\n.bxsm button.bxsm-refresh,\r\n.bxsm button.bxsm-refresh:hover,\r\n.bxsm button.bxsm-refresh:focus {\r\n  padding: 4px 10px;\r\n  border: 0;\r\n  border-radius: 8px;\r\n  background: transparent;\r\n  color: var(--bxsm-forest-3);\r\n  font-weight: 700;\r\n  font-size: 0.8em;\r\n  text-decoration: underline;\r\n  text-underline-offset: 3px;\r\n  cursor: pointer;\r\n}\r\n.bxsm button.bxsm-refresh:disabled { opacity: 0.45; }\r\n.bxsm-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }\r\n\r\n\/* ---------- daftar pemenang ---------- *\/\r\n.bxsm-winners {\r\n  position: relative;\r\n  z-index: 1;\r\n  width: min(900px, 100%);\r\n  margin: clamp(26px, 5vh, 56px) auto 0;\r\n  padding: 20px 22px;\r\n  border-radius: 22px;\r\n  background: rgba(255, 255, 255, 0.8);\r\n  border: 1px solid var(--bxsm-line);\r\n  box-shadow: 0 16px 34px -22px rgba(29, 90, 43, 0.45);\r\n  -webkit-backdrop-filter: blur(6px);\r\n  backdrop-filter: blur(6px);\r\n}\r\n.bxsm-winners-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }\r\n.bxsm-winners-title { font-weight: 800; font-size: 0.78em; letter-spacing: 0.2em; text-transform: uppercase; color: var(--bxsm-forest-3); white-space: nowrap; }\r\n.bxsm-winners-rule { flex: 1; height: 1px; background: linear-gradient(90deg, var(--bxsm-line), rgba(219, 232, 194, 0)); }\r\n.bxsm button.bxsm-reset,\r\n.bxsm button.bxsm-reset:hover,\r\n.bxsm button.bxsm-reset:focus {\r\n  padding: 5px 12px;\r\n  border: 1px solid rgba(198, 59, 47, 0.35);\r\n  border-radius: 8px;\r\n  background: rgba(198, 59, 47, 0.07);\r\n  color: #b2342a;\r\n  font-weight: 700;\r\n  font-size: 0.74em;\r\n  cursor: pointer;\r\n}\r\n.bxsm button.bxsm-reset:hover:not(:disabled) { background: rgba(198, 59, 47, 0.14); }\r\n.bxsm button.bxsm-reset:disabled { opacity: 0.45; }\r\n.bxsm-winners-list { display: flex; flex-direction: column; gap: 8px; }\r\n.bxsm-winners-empty { padding: 18px 0; text-align: center; font-size: 0.88em; color: var(--bxsm-ink-soft); }\r\n.bxsm-winner-row {\r\n  display: flex;\r\n  align-items: center;\r\n  justify-content: space-between;\r\n  gap: 12px;\r\n  padding: 12px 16px;\r\n  border-radius: 14px;\r\n  background: #ffffff;\r\n  border: 1px solid #e4eed2;\r\n}\r\n.bxsm-winner-left { display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; }\r\n.bxsm-winner-dot { width: 10px; height: 10px; flex-shrink: 0; border-radius: 50%; background: radial-gradient(circle at 35% 30%, #ffffff, var(--bxsm-lime-2) 70%); box-shadow: 0 0 0 3px rgba(181, 225, 79, 0.25); }\r\n.bxsm-winner-info { min-width: 0; }\r\n.bxsm-winner-line { font-weight: 700; font-size: 0.95em; line-height: 1.35; overflow-wrap: anywhere; word-break: normal; }\r\n.bxsm-winner-no { color: var(--bxsm-forest-3); font-weight: 800; margin-right: 0.45em; }\r\n.bxsm-winner-prize { display: inline-block; margin-top: 4px; padding: 2px 10px; border-radius: 999px; background: var(--bxsm-lime-soft); color: var(--bxsm-forest-2); font-size: 0.76em; font-weight: 700; overflow-wrap: anywhere; }\r\n.bxsm button.bxsm-detail,\r\n.bxsm button.bxsm-detail:hover,\r\n.bxsm button.bxsm-detail:focus {\r\n  flex-shrink: 0;\r\n  padding: 6px 14px;\r\n  border: 1px solid #b9d68a;\r\n  border-radius: 9px;\r\n  background: #f4f9e8;\r\n  color: var(--bxsm-forest-2);\r\n  font-weight: 700;\r\n  font-size: 0.78em;\r\n  cursor: pointer;\r\n}\r\n.bxsm button.bxsm-detail:hover:not(:disabled) { background: var(--bxsm-lime-soft); }\r\n.bxsm button.bxsm-detail:disabled { opacity: 0.45; }\r\n\r\n\/* =====================================================================\r\n   LAYER POPUP (dipindah ke <body> oleh JS agar tidak terpotong Elementor)\r\n   ===================================================================== *\/\r\n.bxsm-overlay {\r\n  position: fixed;\r\n  inset: 0;\r\n  z-index: 100000;\r\n  display: flex;\r\n  align-items: center;\r\n  justify-content: center;\r\n  padding: 20px;\r\n  background: rgba(12, 40, 20, 0.55);\r\n  -webkit-backdrop-filter: blur(6px);\r\n  backdrop-filter: blur(6px);\r\n  opacity: 0;\r\n  visibility: hidden;\r\n  transition: opacity 0.25s ease, visibility 0s linear 0.25s;\r\n}\r\n.bxsm-overlay.show { opacity: 1; visibility: visible; transition: opacity 0.25s ease; }\r\n.bxsm-sparkle { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\r\n.bxsm-modal {\r\n  position: relative;\r\n  width: 100%;\r\n  max-width: 470px;\r\n  max-height: calc(100vh - 40px);\r\n  overflow-y: auto;\r\n  padding: clamp(24px, 4vh, 34px) 28px 0;\r\n  border-radius: 26px;\r\n  background: #ffffff;\r\n  text-align: center;\r\n  color: var(--bxsm-forest);\r\n  box-shadow: 0 32px 80px -22px rgba(10, 40, 20, 0.6), 0 0 0 1px rgba(156, 207, 60, 0.3);\r\n  transform: translateY(16px) scale(0.96);\r\n  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);\r\n  outline: none;\r\n}\r\n.bxsm-overlay.show .bxsm-modal { transform: none; }\r\n.bxsm-modal::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 6px; background: linear-gradient(90deg, #9ccf3c, #d6f285, #2f7a34); }\r\n.bxsm button.bxsm-m-close,\r\n.bxsm button.bxsm-m-close:hover,\r\n.bxsm button.bxsm-m-close:focus {\r\n  position: absolute;\r\n  top: 14px;\r\n  right: 14px;\r\n  width: 34px;\r\n  height: 34px;\r\n  padding: 0;\r\n  border: 1px solid #e1ead0;\r\n  border-radius: 50%;\r\n  background: #f7faef;\r\n  color: var(--bxsm-forest);\r\n  font-size: 20px;\r\n  line-height: 1;\r\n  cursor: pointer;\r\n  display: flex;\r\n  align-items: center;\r\n  justify-content: center;\r\n}\r\n.bxsm button.bxsm-m-close:disabled { opacity: 0.45; }\r\n.bxsm-m-kicker { font-weight: 800; font-size: 0.82em; letter-spacing: 0.16em; color: var(--bxsm-forest-3); padding: 0 30px; }\r\n.bxsm-m-no { margin-top: 4px; font-size: 0.78em; font-weight: 700; color: var(--bxsm-ink-soft); }\r\n.bxsm-m-no:empty { display: none; }\r\n.bxsm-m-name { margin-top: 6px; font-family: var(--bxsm-serif); font-weight: 800; font-size: clamp(1.45em, 4.2vh, 2em); line-height: 1.15; color: var(--bxsm-forest); overflow-wrap: anywhere; }\r\n.bxsm-m-struk-box { margin: clamp(10px, 1.8vh, 16px) 0 4px; padding: clamp(8px, 1.4vh, 12px) 16px; border-radius: 16px; background: #f8fbef; border: 2px solid var(--bxsm-lime); }\r\n.bxsm-m-struk { margin-top: 2px; font-weight: 800; font-size: 1.55em; line-height: 1.2; letter-spacing: 0.02em; color: var(--bxsm-forest); overflow-wrap: anywhere; word-break: normal; white-space: pre-wrap; }\r\n.bxsm-m-fields { display: flex; flex-direction: column; gap: clamp(9px, 1.6vh, 14px); margin: clamp(10px, 1.8vh, 16px) 0 clamp(10px, 1.8vh, 18px); text-align: left; }\r\n.bxsm-m-label { display: block; font-size: 0.7em; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(15, 58, 29, 0.6); }\r\n.bxsm-m-label span { font-weight: 500; text-transform: none; letter-spacing: 0; color: rgba(15, 58, 29, 0.45); }\r\n.bxsm-m-value { margin-top: 3px; font-weight: 600; font-size: 0.95em; overflow-wrap: anywhere; }\r\n.bxsm-m-wa-row { display: flex; align-items: center; gap: 10px; margin-top: 3px; }\r\n.bxsm-m-wa-row .bxsm-m-value { margin-top: 0; }\r\n.bxsm-m-meta { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }\r\n.bxsm button.bxsm-m-copy,\r\n.bxsm button.bxsm-m-copy:hover,\r\n.bxsm button.bxsm-m-copy:focus {\r\n  padding: 5px 12px;\r\n  border: 1px solid #c9dda6;\r\n  border-radius: 8px;\r\n  background: #f4f9e8;\r\n  color: var(--bxsm-forest-2);\r\n  font-weight: 700;\r\n  font-size: 0.8em;\r\n  cursor: pointer;\r\n}\r\n.bxsm img.bxsm-m-foto {\r\n  display: block;\r\n  width: 100%;\r\n  max-height: clamp(90px, 20vh, 210px);\r\n  margin-top: 6px;\r\n  object-fit: contain;\r\n  border-radius: 12px;\r\n  border: 1px solid #e1ead0;\r\n  background: #f4f7ec;\r\n  cursor: zoom-in;\r\n}\r\n.bxsm.bxsm-layer input.bxsm-input[type=\"text\"],\r\n.bxsm.bxsm-layer input.bxsm-input[type=\"text\"]:focus {\r\n  display: block;\r\n  width: 100%;\r\n  height: auto;\r\n  margin: 5px 0 0;\r\n  padding: 10px 14px;\r\n  border: 1.5px solid #d3e2b6;\r\n  border-radius: 10px;\r\n  background: #ffffff;\r\n  color: var(--bxsm-forest);\r\n  font-family: var(--bxsm-font);\r\n  font-size: 0.92em;\r\n  font-weight: 600;\r\n  line-height: 1.3;\r\n  box-shadow: none;\r\n  outline: none;\r\n}\r\n.bxsm.bxsm-layer input.bxsm-input[type=\"text\"]:focus { border-color: var(--bxsm-lime-2); box-shadow: 0 0 0 3px rgba(181, 225, 79, 0.3); }\r\n.bxsm-m-footer { position: sticky; bottom: 0; z-index: 2; margin: 0 -28px; padding: 12px 28px clamp(16px, 2.6vh, 24px); background: linear-gradient(180deg, rgba(255, 255, 255, 0.85) 0%, #ffffff 24px); }\r\n.bxsm-m-error { margin: 0 0 10px; padding: 8px 12px; border-radius: 10px; background: #fdecea; color: #a8281e; font-size: 0.84em; font-weight: 700; text-align: left; line-height: 1.4; }\r\n.bxsm-m-actions { display: flex; gap: 10px; }\r\n.bxsm button.bxsm-m-confirm,\r\n.bxsm button.bxsm-m-confirm:hover,\r\n.bxsm button.bxsm-m-confirm:focus,\r\n.bxsm button.bxsm-m-cancel,\r\n.bxsm button.bxsm-m-cancel:hover,\r\n.bxsm button.bxsm-m-cancel:focus {\r\n  flex: 1;\r\n  padding: 14px;\r\n  border: 0;\r\n  border-radius: 14px;\r\n  font-weight: 800;\r\n  font-size: 0.95em;\r\n  letter-spacing: 0.05em;\r\n  text-transform: uppercase;\r\n  cursor: pointer;\r\n  transition: transform 0.12s ease, filter 0.15s ease;\r\n}\r\n.bxsm button.bxsm-m-confirm, .bxsm button.bxsm-m-confirm:hover, .bxsm button.bxsm-m-confirm:focus {\r\n  background: linear-gradient(180deg, #2b7036 0%, #134520 100%);\r\n  color: #ffffff;\r\n  box-shadow: 0 4px 0 #0a2c14, 0 10px 20px -10px rgba(15, 58, 29, 0.6);\r\n}\r\n.bxsm button.bxsm-m-cancel, .bxsm button.bxsm-m-cancel:hover, .bxsm button.bxsm-m-cancel:focus {\r\n  background: linear-gradient(180deg, #df4b3e 0%, #a8281e 100%);\r\n  color: #ffffff;\r\n  box-shadow: 0 4px 0 #6f1811, 0 10px 20px -10px rgba(168, 40, 30, 0.6);\r\n}\r\n.bxsm button.bxsm-m-confirm:hover:not(:disabled), .bxsm button.bxsm-m-cancel:hover:not(:disabled) { filter: brightness(1.07); transform: translateY(-1px); }\r\n.bxsm button.bxsm-m-confirm:disabled, .bxsm button.bxsm-m-cancel:disabled { opacity: 0.6; transform: none; }\r\n\r\n.bxsm-lightbox {\r\n  position: fixed;\r\n  inset: 0;\r\n  z-index: 100010;\r\n  display: flex;\r\n  align-items: center;\r\n  justify-content: center;\r\n  padding: 4vh 4vw;\r\n  background: rgba(0, 0, 0, 0.9);\r\n  cursor: zoom-out;\r\n}\r\n.bxsm img.bxsm-lightbox-img { max-width: 92vw; max-height: 92vh; width: auto; height: auto; border-radius: 12px; box-shadow: 0 24px 80px rgba(0, 0, 0, 0.8); object-fit: contain; cursor: default; }\r\n\r\n.bxsm-toast {\r\n  position: fixed;\r\n  left: 50%;\r\n  top: 18px;\r\n  z-index: 100020;\r\n  max-width: min(92vw, 560px);\r\n  padding: 11px 22px;\r\n  border-radius: 16px;\r\n  background: var(--bxsm-forest);\r\n  color: #ffffff;\r\n  font-family: var(--bxsm-font);\r\n  font-size: 0.86em;\r\n  font-weight: 600;\r\n  line-height: 1.4;\r\n  text-align: center;\r\n  box-shadow: 0 12px 32px rgba(10, 40, 20, 0.35);\r\n  opacity: 0;\r\n  pointer-events: none;\r\n  transform: translateX(-50%) translateY(-120px);\r\n  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease;\r\n}\r\n.bxsm-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }\r\n\r\n\/* ---------- animasi ---------- *\/\r\n@keyframes bxsmFadeIn { from { opacity: 0.25; } to { opacity: 1; } }\r\n@keyframes bxsmSpin { to { transform: rotate(360deg); } }\r\n@keyframes bxsmDot { 0%, 80%, 100% { opacity: 0.25; } 40% { opacity: 1; } }\r\n@keyframes bxsmStop {\r\n  0% { box-shadow: inset 0 0 0 1px rgba(29, 90, 43, 0.1), inset 0 14px 18px -14px rgba(15, 58, 29, 0.45), inset 0 -14px 18px -14px rgba(15, 58, 29, 0.45), 0 0 0 3px rgba(181, 225, 79, 0.95), 0 0 26px rgba(181, 225, 79, 0.85); }\r\n  100% { box-shadow: inset 0 0 0 1px rgba(29, 90, 43, 0.1), inset 0 14px 18px -14px rgba(15, 58, 29, 0.45), inset 0 -14px 18px -14px rgba(15, 58, 29, 0.45), 0 2px 0 rgba(255, 255, 255, 0.7); }\r\n}\r\n\r\n\/* ---------- responsif ---------- *\/\r\n@media (max-height: 700px) and (min-width: 641px) {\r\n  .bxsm-logo { max-height: clamp(48px, 11vh, 120px); }\r\n  .bxsm-machine { --bxsm-row: clamp(40px, min(9.4vh, 6.2vw), 128px); }\r\n  .bxsm-tf-brand { display: none; }\r\n}\r\n@media (max-width: 640px) {\r\n  .bxsm-page { padding: 12px 8px 24px; }\r\n  .bxsm-bg { background-image: var(--bxsm-bg-mobile), var(--bxsm-bg-desktop); }\r\n  \/* Layar pertama = bingkai live IG: konten di tengah layar, daftar pemenang di bawahnya *\/\r\n  .bxsm-stage { min-height: calc(100vh - 36px); min-height: calc(100svh - 36px); justify-content: center; gap: clamp(10px, 2vh, 18px); }\r\n  \/* Jumlah gulungan SAMA dengan desktop; hanya ukurannya yang mengecil *\/\r\n  .bxsm-machine { --bxsm-row: clamp(46px, 14.5vw, 76px); --bxsm-mpad: 7px; --bxsm-wpad: 4px; --bxsm-gap: 2px; border-radius: 22px; margin-top: 18px; }\r\n  .bxsm-window { border-radius: 15px; }\r\n  .bxsm-reel { border-radius: 7px; }\r\n  .bxsm-payline { left: -3px; right: -3px; border-width: 2.5px; border-radius: 10px; }\r\n  .bxsm-tab { font-size: 11px; letter-spacing: 0.16em; padding: 6px 16px; }\r\n  .bxsm-logo { max-height: clamp(56px, 13vh, 130px); max-width: 86vw; }\r\n  .bxsm-tagline { font-size: clamp(14px, 4.3vw, 18px); gap: 0.55em; }\r\n  .bxsm-result-label { font-size: clamp(13px, 4vw, 17px); letter-spacing: 0.1em; }\r\n  .bxsm-result-line { font-size: clamp(13px, 3.9vw, 16px); }\r\n  .bxsm-drawing { font-size: clamp(16px, 5vw, 20px); }\r\n  .bxsm button.bxsm-draw, .bxsm button.bxsm-draw:hover, .bxsm button.bxsm-draw:focus, .bxsm button.bxsm-draw:active { min-width: 0; width: 100%; font-size: 18px; }\r\n  .bxsm-winners { padding: 16px 12px; border-radius: 18px; }\r\n  .bxsm-winner-row { padding: 10px 12px; }\r\n  .bxsm-modal { padding: 28px 18px 0; border-radius: 20px; }\r\n  .bxsm-m-footer { margin: 0 -18px; padding: 12px 18px 18px; }\r\n  .bxsm-m-name { font-size: 1.6em; }\r\n  .bxsm-m-struk { font-size: 1.3em; }\r\n  .bxsm-m-meta { grid-template-columns: 1fr; }\r\n}\r\n@media (max-width: 640px) and (max-height: 640px) {\r\n  .bxsm-stage { min-height: 0; }\r\n}\r\n@media (prefers-reduced-motion: reduce) {\r\n  .bxsm *, .bxsm *::before, .bxsm *::after {\r\n    animation-duration: 0.001ms !important;\r\n    animation-iteration-count: 1 !important;\r\n    transition-duration: 0.001ms !important;\r\n  }\r\n}\r\n<\/style>\r\n\r\n<!-- ===================== HALAMAN ===================== -->\r\n<div class=\"bxsm bxsm-page\" data-mode=\"loading\">\r\n  <div class=\"bxsm-bg\" aria-hidden=\"true\"><\/div>\r\n\r\n  <div class=\"bxsm-stage\">\r\n    <!-- JUDUL \/ LOGO -->\r\n    <div class=\"bxsm-head\">\r\n      <div class=\"bxsm-logo-wrap\">\r\n        <!-- [GANTI] URL logo -->\r\n        <img decoding=\"async\" class=\"bxsm-logo\" src=\"https:\/\/bingxueindonesia.co.id\/wp-content\/uploads\/2026\/09\/Asset-1.png\" alt=\"Bingxue Green Grape\"\r\n             onerror=\"var p=this.closest('.bxsm-page');if(p){p.classList.add('bxsm-no-logo');}\">\r\n      <\/div>\r\n      <div class=\"bxsm-title-fallback\">\r\n        <div class=\"bxsm-tf-brand\">BINGXUE<\/div>\r\n        <div class=\"bxsm-tf-main\">Green Grape<\/div>\r\n        <div class=\"bxsm-tf-sub\">LUCKY DRAW<\/div>\r\n      <\/div>\r\n      <div class=\"bxsm-live\">\r\n        <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"2.2\" fill=\"currentColor\" stroke=\"none\"\/><path d=\"M8.2 8.2a5.4 5.4 0 0 0 0 7.6M15.8 8.2a5.4 5.4 0 0 1 0 7.6M5.3 5.3a9.5 9.5 0 0 0 0 13.4M18.7 5.3a9.5 9.5 0 0 1 0 13.4\"\/><\/svg>\r\n        LIVE SETIAP JUMAT\r\n      <\/div>\r\n    <\/div>\r\n\r\n    <!-- MESIN SLOT -->\r\n    <div class=\"bxsm-machine\" role=\"group\" aria-label=\"Mesin undian nomor struk\">\r\n      <div class=\"bxsm-tab\" aria-hidden=\"true\">NOMOR STRUK<\/div>\r\n      <div class=\"bxsm-window\">\r\n        <div class=\"bxsm-reels-wrap\">\r\n          <div class=\"bxsm-reels\" role=\"img\" aria-label=\"Nomor struk belum diundi\"><\/div>\r\n          <div class=\"bxsm-payline\" aria-hidden=\"true\"><\/div>\r\n          <div class=\"bxsm-wide-final\"><\/div>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n\r\n    <!-- TEKS DI BAWAH MESIN -->\r\n    <div class=\"bxsm-caption\">\r\n      <div class=\"bxsm-tagline\">Live draw setiap Jumat pukul 19.00 WIB<\/div>\r\n      <div class=\"bxsm-drawing\" hidden>Mengundi<span class=\"bxsm-dots\"><span>.<\/span><span>.<\/span><span>.<\/span><\/span><\/div>\r\n      <div class=\"bxsm-result\" hidden>\r\n        <div class=\"bxsm-result-label\">SELAMAT KEPADA PEMENANG!<\/div>\r\n        <div class=\"bxsm-result-line\">No. struk: <span class=\"bxsm-result-no\"><\/span><\/div>\r\n      <\/div>\r\n      <div class=\"bxsm-recovery\" role=\"alert\" hidden>\r\n        <div class=\"bxsm-rec-title\">Hasil undian belum pasti<\/div>\r\n        <div class=\"bxsm-rec-text\">Koneksi terputus atau server tidak memberi jawaban yang jelas saat mengundi. Pemenang <strong>mungkin sudah tersimpan<\/strong> di server. Jangan mulai undian baru sebelum hasilnya dicek.<\/div>\r\n        <div class=\"bxsm-rec-msg\"><\/div>\r\n        <div class=\"bxsm-rec-actions\">\r\n          <button type=\"button\" class=\"bxsm-btn bxsm-rec-check\">PERIKSA HASIL<\/button>\r\n          <button type=\"button\" class=\"bxsm-btn bxsm-rec-abandon\" disabled>Lanjutkan tanpa pemenang<\/button>\r\n        <\/div>\r\n        <div class=\"bxsm-rec-help\">\"Periksa Hasil\" mengecek daftar pemenang di server. Jika belum ada pemenang baru (dan token masih aman dipakai), permintaan dikirim ulang dengan token undian yang sama, sehingga server tidak memilih pemenang kedua untuk undian ini. \"Lanjutkan tanpa pemenang\" baru aktif setelah pengecekan tidak menemukan pemenang baru.<\/div>\r\n        <div class=\"bxsm-rec-meta\"><\/div>\r\n      <\/div>\r\n    <\/div>\r\n\r\n    <!-- TOMBOL -->\r\n    <div class=\"bxsm-controls\">\r\n      <button type=\"button\" class=\"bxsm-btn bxsm-draw\" hidden disabled>\r\n        <span class=\"bxsm-draw-label\">MULAI UNDIAN<\/span>\r\n        <span class=\"bxsm-draw-icon\" aria-hidden=\"true\">\r\n          <svg class=\"bxsm-ic-go\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 5.5l6.5 6.5L9 18.5\"\/><\/svg>\r\n          <svg class=\"bxsm-ic-busy\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\"><path d=\"M12 3a9 9 0 1 0 9 9\"\/><\/svg>\r\n        <\/span>\r\n      <\/button>\r\n      <div class=\"bxsm-meta-row\">\r\n      <div class=\"bxsm-count\" hidden>\r\n        <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><circle cx=\"9\" cy=\"8\" r=\"3.4\"\/><path d=\"M2.5 19.2c0-3.4 2.9-5.7 6.5-5.7s6.5 2.3 6.5 5.7c0 .5-.4.8-.8.8H3.3c-.4 0-.8-.3-.8-.8z\"\/><circle cx=\"16.8\" cy=\"9\" r=\"2.7\" opacity=\".7\"\/><path d=\"M16.3 13.6c3 .1 5.2 2.1 5.2 4.9 0 .4-.3.7-.7.7h-3.9c.1-2.2-.2-3.9-.6-5.6z\" opacity=\".7\"\/><\/svg>\r\n        <span><strong class=\"bxsm-count-num\">0<\/strong> <span class=\"bxsm-count-label\">struk siap diundi<\/span><\/span>\r\n      <\/div>\r\n      <button type=\"button\" class=\"bxsm-btn bxsm-refresh\" hidden>Muat ulang data<\/button>\r\n      <\/div>\r\n      <div class=\"bxsm-status\" role=\"status\" aria-live=\"polite\"><\/div>\r\n      <div class=\"bxsm-sr\" aria-live=\"assertive\"><\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- DAFTAR PEMENANG -->\r\n  <div class=\"bxsm-winners\">\r\n    <div class=\"bxsm-winners-head\">\r\n      <span class=\"bxsm-winners-title\">Pemenang sesi ini<\/span>\r\n      <span class=\"bxsm-winners-rule\"><\/span>\r\n      <button type=\"button\" class=\"bxsm-btn bxsm-reset\" hidden>Reset List<\/button>\r\n    <\/div>\r\n    <div class=\"bxsm-winners-list\">\r\n      <div class=\"bxsm-winners-empty\">Belum ada pemenang<\/div>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- ===================== LAYER POPUP (pindah ke <body>) ===================== -->\r\n<div class=\"bxsm bxsm-layer\">\r\n  <div class=\"bxsm-overlay\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Pemenang undian\" aria-hidden=\"true\">\r\n    <canvas class=\"bxsm-sparkle\" aria-hidden=\"true\"><\/canvas>\r\n    <div class=\"bxsm-modal\" tabindex=\"-1\">\r\n      <button type=\"button\" class=\"bxsm-btn bxsm-m-close\" aria-label=\"Tutup\" title=\"Tutup\">&times;<\/button>\r\n      <div class=\"bxsm-m-kicker\">SELAMAT KEPADA PEMENANG!<\/div>\r\n      <div class=\"bxsm-m-no\"><\/div>\r\n      <div class=\"bxsm-m-name\">&mdash;<\/div>\r\n      <div class=\"bxsm-m-struk-box\">\r\n        <div class=\"bxsm-m-label\">Nomor Struk<\/div>\r\n        <div class=\"bxsm-m-struk\">&mdash;<\/div>\r\n      <\/div>\r\n      <div class=\"bxsm-m-fields\">\r\n        <div>\r\n          <div class=\"bxsm-m-label\">Nomor WhatsApp<\/div>\r\n          <div class=\"bxsm-m-wa-row\">\r\n            <div class=\"bxsm-m-value bxsm-m-wa\">&mdash;<\/div>\r\n            <button type=\"button\" class=\"bxsm-btn bxsm-m-copy\">Salin<\/button>\r\n          <\/div>\r\n        <\/div>\r\n        <div class=\"bxsm-m-meta\">\r\n          <div><div class=\"bxsm-m-label\">Produk<\/div><div class=\"bxsm-m-value bxsm-m-produk\">&mdash;<\/div><\/div>\r\n          <div><div class=\"bxsm-m-label\">Waktu Menang<\/div><div class=\"bxsm-m-value bxsm-m-won\">&mdash;<\/div><\/div>\r\n        <\/div>\r\n        <div class=\"bxsm-m-foto-field\">\r\n          <div class=\"bxsm-m-label\">Foto Struk<\/div>\r\n          <img class=\"bxsm-m-foto\" alt=\"Foto struk pemenang\" title=\"Klik untuk perbesar\">\r\n        <\/div>\r\n        <label>\r\n          <span class=\"bxsm-m-label\">Hadiah <span>(opsional)<\/span><\/span>\r\n          <input type=\"text\" class=\"bxsm-input bxsm-m-prize\" maxlength=\"200\" placeholder=\"Contoh: Voucher Rp 100.000\" autocomplete=\"off\">\r\n        <\/label>\r\n      <\/div>\r\n      <div class=\"bxsm-m-footer\">\r\n        <div class=\"bxsm-m-error\" role=\"alert\" hidden><\/div>\r\n        <div class=\"bxsm-m-actions\">\r\n          <button type=\"button\" class=\"bxsm-btn bxsm-m-confirm\">Konfirmasi<\/button>\r\n          <button type=\"button\" class=\"bxsm-btn bxsm-m-cancel\">Batalkan<\/button>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n  <div class=\"bxsm-lightbox\" hidden>\r\n    <img class=\"bxsm-lightbox-img\" alt=\"Foto struk\">\r\n  <\/div>\r\n  <div class=\"bxsm-toast\" role=\"status\" aria-live=\"polite\"><\/div>\r\n<\/div>\r\n\r\n<script>\r\n(function () {\r\n  'use strict';\r\n\r\n  \/* ==========================================================================\r\n     [GANTI] TEKS\r\n     ========================================================================== *\/\r\n  var PUBLIC_STATUS = 'Live draw setiap Jumat pukul 19.00 WIB'; \/\/ teks untuk pengunjung umum (bukan admin)\r\n  var TAGLINE = 'Siapa yang beruntung hari ini?';\r\n  var PUBLIC_WINNERS_TEXT = 'Pemenang diumumkan saat live draw.';\r\n\r\n  \/* ==========================================================================\r\n     ENDPOINT (jangan diubah: sama dengan Snippet 3)\r\n     ========================================================================== *\/\r\n  var API = window.location.origin + '\/wp-json\/swe\/v1';\r\n  var AJAX = window.location.origin + '\/wp-admin\/admin-ajax.php';\r\n\r\n  \/* ==========================================================================\r\n     PENGATURAN ANIMASI & KEAMANAN UNDIAN\r\n     ========================================================================== *\/\r\n  var ROLL_MS = 6000;          \/\/ gulungan terakhir berhenti ~6 detik setelah jawaban server\r\n  var FIRST_STOP_MS = 3300;    \/\/ gulungan pertama berhenti\r\n  var SETTLE_MS = 260;         \/\/ efek \"klik\" kecil saat berhenti\r\n  var OVERSHOOT = 0.16;        \/\/ seberapa jauh lewat sedikit sebelum kembali (dalam tinggi baris)\r\n  var EASE_POWER = 2.7;        \/\/ kurva perlambatan\r\n  var SPEED = 0.024;           \/\/ kecepatan putar (baris per milidetik)\r\n  var RAMP_MS = 380;           \/\/ waktu percepatan awal\r\n  var REEL_MAX_UNITS = 16;     \/\/ nomor sampai 16 karakter = 1 gulungan per karakter (SAMA di desktop & HP);\r\n                               \/\/ lebih panjang dari itu baru memakai tampilan lebar\r\n  var IDLE_REELS = 6;          \/\/ jumlah gulungan hiasan (publik \/ sebelum data dimuat)\r\n  var DRAW_TIMEOUT_MS = 15000;\r\n  var API_TIMEOUT_MS = 20000;\r\n  var SAME_TOKEN_SAFE_MS = 12 * 60 * 1000;  \/\/ server menyimpan token->pemenang 15 menit; kita pakai 12\r\n  var STALE_RECOVERY_MS = 6 * 60 * 60 * 1000;\r\n  var RECOVERY_KEY = 'bxgg_slot_recovery_v1';\r\n\r\n  \/* ==========================================================================\r\n     ELEMEN (dicari relatif terhadap widget ini, aman jika Elementor me-render ulang)\r\n     ========================================================================== *\/\r\n  var script = document.currentScript;\r\n  var page = null;\r\n  var layer = null;\r\n  if (script) {\r\n    var sib = script.previousElementSibling;\r\n    while (sib) {\r\n      if (!layer && sib.classList && sib.classList.contains('bxsm-layer')) layer = sib;\r\n      if (sib.classList && sib.classList.contains('bxsm-page')) { page = sib; break; }\r\n      sib = sib.previousElementSibling;\r\n    }\r\n  }\r\n  if (!page) { var pages = document.querySelectorAll('.bxsm-page'); page = pages[pages.length - 1]; }\r\n  if (!layer) {\r\n    \/\/ Editor Elementor: pilih layer milik widget ini (yang belum dipindah ke <body>), bukan sisa render lama.\r\n    var layers = Array.prototype.filter.call(document.querySelectorAll('.bxsm-layer'), function (l) { return l.parentElement !== document.body; });\r\n    layer = layers[layers.length - 1] || null;\r\n  }\r\n  if (!page || !layer) return;\r\n\r\n  \/\/ Popup dipindah ke <body> supaya transform\/overflow dari Elementor tidak memotongnya.\r\n  Array.prototype.forEach.call(document.querySelectorAll('body > .bxsm-layer'), function (n) { if (n !== layer) n.remove(); });\r\n  document.body.appendChild(layer);\r\n\r\n  function qp(sel) { return page.querySelector(sel); }\r\n  function ql(sel) { return layer.querySelector(sel); }\r\n\r\n  var E = {\r\n    machine: qp('.bxsm-machine'),\r\n    reelsWrap: qp('.bxsm-reels-wrap'),\r\n    reels: qp('.bxsm-reels'),\r\n    wideFinal: qp('.bxsm-wide-final'),\r\n    tagline: qp('.bxsm-tagline'),\r\n    drawing: qp('.bxsm-drawing'),\r\n    result: qp('.bxsm-result'),\r\n    resultNo: qp('.bxsm-result-no'),\r\n    recovery: qp('.bxsm-recovery'),\r\n    recMsg: qp('.bxsm-rec-msg'),\r\n    recMeta: qp('.bxsm-rec-meta'),\r\n    recCheck: qp('.bxsm-rec-check'),\r\n    recAbandon: qp('.bxsm-rec-abandon'),\r\n    drawBtn: qp('.bxsm-draw'),\r\n    drawLabel: qp('.bxsm-draw-label'),\r\n    count: qp('.bxsm-count'),\r\n    countNum: qp('.bxsm-count-num'),\r\n    countLabel: qp('.bxsm-count-label'),\r\n    status: qp('.bxsm-status'),\r\n    refresh: qp('.bxsm-refresh'),\r\n    sr: qp('.bxsm-sr'),\r\n    winnersList: qp('.bxsm-winners-list'),\r\n    winnersEmpty: qp('.bxsm-winners-empty'),\r\n    resetBtn: qp('.bxsm-reset'),\r\n    logo: qp('.bxsm-logo'),\r\n    logoWrap: qp('.bxsm-logo-wrap'),\r\n    modal: ql('.bxsm-overlay'),\r\n    modalBox: ql('.bxsm-modal'),\r\n    sparkle: ql('.bxsm-sparkle'),\r\n    mClose: ql('.bxsm-m-close'),\r\n    mNo: ql('.bxsm-m-no'),\r\n    mName: ql('.bxsm-m-name'),\r\n    mStruk: ql('.bxsm-m-struk'),\r\n    mWa: ql('.bxsm-m-wa'),\r\n    mCopy: ql('.bxsm-m-copy'),\r\n    mProduk: ql('.bxsm-m-produk'),\r\n    mWon: ql('.bxsm-m-won'),\r\n    mFotoField: ql('.bxsm-m-foto-field'),\r\n    mFoto: ql('.bxsm-m-foto'),\r\n    mPrize: ql('.bxsm-m-prize'),\r\n    mError: ql('.bxsm-m-error'),\r\n    mConfirm: ql('.bxsm-m-confirm'),\r\n    mCancel: ql('.bxsm-m-cancel'),\r\n    lightbox: ql('.bxsm-lightbox'),\r\n    lightboxImg: ql('.bxsm-lightbox-img'),\r\n    toast: ql('.bxsm-toast')\r\n  };\r\n\r\n  \/* ==========================================================================\r\n     STATE\r\n     ========================================================================== *\/\r\n  var S = {\r\n    isAdmin: false,\r\n    publicMode: false,\r\n    sessionExpired: false,\r\n    poolLoaded: false,\r\n    winnersSynced: false,\r\n    loadingPool: false,\r\n    loadError: false,\r\n    total: 0,               \/\/ jumlah struk siap diundi (dari server)\r\n    segments: [],           \/\/ [{id, no_struk}] dari GET \/pool (hanya untuk tampilan, tidak untuk memilih)\r\n    info: '',\r\n    typicalLen: 0,\r\n    idleShape: '',          \/\/ pola nomor struk paling umum, mis. '##-######\/##' (hanya untuk tampilan awal)\r\n    winners: [],            \/\/ pemenang sesi ini dari GET \/winners (terbaru di atas)\r\n    hidden: {},             \/\/ \"Reset List\": id yang disembunyikan di layar ini saja\r\n    drawing: false,         \/\/ permintaan \/draw sedang berjalan ATAU animasi berjalan\r\n    detailLoading: false,\r\n    modalOpen: false,\r\n    modalBusy: false,       \/\/ false | 'prize' | 'cancel'\r\n    modalWinner: null,\r\n    waFull: '',             \/\/ nomor WA lengkap, hanya untuk tombol Salin\r\n    recovery: null,         \/\/ {token, startedAt, snapshot, checkedNoWinner, stale, message}\r\n    recoveryChecking: false,\r\n    lastResult: null,       \/\/ pemenang yang sedang tampil di mesin\r\n    hasDrawn: false\r\n  };\r\n\r\n  \/* ==========================================================================\r\n     UTIL\r\n     ========================================================================== *\/\r\n  function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }\r\n  function str(v) { return (v === null || v === undefined) ? '' : String(v); }\r\n  function fmtCount(n) { try { return new Intl.NumberFormat('id-ID').format(n); } catch (e) { return String(n); } }\r\n  var reduceMq = window.matchMedia ? window.matchMedia('(prefers-reduced-motion: reduce)') : null;\r\n  function reducedMotion() { return !!(reduceMq && reduceMq.matches); }\r\n\r\n  \/\/ Nomor struk dipecah per karakter yang terlihat (aman untuk huruf non-ASCII), TANPA konversi angka.\r\n  var segmenter = null;\r\n  try { if (window.Intl && Intl.Segmenter) segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' }); } catch (e) { segmenter = null; }\r\n  function graphemes(s) {\r\n    s = str(s);\r\n    if (segmenter) { var out = []; var it = segmenter.segment(s)[Symbol.iterator](); var x; while (!(x = it.next()).done) out.push(x.value.segment); return out; }\r\n    return Array.from(s);\r\n  }\r\n  var SPIN_RE, NUM_RE;\r\n  try { SPIN_RE = new RegExp('^[\\\\p{L}\\\\p{N}]', 'u'); NUM_RE = new RegExp('^\\\\p{N}', 'u'); } catch (e) { SPIN_RE = \/^[A-Za-z0-9]\/; NUM_RE = \/^[0-9]\/; }\r\n  var DIGITS = '0123456789'.split('');\r\n  var UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');\r\n  var LOWER = 'abcdefghijklmnopqrstuvwxyz'.split('');\r\n  \/\/ Huruf hiasan saat menggulung (tanpa M\/W yang sangat lebar). Huruf pemenang tetap ditampilkan apa adanya.\r\n  var UPPER_DECOR = 'ABCDEFGHJKLNPRSTUVXYZ'.split('');\r\n  var LOWER_DECOR = 'abcdefghknopqrstuvxyz'.split('');\r\n  function alphabetFor(ch) {\r\n    if (\/^[0-9]$\/.test(ch)) return DIGITS;\r\n    if (\/^[A-Z]$\/.test(ch)) return UPPER_DECOR;\r\n    if (\/^[a-z]$\/.test(ch)) return LOWER_DECOR;\r\n    return [ch].concat(NUM_RE.test(ch) ? DIGITS : UPPER_DECOR);\r\n  }\r\n  function randInt(n) { return Math.floor(Math.random() * n); }\r\n  function pickOther(alpha, not) {\r\n    if (alpha.length < 2) return alpha[0];\r\n    for (var i = 0; i < 20; i++) { var c = alpha[randInt(alpha.length)]; if (c !== not) return c; }\r\n    return alpha[0] === not ? alpha[1] : alpha[0];\r\n  }\r\n  function hash32(a, b) {\r\n    var h = Math.imul(a ^ 0x9e3779b9, 0x85ebca6b) ^ Math.imul((b | 0) + 0x632be5ab, 0xc2b2ae35);\r\n    h ^= h >>> 15; h = Math.imul(h, 0x2c1b3c6d); h ^= h >>> 12; h = Math.imul(h, 0x297a2d39); h ^= h >>> 15;\r\n    return h >>> 0;\r\n  }\r\n  function safeImgUrl(u) {\r\n    u = str(u).trim();\r\n    if (!u) return '';\r\n    try { var url = new URL(u, window.location.href); return (url.protocol === 'https:' || url.protocol === 'http:') ? url.href : ''; } catch (e) { return ''; }\r\n  }\r\n  function timeWib(ms) {\r\n    try { return new Date(ms).toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit', timeZone: 'Asia\/Jakarta' }) + ' WIB'; }\r\n    catch (e) { return new Date(ms).toLocaleTimeString(); }\r\n  }\r\n  function newToken() {\r\n    if (window.crypto && crypto.randomUUID) return crypto.randomUUID();\r\n    var a = new Uint32Array(4);\r\n    if (window.crypto && crypto.getRandomValues) crypto.getRandomValues(a); else for (var i = 0; i < 4; i++) a[i] = Math.random() * 4294967296;\r\n    return Array.prototype.map.call(a, function (n) { return ('00000000' + n.toString(16)).slice(-8); }).join('-');\r\n  }\r\n  \/\/ Data pemenang dari server -> objek aman (semua teks tetap string, no_struk tidak diubah sedikit pun).\r\n  function normalizeWinner(w) {\r\n    w = w || {};\r\n    var no = Number(w.no);\r\n    return {\r\n      id: Number(w.id),\r\n      no: isFinite(no) ? no : 0,\r\n      nama: str(w.nama),\r\n      no_struk: str(w.no_struk),\r\n      produk: Array.isArray(w.produk) ? w.produk.map(str) : [],\r\n      foto_struk_url: str(w.foto_struk_url),\r\n      no_wa_masked: str(w.no_wa_masked),\r\n      no_wa: str(w.no_wa),\r\n      prize: str(w.prize),\r\n      won_at: str(w.won_at)\r\n    };\r\n  }\r\n  function sortWinners(list) { return list.slice().sort(function (a, b) { return (b.no - a.no) || (b.id - a.id); }); }\r\n\r\n  \/* ==========================================================================\r\n     API (nonce runtime + header X-WP-Nonce, retry 1x dengan nonce baru pada 401\/403)\r\n     ========================================================================== *\/\r\n  var restNonce = '';\r\n  var noncePromise = null;\r\n  function makeErr(msg, sent) { var e = new Error(msg); e.sent = !!sent; return e; }\r\n\r\n  function getNonce(force) {\r\n    if (restNonce && !force) return Promise.resolve(restNonce);\r\n    if (noncePromise && !force) return noncePromise;\r\n    var p = (async function () {\r\n      var r;\r\n      try {\r\n        r = await fetch(AJAX, {\r\n          method: 'POST', credentials: 'same-origin', cache: 'no-store',\r\n          body: new URLSearchParams({ action: 'bxgg_spin_nonce' })\r\n        });\r\n      } catch (e) { throw makeErr('network', false); }\r\n      if (r.status >= 500) throw makeErr('network', false);\r\n      var d = null;\r\n      try { d = await r.json(); } catch (e) { d = null; } \/\/ logged out -> \"0\"\r\n      if (!d || !d.success || !d.nonce) { restNonce = ''; throw makeErr('not-admin', false); }\r\n      restNonce = String(d.nonce);\r\n      return restNonce;\r\n    })();\r\n    noncePromise = p;\r\n    p.then(function () { if (noncePromise === p) noncePromise = null; }, function () { if (noncePromise === p) noncePromise = null; });\r\n    return p;\r\n  }\r\n\r\n  \/\/ Memanggil \/wp-json\/swe\/v1\/... dan mengembalikan {status, data}.\r\n  \/\/ Error: 'network' \/ 'timeout' (e.sent = permintaan mungkin sudah sampai server), 'not-admin'.\r\n  async function apiCall(path, body, opts) {\r\n    opts = opts || {};\r\n    var nonce = await getNonce(false);\r\n    var init = { method: body ? 'POST' : 'GET', credentials: 'same-origin', cache: 'no-store', headers: { 'X-WP-Nonce': nonce } };\r\n    if (body) { init.headers['Content-Type'] = 'application\/json'; init.body = JSON.stringify(body); }\r\n    var ctrl = null, timer = 0;\r\n    if (window.AbortController) {\r\n      ctrl = new AbortController();\r\n      init.signal = ctrl.signal;\r\n      timer = setTimeout(function () { ctrl.abort(); }, opts.timeout || API_TIMEOUT_MS);\r\n    }\r\n    var r, d = null;\r\n    try {\r\n      try { r = await fetch(API + path, init); }\r\n      catch (e) { throw makeErr(ctrl && ctrl.signal.aborted ? 'timeout' : 'network', true); }\r\n      try { d = await r.json(); } catch (e) { d = null; }\r\n    } finally { clearTimeout(timer); }\r\n    if (r.status === 401 || r.status === 403) {\r\n      if (opts.retried) throw makeErr('not-admin', false);\r\n      await getNonce(true);\r\n      return apiCall(path, body, Object.assign({}, opts, { retried: true }));\r\n    }\r\n    return { status: r.status, data: (d && typeof d === 'object') ? d : null };\r\n  }\r\n  async function api(path, body, opts) {\r\n    var res = await apiCall(path, body, opts);\r\n    return res.data || { success: false, message: 'Respons server tidak valid (HTTP ' + res.status + ').' };\r\n  }\r\n\r\n  \/* ==========================================================================\r\n     MESIN SLOT (DOM terbatas: 5 sel per gulungan, berapa pun jumlah peserta)\r\n     ========================================================================== *\/\r\n  var machine = (function () {\r\n    var host = E.reels;\r\n    var reels = [];\r\n    var key = '';\r\n    var mode = 'reels';\r\n    var raf = 0;\r\n    var spinning = false;\r\n    var spinStartedAt = 0;\r\n    var landing = null;\r\n    var finalText = null;     \/\/ teks pemenang yang sedang tampil (atau null)\r\n    var idleWideText = '';\r\n    var measureCtx = null;\r\n\r\n    function makeReel(alpha, wide) {\r\n      var el = document.createElement('div');\r\n      el.className = 'bxsm-reel' + (wide ? ' is-wide' : '');\r\n      var strip = document.createElement('div');\r\n      strip.className = 'bxsm-strip';\r\n      var cells = [];\r\n      for (var c = 0; c < 5; c++) {\r\n        var cell = document.createElement('div');\r\n        cell.className = 'bxsm-cell';\r\n        strip.appendChild(cell);\r\n        cells.push(cell);\r\n      }\r\n      el.appendChild(strip);\r\n      return { el: el, strip: strip, cells: cells, alpha: alpha, seed: (Math.random() * 4294967296) >>> 0, ov: new Map(), pos: 0, shown: null, plan: null, prev: null, fast: false, target: '' };\r\n    }\r\n    function sym(r, k) {\r\n      if (r.ov.has(k)) return r.ov.get(k);\r\n      var a = r.alpha;\r\n      return a.length ? a[hash32(r.seed, k) % a.length] : '';\r\n    }\r\n    \/\/ Sel 0..4 = baris -2..+2; baris tengah (sel 2) = posisi bulat; strip digeser f*20% (1 baris).\r\n    function render(r) {\r\n      var n = Math.floor(r.pos);\r\n      var f = r.pos - n;\r\n      if (n !== r.shown) {\r\n        for (var c = 0; c < 5; c++) r.cells[c].textContent = sym(r, n + 2 - c);\r\n        r.shown = n;\r\n      }\r\n      r.strip.style.transform = 'translate3d(0,' + (f * 20).toFixed(4) + '%,0)';\r\n    }\r\n    function setFast(r, on) { if (r.fast !== on) { r.fast = on; r.el.classList.toggle('is-fast', on); } }\r\n\r\n    function posSpin(p, t) {\r\n      var dt = Math.max(0, t - p.t0);\r\n      if (p.atSpeed) return p.p0 + SPEED * dt;\r\n      if (dt < RAMP_MS) return p.p0 + SPEED * dt * dt \/ (2 * RAMP_MS);\r\n      return p.p0 + SPEED * RAMP_MS \/ 2 + SPEED * (dt - RAMP_MS);\r\n    }\r\n    function velSpin(p, t) { var dt = Math.max(0, t - p.t0); return p.atSpeed ? SPEED : Math.min(1, dt \/ RAMP_MS) * SPEED; }\r\n    function posAt(r, t) {\r\n      var p = r.plan;\r\n      if (!p) return r.pos;\r\n      if (p.type === 'spin') return posSpin(p, t);\r\n      if (t < p.t0) return posSpin(r.prev, t);\r\n      var dt = t - p.t0;\r\n      if (dt < p.D) { var tau = dt \/ p.D; return p.p0 + (p.Kp - p.p0) * (1 - Math.pow(1 - tau, p.pw)); }\r\n      var u = (dt - p.D) \/ SETTLE_MS;\r\n      if (u < 1) { var e = u < 0.5 ? 2 * u * u : 1 - Math.pow(-2 * u + 2, 2) \/ 2; return p.Kp + (p.K - p.Kp) * e; }\r\n      return p.K;\r\n    }\r\n    function velAt(r, t) {\r\n      var p = r.plan;\r\n      if (!p) return 0;\r\n      if (p.type === 'spin') return velSpin(p, t);\r\n      if (t < p.t0) return velSpin(r.prev, t);\r\n      var dt = t - p.t0;\r\n      if (dt >= p.D) return 0;\r\n      return (p.Kp - p.p0) * p.pw * Math.pow(1 - dt \/ p.D, p.pw - 1) \/ p.D;\r\n    }\r\n    function landDone(r, t) { var p = r.plan; return !p || (p.type === 'land' && t >= p.t0 + p.D + SETTLE_MS); }\r\n\r\n    function frame() {\r\n      raf = 0;\r\n      var t = performance.now();\r\n      var allDone = true;\r\n      for (var i = 0; i < reels.length; i++) {\r\n        var r = reels[i];\r\n        if (r.plan) {\r\n          r.pos = posAt(r, t);\r\n          setFast(r, velAt(r, t) > 0.009);\r\n          if (r.plan.type === 'land' && landDone(r, t)) {\r\n            r.pos = r.plan.K; r.plan = null; r.prev = null; setFast(r, false); flash(r.el);\r\n          } else {\r\n            allDone = false;\r\n          }\r\n        }\r\n        render(r);\r\n      }\r\n      if (landing && allDone) { completeLanding(); return; }\r\n      if (spinning || landing) raf = requestAnimationFrame(frame);\r\n    }\r\n    function loop() { if (!raf) raf = requestAnimationFrame(frame); }\r\n    function flash(el) {\r\n      if (reducedMotion()) return;\r\n      el.classList.remove('is-stopped');\r\n      void el.offsetWidth;\r\n      el.classList.add('is-stopped');\r\n    }\r\n\r\n    function stopAll() {\r\n      if (raf) cancelAnimationFrame(raf);\r\n      raf = 0;\r\n      spinning = false;\r\n      if (landing) { clearTimeout(landing.timer); landing = null; }\r\n      reels.forEach(function (r) { r.plan = null; r.prev = null; setFast(r, false); });\r\n      E.machine.classList.remove('is-waiting');\r\n    }\r\n\r\n    function build(layout) {\r\n      if (raf) cancelAnimationFrame(raf);\r\n      raf = 0;\r\n      host.textContent = '';\r\n      reels = [];\r\n      mode = layout.mode;\r\n      key = layout.key;\r\n      E.reelsWrap.classList.remove('is-wrapped');\r\n      E.reelsWrap.classList.toggle('is-wide', mode === 'wide');\r\n      if (mode === 'wide') {\r\n        var w = makeReel(layout.samples && layout.samples.length ? layout.samples : ['?'], true);\r\n        host.appendChild(w.el);\r\n        reels.push(w);\r\n        E.machine.style.setProperty('--bxsm-units', '999');\r\n        E.machine.style.setProperty('--bxsm-cols', '1');\r\n      } else {\r\n        var units = 0;\r\n        layout.glyphs.forEach(function (g, i) { units += layout.spin[i] ? 1.32 : (\/^\\s+$\/.test(g) ? 0.26 : 0.42); });\r\n        E.machine.style.setProperty('--bxsm-units', units.toFixed(3));\r\n        E.machine.style.setProperty('--bxsm-cols', String(layout.glyphs.length));\r\n        layout.glyphs.forEach(function (g, i) {\r\n          if (layout.spin[i]) {\r\n            var r = makeReel(layout.alpha[i] || DIGITS, false);\r\n            host.appendChild(r.el);\r\n            reels.push(r);\r\n          } else {\r\n            var s = document.createElement('div');\r\n            var blank = \/^\\s+$\/.test(g);\r\n            s.className = 'bxsm-sep' + (blank ? ' is-space' : '');\r\n            s.textContent = blank ? '' : g;\r\n            s.setAttribute('data-g', g);\r\n            host.appendChild(s);\r\n          }\r\n        });\r\n      }\r\n    }\r\n\r\n    \/\/ Jumlah gulungan hanya ditentukan oleh panjang nomor struk, TIDAK oleh lebar layar,\r\n    \/\/ jadi HP (IG Live) dan desktop selalu menampilkan jumlah gulungan yang sama.\r\n    function maxUnits() { return REEL_MAX_UNITS; }\r\n\r\n    \/\/ Ukuran angka: sebesar mungkin tanpa terpotong, mengikuti lebar gulungan (penting di HP).\r\n    function fitReels() {\r\n      if (mode !== 'reels' || !reels.length) return;\r\n      var r0 = reels[0];\r\n      var reelW = r0.el.clientWidth;\r\n      var rowPx = r0.cells[2].getBoundingClientRect().height;\r\n      if (!reelW || !rowPx) return;\r\n      var chars = { '?': 1 };\r\n      reels.forEach(function (r) {\r\n        r.alpha.forEach(function (c) { chars[c] = 1; });\r\n        r.ov.forEach(function (v) { chars[v] = 1; });\r\n        if (r.target) chars[r.target] = 1;\r\n      });\r\n      if (!measureCtx) measureCtx = document.createElement('canvas').getContext('2d');\r\n      measureCtx.font = '800 100px ' + getComputedStyle(r0.cells[2]).fontFamily;\r\n      var widest = 0;\r\n      Object.keys(chars).forEach(function (c) { widest = Math.max(widest, measureCtx.measureText(c).width); });\r\n      if (!widest) return;\r\n      var fs = Math.min(rowPx * 0.74, reelW * 0.84 * 100 \/ widest);\r\n      host.style.setProperty('--bxsm-cell-fs', Math.max(10, fs).toFixed(2) + 'px');\r\n    }\r\n    function idleLayout(n) {\r\n      n = Math.max(1, n | 0);\r\n      var glyphs = [], spin = [], alpha = [];\r\n      for (var i = 0; i < n; i++) { glyphs.push('?'); spin.push(true); alpha.push(DIGITS); }\r\n      return { mode: 'reels', glyphs: glyphs, spin: spin, alpha: alpha, key: 'reels:' + new Array(n + 1).join('#') };\r\n    }\r\n    function wideIdleLayout(len, samples) {\r\n      return { mode: 'wide', key: 'wide', samples: samples || [], text: new Array(Math.max(4, len) + 1).join('?') };\r\n    }\r\n    \/\/ Tampilan untuk teks tertentu: gulungan per karakter jika muat, selain itu tampilan lebar.\r\n    function layoutForText(text, samples, idle) {\r\n      var glyphs = graphemes(text);\r\n      var spin = glyphs.map(function (g) { return SPIN_RE.test(g) || (!!idle && g === '?'); });\r\n      var nSpin = spin.filter(Boolean).length;\r\n      var units = nSpin + 0.45 * (glyphs.length - nSpin);\r\n      if (!nSpin || units > maxUnits()) return { mode: 'wide', key: 'wide', samples: samples || [], text: text };\r\n      return {\r\n        mode: 'reels',\r\n        glyphs: glyphs,\r\n        spin: spin,\r\n        alpha: glyphs.map(function (g, i) { return spin[i] ? (idle ? DIGITS : alphabetFor(g)) : null; }),\r\n        key: 'reels:' + glyphs.map(function (g, i) { return spin[i] ? '#' : 'S' + g; }).join('')\r\n      };\r\n    }\r\n    function bullets(text) { return new Array(Math.min(Math.max(graphemes(text).length, 4), 24) + 1).join('\u2022'); }\r\n    function lookAlikes(text) {\r\n      \/\/ Hanya hiasan gulungan lebar saat peserta < 6; tidak pernah menjadi hasil akhir.\r\n      var g = graphemes(text), out = [];\r\n      for (var k = 0; k < 12; k++) {\r\n        out.push(g.map(function (c) {\r\n          if (\/^[0-9]$\/.test(c)) return DIGITS[randInt(10)];\r\n          if (\/^[A-Z]$\/.test(c)) return UPPER_DECOR[randInt(UPPER_DECOR.length)];\r\n          if (\/^[a-z]$\/.test(c)) return LOWER_DECOR[randInt(LOWER_DECOR.length)];\r\n          return c;\r\n        }).join(''));\r\n      }\r\n      return out;\r\n    }\r\n\r\n    function showIdle(layout) {\r\n      stopAll();\r\n      if (layout.key !== key || !reels.length) build(layout);\r\n      finalText = null;\r\n      E.reelsWrap.classList.remove('is-wrapped');\r\n      reels.forEach(function (r) {\r\n        r.ov.clear();\r\n        if (mode === 'wide') {\r\n          r.alpha = layout.samples && layout.samples.length ? layout.samples : ['?'];\r\n          var b = bullets(layout.text);\r\n          r.ov.set(0, layout.text); r.ov.set(1, b); r.ov.set(2, b); r.ov.set(-1, b); r.ov.set(-2, b);\r\n        } else {\r\n          r.alpha = DIGITS;\r\n          r.ov.set(0, '?');\r\n        }\r\n        r.pos = 0; r.shown = null;\r\n        render(r);\r\n      });\r\n      idleWideText = mode === 'wide' ? layout.text : '';\r\n      if (mode === 'wide') fitWide(layout.text); else fitReels();\r\n      host.setAttribute('aria-label', 'Nomor struk belum diundi');\r\n    }\r\n\r\n    function startSpin() {\r\n      finalText = null;\r\n      E.reelsWrap.classList.remove('is-wrapped');\r\n      if (reducedMotion()) { E.machine.classList.add('is-waiting'); return; }\r\n      var t = performance.now();\r\n      reels.forEach(function (r) { r.prev = null; r.plan = { type: 'spin', t0: t, p0: r.pos, atSpeed: false }; });\r\n      spinning = true;\r\n      spinStartedAt = t;\r\n      loop();\r\n    }\r\n\r\n    \/\/ Menggulung lalu berhenti TEPAT pada teks dari server.\r\n    function landOn(text, samples) {\r\n      text = str(text);\r\n      return new Promise(function (resolve) {\r\n        var layout = layoutForText(text, samples);\r\n        E.machine.classList.remove('is-waiting');\r\n        if (reducedMotion()) {\r\n          placeFinal(layout, text, samples);\r\n          setTimeout(resolve, 650);\r\n          return;\r\n        }\r\n        var t = performance.now();\r\n        if (layout.key !== key || !spinning || !reels.length) {\r\n          var carrySpeed = spinning;\r\n          stopAll();\r\n          build(layout);\r\n          host.classList.remove('is-enter'); void host.offsetWidth; host.classList.add('is-enter');\r\n          reels.forEach(function (r) { r.pos = Math.random() * 50; r.shown = null; r.plan = { type: 'spin', t0: t, p0: r.pos, atSpeed: carrySpeed }; });\r\n          spinning = true;\r\n          if (!carrySpeed) spinStartedAt = t;\r\n        }\r\n        if (mode === 'wide') {\r\n          reels[0].alpha = samples && samples.length >= 6 ? samples : lookAlikes(text);\r\n          reels[0].target = text;\r\n          fitWide(text);\r\n        } else {\r\n          var j = 0;\r\n          layout.glyphs.forEach(function (g, i) { if (layout.spin[i]) { reels[j].alpha = layout.alpha[i]; reels[j].target = g; j++; } });\r\n          fitReels();\r\n        }\r\n        var m = reels.length;\r\n        var endAt = t;\r\n        reels.forEach(function (r, idx) {\r\n          var sp = r.plan;\r\n          var tL = sp.atSpeed ? t : Math.max(t, sp.t0 + RAMP_MS);\r\n          var stopAt = t + (m === 1 ? ROLL_MS : FIRST_STOP_MS + (ROLL_MS - FIRST_STOP_MS) * idx \/ (m - 1));\r\n          var D = Math.max(1200, stopAt - tL - SETTLE_MS);\r\n          var p0 = posSpin(sp, tL);\r\n          var K = Math.ceil(p0 + SPEED * D \/ EASE_POWER);\r\n          var Kp = K + OVERSHOOT;\r\n          r.ov.clear();\r\n          r.ov.set(K, r.target);\r\n          if (mode === 'wide') {\r\n            var b = bullets(text);\r\n            r.ov.set(K + 1, b); r.ov.set(K + 2, b); r.ov.set(K - 1, b); r.ov.set(K - 2, b);\r\n          } else {\r\n            r.ov.set(K + 1, pickOther(r.alpha, r.target)); r.ov.set(K + 2, pickOther(r.alpha, r.target));\r\n            r.ov.set(K - 1, pickOther(r.alpha, r.target)); r.ov.set(K - 2, pickOther(r.alpha, r.target));\r\n          }\r\n          r.prev = sp;\r\n          r.plan = { type: 'land', t0: tL, p0: p0, D: D, K: K, Kp: Kp, pw: SPEED * D \/ (Kp - p0) };\r\n          endAt = Math.max(endAt, tL + D + SETTLE_MS);\r\n        });\r\n        landing = { resolve: resolve, text: text, layout: layout, samples: samples, timer: 0 };\r\n        \/\/ Cadangan: jika tab di background (requestAnimationFrame berhenti), tetap selesai tepat waktu.\r\n        landing.timer = setTimeout(function () { if (landing) completeLanding(); }, Math.max(0, endAt - performance.now()) + 1200);\r\n        loop();\r\n      });\r\n    }\r\n\r\n    function completeLanding() {\r\n      if (!landing) return;\r\n      var L = landing;\r\n      landing = null;\r\n      clearTimeout(L.timer);\r\n      if (raf) cancelAnimationFrame(raf);\r\n      raf = 0;\r\n      reels.forEach(function (r) {\r\n        if (r.plan && r.plan.type === 'land') r.pos = r.plan.K;\r\n        r.plan = null; r.prev = null; setFast(r, false);\r\n        r.shown = null;\r\n        render(r);\r\n      });\r\n      spinning = false;\r\n      afterFinal(L.text, L.layout, L.samples);\r\n      L.resolve();\r\n    }\r\n\r\n    \/\/ Tanpa animasi (reduced motion \/ cadangan jika animasi gagal).\r\n    function placeFinal(layout, text, samples) {\r\n      stopAll();\r\n      layout = layout || layoutForText(text, samples);\r\n      if (layout.key !== key || !reels.length) build(layout);\r\n      if (mode === 'wide') {\r\n        var r0 = reels[0];\r\n        var b = bullets(text);\r\n        r0.alpha = samples && samples.length >= 6 ? samples : lookAlikes(text);\r\n        r0.ov.clear(); r0.ov.set(0, text); r0.ov.set(1, b); r0.ov.set(2, b); r0.ov.set(-1, b); r0.ov.set(-2, b);\r\n        r0.pos = 0; r0.shown = null; render(r0);\r\n        fitWide(text);\r\n      } else {\r\n        var j = 0;\r\n        layout.glyphs.forEach(function (g, i) {\r\n          if (!layout.spin[i]) return;\r\n          var r = reels[j++];\r\n          r.alpha = layout.alpha[i];\r\n          r.ov.clear(); r.ov.set(0, g);\r\n          r.ov.set(1, pickOther(r.alpha, g)); r.ov.set(2, pickOther(r.alpha, g)); r.ov.set(-1, pickOther(r.alpha, g)); r.ov.set(-2, pickOther(r.alpha, g));\r\n          r.pos = 0; r.shown = null; render(r);\r\n        });\r\n      }\r\n      afterFinal(text, layout, samples);\r\n    }\r\n\r\n    function afterFinal(text, layout, samples) {\r\n      finalText = text;\r\n      fitReels();\r\n      host.setAttribute('aria-label', 'Nomor struk pemenang: ' + text);\r\n      if (mode === 'wide' && !fitWide(text)) showWrapped(text);\r\n      \/\/ Pemeriksaan akhir: yang tampil HARUS sama persis dengan data server.\r\n      if (currentText() !== text) showWrapped(text);\r\n    }\r\n    function showWrapped(text) {\r\n      E.wideFinal.textContent = text;\r\n      E.reelsWrap.classList.add('is-wrapped');\r\n    }\r\n\r\n    \/\/ Ukuran huruf tampilan lebar agar nomor panjang tetap muat satu baris (jika mungkin).\r\n    function fitWide(text) {\r\n      if (mode !== 'wide' || !reels.length) return true;\r\n      var r = reels[0];\r\n      var cs = getComputedStyle(r.cells[2]);\r\n      var rowPx = r.cells[2].getBoundingClientRect().height || 80;\r\n      var base = rowPx * 0.56;\r\n      var avail = r.el.clientWidth - base; \/\/ padding 0.5em kiri + kanan\r\n      if (avail <= 0) return true;\r\n      if (!measureCtx) measureCtx = document.createElement('canvas').getContext('2d');\r\n      measureCtx.font = '800 ' + base + 'px ' + cs.fontFamily;\r\n      var wpx = measureCtx.measureText(text).width + graphemes(text).length * base * 0.03;\r\n      var fs = wpx > avail ? base * avail \/ wpx * 0.97 : base;\r\n      var fits = fs >= 14;\r\n      host.style.setProperty('--bxsm-wide-fs', Math.max(14, Math.min(base, fs)).toFixed(2) + 'px');\r\n      return fits;\r\n    }\r\n\r\n    \/\/ Teks yang benar-benar tampil di baris tengah (untuk pemeriksaan & aksesibilitas).\r\n    function currentText() {\r\n      if (E.reelsWrap.classList.contains('is-wrapped')) return E.wideFinal.textContent;\r\n      var out = '', ri = 0;\r\n      Array.prototype.forEach.call(host.children, function (node) {\r\n        if (node.classList.contains('bxsm-reel')) { var r = reels[ri++]; out += sym(r, Math.round(r.pos)); }\r\n        else out += node.getAttribute('data-g') || '';\r\n      });\r\n      return out;\r\n    }\r\n\r\n    function onResize() {\r\n      fitReels();\r\n      if (landing || spinning) { if (mode === 'wide' && landing) fitWide(landing.text); return; }\r\n      if (finalText !== null) {\r\n        if (mode === 'wide') {\r\n          E.reelsWrap.classList.remove('is-wrapped');\r\n          if (!fitWide(finalText)) showWrapped(finalText);\r\n        }\r\n        return;\r\n      }\r\n      if (mode === 'wide' && idleWideText) fitWide(idleWideText);\r\n    }\r\n\r\n    return {\r\n      layoutForText: layoutForText,\r\n      idleLayout: idleLayout,\r\n      wideIdleLayout: wideIdleLayout,\r\n      maxUnits: maxUnits,\r\n      showIdle: showIdle,\r\n      startSpin: startSpin,\r\n      landOn: landOn,\r\n      placeFinal: function (text) { placeFinal(null, str(text), []); },\r\n      currentText: currentText,\r\n      isIdle: function () { return !landing && !spinning && finalText === null; },\r\n      currentKey: function () { return key; },\r\n      onResize: onResize\r\n    };\r\n  })();\r\n\r\n  \/* ==========================================================================\r\n     TAMPILAN\r\n     ========================================================================== *\/\r\n  function setStatus(text, kind) {\r\n    E.status.textContent = text || '';\r\n    E.status.className = 'bxsm-status' + (kind ? ' is-' + kind : '');\r\n  }\r\n  function announce(text) { E.sr.textContent = ''; setTimeout(function () { E.sr.textContent = text; }, 40); }\r\n  var captionMode = 'public';\r\n  function setCaption(mode, w) {\r\n    captionMode = mode;\r\n    E.tagline.hidden = !(mode === 'tagline' || mode === 'public');\r\n    E.tagline.textContent = mode === 'public' ? PUBLIC_STATUS : TAGLINE;\r\n    E.drawing.hidden = mode !== 'drawing';\r\n    E.result.hidden = mode !== 'result';\r\n    if (mode === 'result' && w) E.resultNo.textContent = w.no_struk;\r\n    E.recovery.hidden = mode !== 'recovery';\r\n  }\r\n\r\n  function canDraw() {\r\n    return S.isAdmin && !S.publicMode && !S.sessionExpired && S.poolLoaded && S.winnersSynced && S.total > 0 &&\r\n      !S.drawing && !S.modalOpen && !S.modalBusy && !S.recovery && !S.detailLoading;\r\n  }\r\n\r\n  function updateUI() {\r\n    var admin = S.isAdmin && !S.publicMode;\r\n    page.setAttribute('data-mode', S.publicMode ? 'public' : (admin ? 'admin' : 'loading'));\r\n    E.drawBtn.hidden = !admin || !!S.recovery;\r\n    E.drawBtn.disabled = !canDraw();\r\n    E.drawBtn.classList.toggle('is-busy', S.drawing);\r\n    E.drawBtn.setAttribute('aria-busy', S.drawing ? 'true' : 'false');\r\n    E.drawLabel.textContent = S.drawing ? 'Mengundi...' : (S.hasDrawn ? 'UNDI LAGI' : 'MULAI UNDIAN');\r\n    E.count.hidden = !admin || !S.poolLoaded;\r\n    E.countNum.textContent = fmtCount(S.total);\r\n    E.countNum.hidden = S.total <= 0;\r\n    E.countLabel.textContent = S.total > 0 ? 'struk siap diundi' : 'Belum ada struk yang siap diundi';\r\n    E.count.title = S.info || '';\r\n    E.refresh.hidden = S.publicMode || !(admin || S.loadError);\r\n    E.refresh.disabled = S.drawing || S.loadingPool || S.recoveryChecking;\r\n    E.resetBtn.hidden = !admin;\r\n    E.resetBtn.disabled = S.drawing;\r\n    Array.prototype.forEach.call(E.winnersList.querySelectorAll('.bxsm-detail'), function (b) { b.disabled = S.drawing || S.detailLoading; });\r\n    renderRecovery();\r\n  }\r\n\r\n  function renderRecovery() {\r\n    var rec = S.recovery;\r\n    if (!rec) return;\r\n    var age = Date.now() - rec.startedAt;\r\n    E.recMsg.textContent = rec.message || '';\r\n    E.recCheck.disabled = S.recoveryChecking || S.drawing || S.modalOpen;\r\n    E.recCheck.textContent = S.recoveryChecking ? 'Memeriksa...' : 'PERIKSA HASIL';\r\n    E.recAbandon.disabled = !rec.checkedNoWinner || S.recoveryChecking || S.drawing || S.modalOpen;\r\n    E.recMeta.textContent = 'Token undian: ' + rec.token.slice(0, 8) + '... | dimulai ' + timeWib(rec.startedAt) +\r\n      (age < SAME_TOKEN_SAFE_MS\r\n        ? ' | kirim ulang dengan token yang sama aman sampai ' + timeWib(rec.startedAt + SAME_TOKEN_SAFE_MS)\r\n        : ' | token sudah terlalu lama untuk dikirim ulang');\r\n  }\r\n\r\n  function renderWinners() {\r\n    var list = E.winnersList;\r\n    Array.prototype.forEach.call(list.querySelectorAll('.bxsm-winner-row'), function (n) { n.remove(); });\r\n    var visible = S.winners.filter(function (w) { return !S.hidden[w.id]; });\r\n    E.winnersEmpty.textContent = S.publicMode ? PUBLIC_WINNERS_TEXT : 'Belum ada pemenang';\r\n    E.winnersEmpty.hidden = visible.length > 0;\r\n    var frag = document.createDocumentFragment();\r\n    visible.forEach(function (w) {\r\n      var row = document.createElement('div'); row.className = 'bxsm-winner-row';\r\n      var left = document.createElement('div'); left.className = 'bxsm-winner-left';\r\n      var dot = document.createElement('span'); dot.className = 'bxsm-winner-dot';\r\n      var info = document.createElement('div'); info.className = 'bxsm-winner-info';\r\n      var line = document.createElement('div'); line.className = 'bxsm-winner-line';\r\n      var no = document.createElement('span'); no.className = 'bxsm-winner-no'; no.textContent = '#' + (w.no > 0 ? w.no : '?');\r\n      var struk = document.createElement('span'); struk.className = 'bxsm-winner-struk'; struk.textContent = w.no_struk;\r\n      line.appendChild(no); line.appendChild(struk);\r\n      info.appendChild(line);\r\n      if (w.prize) { var pz = document.createElement('div'); pz.className = 'bxsm-winner-prize'; pz.textContent = w.prize; info.appendChild(pz); }\r\n      left.appendChild(dot); left.appendChild(info);\r\n      var btn = document.createElement('button');\r\n      btn.type = 'button'; btn.className = 'bxsm-btn bxsm-detail'; btn.textContent = 'Detail';\r\n      btn.setAttribute('data-id', String(w.id));\r\n      btn.disabled = S.drawing || S.detailLoading;\r\n      row.appendChild(left); row.appendChild(btn);\r\n      frag.appendChild(row);\r\n    });\r\n    list.appendChild(frag);\r\n  }\r\n\r\n  var toastTimer = 0;\r\n  function toast(msg) {\r\n    E.toast.textContent = msg;\r\n    E.toast.classList.add('show');\r\n    clearTimeout(toastTimer);\r\n    toastTimer = setTimeout(function () { E.toast.classList.remove('show'); }, 2800);\r\n  }\r\n\r\n  \/\/ Bentuk tampilan sebelum undian (mengikuti panjang nomor struk yang paling umum di pool).\r\n  \/\/ Panjang (median) dan pola paling umum dari sampel pool, hanya untuk bentuk tampilan sebelum undian.\r\n  function analyzePool(segs) {\r\n    S.typicalLen = 0;\r\n    S.idleShape = '';\r\n    if (!segs.length) return;\r\n    var step = Math.max(1, Math.floor(segs.length \/ 401));\r\n    var sample = [];\r\n    for (var i = 0; i < segs.length; i += step) sample.push(graphemes(segs[i].no_struk));\r\n    var lens = sample.map(function (g) { return g.length; }).sort(function (a, b) { return a - b; });\r\n    var L = lens[Math.floor(lens.length \/ 2)] || 0;\r\n    var shapes = {}, best = '', bestN = 0;\r\n    sample.forEach(function (g) {\r\n      if (g.length !== L) return;\r\n      var sh = g.map(function (c) { return SPIN_RE.test(c) ? '#' : c; }).join('\\u0001');\r\n      shapes[sh] = (shapes[sh] || 0) + 1;\r\n      if (shapes[sh] > bestN) { bestN = shapes[sh]; best = sh; }\r\n    });\r\n    S.typicalLen = L;\r\n    S.idleShape = best;\r\n  }\r\n  function readyLayout() {\r\n    if (!S.isAdmin || S.publicMode || !S.typicalLen) return machine.idleLayout(IDLE_REELS);\r\n    var L = S.typicalLen;\r\n    if (L > machine.maxUnits()) return machine.wideIdleLayout(Math.min(L, 16), poolSamples(null));\r\n    if (S.idleShape) {\r\n      var lay = machine.layoutForText(S.idleShape.split('\\u0001').map(function (c) { return c === '#' ? '?' : c; }).join(''), null, true);\r\n      if (lay.mode === 'reels' && lay.spin.some(Boolean)) return lay;\r\n    }\r\n    return machine.idleLayout(Math.max(3, L));\r\n  }\r\n  \/\/ Hiasan untuk tampilan lebar: maksimal 40 nomor acak dari pool (TIDAK dipakai untuk memilih pemenang).\r\n  function poolSamples(excludeId) {\r\n    var segs = S.segments, n = segs.length, out = [], used = {};\r\n    if (!n) return out;\r\n    var want = Math.min(40, n);\r\n    for (var tries = 0; out.length < want && tries < want * 4; tries++) {\r\n      var i = randInt(n);\r\n      if (used[i]) continue;\r\n      used[i] = true;\r\n      if (segs[i].id === excludeId || !segs[i].no_struk) continue;\r\n      out.push(segs[i].no_struk);\r\n    }\r\n    return out;\r\n  }\r\n  function showReadyMachine() {\r\n    S.lastResult = null;\r\n    machine.showIdle(readyLayout());\r\n  }\r\n\r\n  \/* ==========================================================================\r\n     MEMUAT DATA\r\n     ========================================================================== *\/\r\n  async function loadPool() {\r\n    if (S.drawing) return false;\r\n    S.loadingPool = true;\r\n    updateUI();\r\n    var d;\r\n    try {\r\n      d = await api('\/pool');\r\n    } catch (e) {\r\n      S.loadingPool = false;\r\n      if (e.message === 'not-admin') {\r\n        if (!S.isAdmin) showPublicMode(); else markSessionExpired();\r\n        return false;\r\n      }\r\n      S.loadError = true;\r\n      setStatus('Gagal terhubung ke server. Periksa koneksi lalu klik \"Muat ulang data\".', 'error');\r\n      updateUI();\r\n      return false;\r\n    }\r\n    S.loadingPool = false;\r\n    if (!d || !d.success) {\r\n      S.loadError = true;\r\n      setStatus((d && d.message) || 'Gagal memuat data undian.', 'error');\r\n      updateUI();\r\n      return false;\r\n    }\r\n    if (S.drawing) { updateUI(); return false; } \/\/ undian dimulai saat memuat: data ini sudah usang\r\n    var firstTime = !S.isAdmin;\r\n    S.isAdmin = true;\r\n    S.publicMode = false;\r\n    S.sessionExpired = false;\r\n    S.loadError = false;\r\n    var segs = Array.isArray(d.segments) ? d.segments : [];\r\n    S.segments = segs.map(function (s) { return { id: Number(s && s.id), no_struk: str(s && s.no_struk) }; })\r\n      .filter(function (s) { return isFinite(s.id) && s.id > 0; });\r\n    var tot = Number(d.total);\r\n    S.total = isFinite(tot) && tot >= 0 ? tot : S.segments.length;\r\n    S.info = str(d.info);\r\n    analyzePool(S.segments);\r\n    S.poolLoaded = true;\r\n    if (firstTime && E.status.className.indexOf('is-error') === -1) setStatus('');\r\n    if (!S.lastResult && !S.recovery) {\r\n      showReadyMachine();\r\n      if (captionMode === 'public') setCaption('tagline');\r\n    }\r\n    if (S.total === 0) setStatus('Belum ada struk berstatus Disetujui. Setujui struk di dashboard, lalu klik \"Muat ulang data\".', 'warn');\r\n    updateUI();\r\n    return true;\r\n  }\r\n\r\n  async function loadWinners(force) {\r\n    if (S.drawing && !force) return false;\r\n    var d;\r\n    try { d = await api('\/winners'); }\r\n    catch (e) {\r\n      if (S.isAdmin) {\r\n        if (e.message === 'not-admin') markSessionExpired();\r\n        else setStatus('Gagal memuat daftar pemenang. Klik \"Muat ulang data\".', 'error');\r\n      }\r\n      return false;\r\n    }\r\n    if (!d || !d.success) {\r\n      if (S.isAdmin) setStatus((d && d.message) || 'Gagal memuat daftar pemenang.', 'error');\r\n      return false;\r\n    }\r\n    if (S.drawing && !force) return false; \/\/ animasi sudah mulai: jangan bocorkan pemenang lebih awal\r\n    S.winners = sortWinners((Array.isArray(d.winners) ? d.winners : []).map(normalizeWinner));\r\n    S.winnersSynced = true;\r\n    renderWinners();\r\n    updateUI();\r\n    return true;\r\n  }\r\n\r\n  function showPublicMode() {\r\n    S.isAdmin = false;\r\n    S.publicMode = true;\r\n    S.poolLoaded = false;\r\n    S.winnersSynced = false;\r\n    S.segments = [];\r\n    S.total = 0;\r\n    S.winners = [];\r\n    S.typicalLen = 0;\r\n    S.idleShape = '';\r\n    machine.showIdle(machine.idleLayout(IDLE_REELS));\r\n    setCaption('public');\r\n    setStatus('');\r\n    renderWinners();\r\n    updateUI();\r\n  }\r\n\r\n  function markSessionExpired() {\r\n    S.sessionExpired = true;\r\n    restNonce = '';\r\n    setStatus('Sesi admin berakhir. Login ulang ke WordPress (boleh di tab lain), lalu klik \"Muat ulang data\".', 'error');\r\n    updateUI();\r\n  }\r\n\r\n  async function syncAll() {\r\n    var res = await Promise.all([loadPool(), loadWinners()]);\r\n    return res[0] && res[1];\r\n  }\r\n\r\n  \/* ==========================================================================\r\n     UNDIAN\r\n     Server memilih & menyimpan pemenang (random_int + transaksi). Browser hanya\r\n     mengirim 1 token per undian dan memakai ulang token itu untuk semua percobaan ulang.\r\n     ========================================================================== *\/\r\n  \/\/ Hasil: winner | pool_empty | busy | rejected | expired | unclear. ctx.ambiguous = true jika\r\n  \/\/ ada permintaan yang mungkin sudah tersimpan di server tanpa jawaban yang jelas.\r\n  async function performDraw(ctx, opts) {\r\n    opts = opts || {};\r\n    var maxNet = opts.maxNet === undefined ? 2 : opts.maxNet;\r\n    var maxBusy = opts.maxBusy === undefined ? 3 : opts.maxBusy;\r\n    var net = 0, busy = 0;\r\n    for (;;) {\r\n      var res;\r\n      try {\r\n        res = await apiCall('\/draw', { token: ctx.token }, { timeout: DRAW_TIMEOUT_MS });\r\n      } catch (e) {\r\n        if (e.message === 'not-admin') return { kind: 'expired' };\r\n        if (e.sent) ctx.ambiguous = true;\r\n        if (net < maxNet) {\r\n          net++;\r\n          setStatus('Koneksi bermasalah. Mencoba lagi dengan token undian yang sama (' + net + '\/' + maxNet + ')...', 'warn');\r\n          await sleep(1000 * net);\r\n          continue;\r\n        }\r\n        return { kind: 'unclear', message: e.message === 'timeout' ? 'Server tidak menjawab dalam ' + Math.round(DRAW_TIMEOUT_MS \/ 1000) + ' detik.' : 'Koneksi ke server terputus.' };\r\n      }\r\n      var d = res.data;\r\n      if (d && d.success && d.winner && d.winner.id && d.winner.no_struk !== undefined && d.winner.no_struk !== null) {\r\n        return { kind: 'winner', winner: normalizeWinner(d.winner), remaining: d.remaining, replay: !!d.replay };\r\n      }\r\n      if (d && d.code === 'busy') {\r\n        ctx.ambiguous = true; \/\/ permintaan lain dengan token ini sedang diproses server\r\n        if (busy < maxBusy) { busy++; setStatus('Server masih memproses undian ini. Menunggu...', 'warn'); await sleep(1500); continue; }\r\n        return { kind: 'busy', message: 'Server masih memproses undian ini.' };\r\n      }\r\n      if (d && d.code === 'pool_empty') return { kind: 'pool_empty', message: 'Belum ada struk yang siap diundi.' };\r\n      if (res.status >= 500 || !d || d.success) {\r\n        \/\/ 5xx \/ bukan JSON \/ sukses tanpa data pemenang: bisa saja sudah tersimpan.\r\n        ctx.ambiguous = true;\r\n        if (net < maxNet) { net++; setStatus('Jawaban server tidak jelas. Mencoba lagi dengan token yang sama (' + net + '\/' + maxNet + ')...', 'warn'); await sleep(1000 * net); continue; }\r\n        return { kind: 'unclear', message: (d && d.message) ? d.message : 'Jawaban server tidak jelas (HTTP ' + res.status + ').' };\r\n      }\r\n      return { kind: 'rejected', message: d.message || 'Undian ditolak server (HTTP ' + res.status + ').' };\r\n    }\r\n  }\r\n\r\n  async function onDrawClick() {\r\n    if (!canDraw()) return;\r\n    S.drawing = true;  \/\/ kunci langsung (sinkron), sebelum await apa pun\r\n    var ctx = { token: newToken(), startedAt: Date.now(), snapshot: S.winners.map(function (w) { return w.id; }), ambiguous: false };\r\n    storeRecovery(ctx); \/\/ penanda: kalau halaman tertutup di tengah jalan, hasilnya bisa dicek saat dibuka lagi\r\n    S.lastResult = null;\r\n    setCaption('drawing');\r\n    setStatus('');\r\n    updateUI();\r\n    machine.startSpin();\r\n\r\n    var out;\r\n    try { out = await performDraw(ctx); }\r\n    catch (e) { out = { kind: 'unclear', message: 'Terjadi kesalahan tak terduga.' }; ctx.ambiguous = true; }\r\n\r\n    if (out.kind === 'winner') {\r\n      clearRecoveryStore();\r\n      await revealWinner(out.winner, out.remaining);\r\n      return;\r\n    }\r\n    S.drawing = false;\r\n    showReadyMachine();\r\n    if (ctx.ambiguous) {\r\n      \/\/ Ada permintaan yang mungkin sudah tersimpan di server: JANGAN izinkan undian baru dengan token baru.\r\n      enterRecovery(ctx, (out.kind === 'expired' ? 'Sesi admin berakhir saat mengundi. Login ulang, klik \"Muat ulang data\", lalu Periksa Hasil. '\r\n        : (out.message ? out.message + ' ' : '')) + 'Pemenang mungkin sudah tersimpan.');\r\n      if (out.kind === 'expired') markSessionExpired(); else setStatus('');\r\n      return;\r\n    }\r\n    \/\/ Jawaban jelas (atau permintaan tidak pernah terkirim) dan TIDAK ada pemenang yang tersimpan.\r\n    clearRecoveryStore();\r\n    setCaption('tagline');\r\n    if (out.kind === 'pool_empty') {\r\n      S.total = 0;\r\n      S.segments = [];\r\n      setStatus('Belum ada struk yang siap diundi. Setujui struk di dashboard, lalu klik \"Muat ulang data\".', 'warn');\r\n    } else if (out.kind === 'expired') {\r\n      markSessionExpired();\r\n    } else if (out.kind === 'unclear') {\r\n      setStatus('Gagal terhubung ke server. Permintaan undian belum terkirim, jadi belum ada pemenang. Periksa koneksi lalu coba lagi.', 'error');\r\n    } else {\r\n      setStatus(out.message || 'Undian gagal. Silakan coba lagi.', 'error');\r\n    }\r\n    updateUI();\r\n  }\r\n\r\n  \/\/ Animasi menuju nomor struk pemenang dari server, lalu tampilkan modal.\r\n  async function revealWinner(w, remaining) {\r\n    S.drawing = true;\r\n    setCaption('drawing');\r\n    setStatus('');\r\n    updateUI();\r\n    try {\r\n      await machine.landOn(w.no_struk, poolSamples(w.id));\r\n    } catch (e) {\r\n      try { machine.placeFinal(w.no_struk); } catch (e2) { \/* modal & daftar tetap menampilkan nomor lengkap *\/ }\r\n    }\r\n    S.lastResult = w;\r\n    S.hasDrawn = true;\r\n    S.winners = sortWinners(S.winners.filter(function (x) { return x.id !== w.id; }).concat([w]));\r\n    delete S.hidden[w.id];\r\n    S.winnersSynced = true;\r\n    S.segments = S.segments.filter(function (s) { return s.id !== w.id; });\r\n    var rem = Number(remaining);\r\n    if (remaining !== undefined && remaining !== null && isFinite(rem) && rem >= 0) S.total = rem;\r\n    renderWinners();\r\n    setCaption('result', w);\r\n    announce('Pemenang: nomor struk ' + w.no_struk + (w.nama ? ', atas nama ' + w.nama : ''));\r\n    showModal(w);          \/\/ modal dibuka sebelum kunci undian dilepas\r\n    S.drawing = false;\r\n    updateUI();\r\n  }\r\n\r\n  \/* ==========================================================================\r\n     PEMULIHAN (hasil undian belum pasti)\r\n     ========================================================================== *\/\r\n  function storeRecovery(rec) {\r\n    try { localStorage.setItem(RECOVERY_KEY, JSON.stringify({ token: rec.token, startedAt: rec.startedAt, snapshot: rec.snapshot })); } catch (e) { \/* storage tidak tersedia *\/ }\r\n  }\r\n  function readRecovery() {\r\n    try {\r\n      var raw = localStorage.getItem(RECOVERY_KEY);\r\n      if (!raw) return null;\r\n      var o = JSON.parse(raw);\r\n      if (!o || typeof o.token !== 'string' || !\/^[A-Za-z0-9-]{8,64}$\/.test(o.token) || !isFinite(o.startedAt) || !Array.isArray(o.snapshot)) return null;\r\n      return { token: o.token, startedAt: Number(o.startedAt), snapshot: o.snapshot.map(Number).filter(function (n) { return isFinite(n); }) };\r\n    } catch (e) { return null; }\r\n  }\r\n  function clearRecoveryStore() { try { localStorage.removeItem(RECOVERY_KEY); } catch (e) { \/* abaikan *\/ } }\r\n\r\n  function enterRecovery(ctx, message) {\r\n    S.recovery = {\r\n      token: ctx.token,\r\n      startedAt: ctx.startedAt,\r\n      snapshot: ctx.snapshot.slice(),\r\n      checkedNoWinner: false,\r\n      stale: Date.now() - ctx.startedAt > STALE_RECOVERY_MS,\r\n      message: message || ''\r\n    };\r\n    storeRecovery(S.recovery);\r\n    setCaption('recovery');\r\n    updateUI();\r\n  }\r\n  function finishRecovery() {\r\n    S.recovery = null;\r\n    clearRecoveryStore();\r\n    updateUI();\r\n  }\r\n  function recMessage(msg) { if (S.recovery) { S.recovery.message = msg; renderRecovery(); } }\r\n\r\n  async function checkRecovery() {\r\n    var rec = S.recovery;\r\n    if (!rec || S.recoveryChecking || S.drawing || S.modalOpen) return;\r\n    S.recoveryChecking = true;\r\n    recMessage('Memeriksa daftar pemenang di server...');\r\n    updateUI();\r\n\r\n    \/\/ 1) Daftar pemenang: apakah ada pemenang baru sejak undian ini dimulai?\r\n    var d;\r\n    try { d = await api('\/winners'); }\r\n    catch (e) {\r\n      S.recoveryChecking = false;\r\n      if (e.message === 'not-admin') { markSessionExpired(); recMessage('Sesi admin berakhir. Login ulang, klik \"Muat ulang data\", lalu klik Periksa Hasil lagi.'); }\r\n      else recMessage('Gagal memeriksa (koneksi). Periksa koneksi lalu klik Periksa Hasil lagi.');\r\n      updateUI();\r\n      return;\r\n    }\r\n    if (!d || !d.success) {\r\n      S.recoveryChecking = false;\r\n      recMessage(((d && d.message) || 'Gagal memeriksa daftar pemenang.') + ' Klik Periksa Hasil lagi.');\r\n      updateUI();\r\n      return;\r\n    }\r\n    S.sessionExpired = false;\r\n    var list = sortWinners((Array.isArray(d.winners) ? d.winners : []).map(normalizeWinner));\r\n    S.winners = list;\r\n    S.winnersSynced = true;\r\n    renderWinners();\r\n    var known = {};\r\n    rec.snapshot.forEach(function (id) { known[id] = true; });\r\n    var fresh = list.filter(function (w) { return !known[w.id]; });\r\n\r\n    if (rec.stale) {\r\n      S.recoveryChecking = false;\r\n      rec.checkedNoWinner = true;\r\n      storeRecovery(rec);\r\n      recMessage(fresh.length\r\n        ? 'Undian ini sudah lebih dari 6 jam. Ada ' + fresh.length + ' pemenang di sesi ini yang belum ada saat undian itu dimulai; periksa daftar pemenang di bawah dan menu Pemenang di dashboard sebelum melanjutkan.'\r\n        : 'Undian ini sudah lebih dari 6 jam dan tidak ada pemenang baru di sesi ini. Cek menu Pemenang di dashboard, lalu klik \"Lanjutkan tanpa pemenang\".');\r\n      updateUI();\r\n      return;\r\n    }\r\n    if (fresh.length === 1) {\r\n      S.recoveryChecking = false;\r\n      finishRecovery();\r\n      toast('Pemenang undian ini ditemukan di server.');\r\n      await revealWinner(fresh[0], null);\r\n      return;\r\n    }\r\n    if (fresh.length > 1) {\r\n      S.recoveryChecking = false;\r\n      finishRecovery();\r\n      setCaption('tagline');\r\n      await loadPool();\r\n      setStatus('Ditemukan ' + fresh.length + ' pemenang baru sejak undian dimulai. Periksa daftar pemenang di bawah sebelum mengundi lagi.', 'warn');\r\n      updateUI();\r\n      return;\r\n    }\r\n\r\n    \/\/ 2) Tidak ada pemenang baru. Kirim ulang dengan token yang SAMA (masih dalam batas aman).\r\n    if (Date.now() - rec.startedAt >= SAME_TOKEN_SAFE_MS) {\r\n      S.recoveryChecking = false;\r\n      rec.checkedNoWinner = true;\r\n      storeRecovery(rec);\r\n      recMessage('Tidak ada pemenang baru di daftar pemenang. Token undian sudah lebih dari 12 menit, jadi tidak dikirim ulang. Jika daftar pemenang sudah benar, klik \"Lanjutkan tanpa pemenang\".');\r\n      updateUI();\r\n      return;\r\n    }\r\n    rec.checkedNoWinner = true; \/\/ pengecekan daftar berhasil dan tidak ada pemenang baru\r\n    storeRecovery(rec);\r\n    recMessage('Tidak ada pemenang baru di daftar. Mengirim ulang permintaan dengan token yang sama...');\r\n    S.drawing = true;\r\n    updateUI();\r\n    machine.startSpin();\r\n    var ctx = { token: rec.token, startedAt: rec.startedAt, snapshot: rec.snapshot, ambiguous: true };\r\n    var out;\r\n    try { out = await performDraw(ctx, { maxNet: 1, maxBusy: 2 }); }\r\n    catch (e) { out = { kind: 'unclear', message: 'Terjadi kesalahan tak terduga.' }; }\r\n    S.recoveryChecking = false;\r\n    setStatus('');\r\n    if (out.kind === 'winner') {\r\n      finishRecovery();\r\n      await revealWinner(out.winner, out.remaining);\r\n      return;\r\n    }\r\n    S.drawing = false;\r\n    showReadyMachine();\r\n    if (out.kind === 'pool_empty') {\r\n      \/\/ Daftar pemenang tidak berubah dan server mencoba mengundi dengan token ini tetapi pool kosong:\r\n      \/\/ tidak ada pemenang yang tersimpan untuk undian ini.\r\n      finishRecovery();\r\n      setCaption('tagline');\r\n      S.total = 0;\r\n      S.segments = [];\r\n      setStatus('Tidak ada pemenang yang tersimpan untuk undian tadi, dan saat ini belum ada struk yang siap diundi.', 'warn');\r\n      updateUI();\r\n      return;\r\n    }\r\n    if (out.kind === 'expired') {\r\n      markSessionExpired();\r\n      recMessage('Sesi admin berakhir. Login ulang, klik \"Muat ulang data\", lalu klik Periksa Hasil lagi.');\r\n    } else if (out.kind === 'busy') {\r\n      recMessage('Server masih memproses undian ini. Tunggu beberapa detik lalu klik Periksa Hasil lagi.');\r\n    } else if (out.kind === 'rejected') {\r\n      recMessage('Server menolak permintaan ulang: ' + out.message + ' Jika daftar pemenang sudah benar, klik \"Lanjutkan tanpa pemenang\".');\r\n    } else {\r\n      recMessage((out.message || 'Masih belum ada jawaban yang jelas.') + ' Klik Periksa Hasil lagi beberapa saat lagi.');\r\n    }\r\n    updateUI();\r\n  }\r\n\r\n  function abandonRecovery() {\r\n    var rec = S.recovery;\r\n    if (!rec || !rec.checkedNoWinner || S.recoveryChecking || S.drawing) return;\r\n    if (!window.confirm('Lanjutkan tanpa pemenang?\\n\\nPengecekan terakhir tidak menemukan pemenang baru untuk undian ini. Undian berikutnya memakai token baru. Pastikan daftar pemenang (juga menu Pemenang di dashboard) sudah benar.')) return;\r\n    finishRecovery();\r\n    setCaption('tagline');\r\n    showReadyMachine();\r\n    setStatus('Status undian dipulihkan. Kamu bisa mengundi lagi.', 'info');\r\n    loadPool();\r\n    updateUI();\r\n  }\r\n\r\n  \/* ==========================================================================\r\n     MODAL PEMENANG\r\n     ========================================================================== *\/\r\n  var prevOverflow = '';\r\n  function setModalBusy(kind) {\r\n    S.modalBusy = kind || false;\r\n    E.mConfirm.disabled = !!kind;\r\n    E.mCancel.disabled = !!kind;\r\n    E.mClose.disabled = !!kind;\r\n    E.mConfirm.textContent = kind === 'prize' ? 'Menyimpan...' : 'Konfirmasi';\r\n    E.mCancel.textContent = kind === 'cancel' ? 'Membatalkan...' : 'Batalkan';\r\n  }\r\n  function setModalError(msg) {\r\n    E.mError.textContent = msg || '';\r\n    E.mError.hidden = !msg;\r\n    if (msg) E.modalBox.scrollTop = E.modalBox.scrollHeight; \/\/ kolom hadiah tetap terlihat di atas tombol\r\n  }\r\n  function showModal(w) {\r\n    S.modalWinner = w;\r\n    S.waFull = w.no_wa || '';       \/\/ nomor lengkap hanya di variabel ini, tidak pernah ditampilkan\r\n    S.modalOpen = true;\r\n    E.mNo.textContent = w.no > 0 ? 'Pemenang #' + w.no : '';\r\n    E.mName.textContent = w.nama || '-';\r\n    E.mStruk.textContent = w.no_struk || '-';\r\n    E.mWa.textContent = w.no_wa_masked || '-';\r\n    E.mProduk.textContent = w.produk.length ? w.produk.join(', ') : '-';\r\n    E.mWon.textContent = w.won_at || '-';\r\n    var src = safeImgUrl(w.foto_struk_url);\r\n    if (src) { E.mFoto.src = src; E.mFotoField.hidden = false; } else { E.mFoto.removeAttribute('src'); E.mFotoField.hidden = true; }\r\n    E.mPrize.value = w.prize || '';\r\n    setModalError('');\r\n    setModalBusy(false);\r\n    if (!E.modal.classList.contains('show')) prevOverflow = document.documentElement.style.overflow;\r\n    document.documentElement.style.overflow = 'hidden';\r\n    E.modal.classList.add('show');\r\n    E.modal.setAttribute('aria-hidden', 'false');\r\n    setTimeout(function () { try { E.modalBox.focus({ preventScroll: true }); } catch (e) { \/* abaikan *\/ } }, 60);\r\n    startSparkle();\r\n    updateUI();\r\n  }\r\n  function closeModal() {\r\n    E.modal.classList.remove('show');\r\n    E.modal.setAttribute('aria-hidden', 'true');\r\n    document.documentElement.style.overflow = prevOverflow;\r\n    stopSparkle();\r\n    S.waFull = '';\r\n    S.modalWinner = null;\r\n    S.modalOpen = false;\r\n    updateUI();\r\n  }\r\n\r\n  \/\/ Konfirmasi: simpan Hadiah (POST \/prize) jika berubah, lalu tutup modal & sinkronkan.\r\n  async function onConfirm() {\r\n    if (S.modalBusy || !S.modalOpen) return;\r\n    var w = S.modalWinner;\r\n    var hadiah = E.mPrize.value.trim();\r\n    if (w && w.id && hadiah !== (w.prize || '')) {\r\n      setModalError('');\r\n      setModalBusy('prize');\r\n      var d = null;\r\n      try { d = await api('\/prize', { id: w.id, prize: hadiah }); }\r\n      catch (e) { d = { success: false, message: e.message === 'not-admin' ? 'Sesi admin berakhir. Login ulang lalu klik Konfirmasi lagi.' : 'Gagal menyimpan hadiah. Cek koneksi lalu klik Konfirmasi lagi.' }; }\r\n      setModalBusy(false);\r\n      if (!d || !d.success) {\r\n        var pm = (d && d.message) || 'Gagal menyimpan hadiah. Cek koneksi lalu klik Konfirmasi lagi.';\r\n        setModalError(pm + ' Hadiah yang diketik belum tersimpan.');\r\n        toast(pm);\r\n        return; \/\/ modal tetap terbuka, teks hadiah yang diketik tetap ada\r\n      }\r\n      w.prize = str(d.prize);\r\n      S.winners.forEach(function (x) { if (x.id === w.id) x.prize = w.prize; });\r\n      renderWinners();\r\n      toast(w.prize ? 'Hadiah berhasil disimpan!' : 'Hadiah dikosongkan.');\r\n    }\r\n    closeModal();\r\n    syncAll();\r\n  }\r\n\r\n  \/\/ Tutup tanpa menyimpan (untuk keadaan darurat, misalnya server menolak hadiah).\r\n  function onCloseX() {\r\n    if (S.modalBusy || !S.modalOpen) return;\r\n    var w = S.modalWinner;\r\n    var typed = E.mPrize.value.trim();\r\n    if (w && typed !== (w.prize || '') && !window.confirm('Hadiah yang diketik belum tersimpan. Tutup tanpa menyimpan?')) return;\r\n    closeModal();\r\n    syncAll();\r\n  }\r\n\r\n  \/\/ Batalkan: langsung batalkan (tanpa pop up konfirmasi), struk kembali ke daftar undian.\r\n  async function onCancelWinner() {\r\n    if (S.modalBusy || !S.modalOpen) return;\r\n    var w = S.modalWinner;\r\n    if (!w || !w.id) return;\r\n    setModalError('');\r\n    setModalBusy('cancel');\r\n    var d = null;\r\n    try { d = await api('\/cancel', { id: w.id }); }\r\n    catch (e) { d = { success: false, message: e.message === 'not-admin' ? 'Sesi admin berakhir. Login ulang lalu coba lagi.' : 'Gagal membatalkan. Cek koneksi lalu coba lagi.' }; }\r\n    setModalBusy(false);\r\n    if (!d || !d.success) {\r\n      var cm = (d && d.message) || 'Gagal membatalkan. Cek koneksi lalu coba lagi.';\r\n      setModalError(cm);\r\n      toast(cm);\r\n      return;\r\n    }\r\n    S.winners = S.winners.filter(function (x) { return x.id !== w.id; });\r\n    renderWinners();\r\n    if (S.lastResult && S.lastResult.id === w.id) {\r\n      \/\/ Nomor yang dibatalkan tidak boleh tetap tampil sebagai hasil di layar.\r\n      S.lastResult = null;\r\n      setCaption('tagline');\r\n    }\r\n    closeModal();\r\n    toast('Kemenangan dibatalkan. Struk kembali ke daftar undian.');\r\n    await syncAll();\r\n    if (!S.lastResult && !S.recovery && !S.drawing) showReadyMachine();\r\n  }\r\n\r\n  async function openDetail(id) {\r\n    if (S.drawing || S.modalOpen || S.detailLoading) return;\r\n    S.detailLoading = true;\r\n    updateUI();\r\n    await loadWinners();\r\n    S.detailLoading = false;\r\n    var w = null;\r\n    S.winners.forEach(function (x) { if (x.id === id) w = x; });\r\n    if (w) showModal(w); else { toast('Data pemenang tidak ditemukan (mungkin sudah dibatalkan).'); updateUI(); }\r\n  }\r\n\r\n  \/\/ Salin nomor WA lengkap dari variabel JS (tampilan tetap tersamar).\r\n  function copyWA() {\r\n    var wa = S.waFull;\r\n    if (!wa) { toast('Nomor WA tidak tersedia.'); return; }\r\n    var done = function () { toast('No. WA disalin!'); };\r\n    var fail = function () { toast('Gagal menyalin.'); };\r\n    if (navigator.clipboard && window.isSecureContext) {\r\n      navigator.clipboard.writeText(wa).then(done).catch(function () { if (fallbackCopy(wa)) done(); else fail(); });\r\n    } else if (fallbackCopy(wa)) done(); else fail();\r\n  }\r\n  function fallbackCopy(text) {\r\n    var ta = document.createElement('textarea');\r\n    ta.value = text;\r\n    ta.setAttribute('readonly', '');\r\n    ta.style.position = 'fixed';\r\n    ta.style.top = '0';\r\n    ta.style.opacity = '0';\r\n    document.body.appendChild(ta);\r\n    ta.select();\r\n    var ok = false;\r\n    try { ok = document.execCommand('copy'); } catch (e) { ok = false; }\r\n    ta.remove();\r\n    return ok;\r\n  }\r\n\r\n  \/* ---------- lightbox foto ---------- *\/\r\n  function openLightbox() {\r\n    var src = E.mFoto.getAttribute('src');\r\n    if (!src) return;\r\n    E.lightboxImg.src = src;\r\n    E.lightbox.hidden = false;\r\n  }\r\n  function closeLightbox() { E.lightbox.hidden = true; E.lightboxImg.removeAttribute('src'); }\r\n\r\n  \/* ---------- efek kilau saat modal pemenang ---------- *\/\r\n  var sctx = E.sparkle.getContext ? E.sparkle.getContext('2d') : null;\r\n  var sparkles = [];\r\n  var sparkleRAF = 0;\r\n  var SPARK_COLORS = ['#b5e14f', '#ffffff', '#d6f285', '#9ccf3c', '#f3e6b8', '#2f7a34'];\r\n  function sizeSparkle() { E.sparkle.width = window.innerWidth; E.sparkle.height = window.innerHeight; }\r\n  function newSparkle(init) {\r\n    return {\r\n      x: Math.random() * E.sparkle.width,\r\n      y: init ? Math.random() * E.sparkle.height : E.sparkle.height + 10,\r\n      size: Math.random() * 5 + 2,\r\n      speedY: Math.random() * 1.4 + 0.4,\r\n      speedX: (Math.random() - 0.5) * 0.8,\r\n      opacity: Math.random() * 0.8 + 0.2,\r\n      fade: Math.random() * 0.012 + 0.004,\r\n      color: SPARK_COLORS[randInt(SPARK_COLORS.length)],\r\n      star: Math.random() > 0.5\r\n    };\r\n  }\r\n  function drawStar(c, x, y, size) {\r\n    c.beginPath();\r\n    for (var i = 0; i < 5; i++) {\r\n      var a1 = (i * 4 * Math.PI) \/ 5 - Math.PI \/ 2;\r\n      var a2 = ((i * 4 + 2) * Math.PI) \/ 5 - Math.PI \/ 2;\r\n      if (i === 0) c.moveTo(x + Math.cos(a1) * size, y + Math.sin(a1) * size);\r\n      else c.lineTo(x + Math.cos(a1) * size, y + Math.sin(a1) * size);\r\n      c.lineTo(x + Math.cos(a2) * size \/ 2, y + Math.sin(a2) * size \/ 2);\r\n    }\r\n    c.closePath();\r\n    c.fill();\r\n  }\r\n  function animateSparkle() {\r\n    sctx.clearRect(0, 0, E.sparkle.width, E.sparkle.height);\r\n    for (var i = 0; i < sparkles.length; i++) {\r\n      var s = sparkles[i];\r\n      s.y -= s.speedY; s.x += s.speedX; s.opacity -= s.fade;\r\n      if (s.opacity <= 0 || s.y < -20) { sparkles[i] = newSparkle(false); continue; }\r\n      sctx.globalAlpha = Math.max(0, s.opacity);\r\n      sctx.fillStyle = s.color;\r\n      if (s.star) drawStar(sctx, s.x, s.y, s.size);\r\n      else { sctx.beginPath(); sctx.arc(s.x, s.y, s.size \/ 2, 0, Math.PI * 2); sctx.fill(); }\r\n    }\r\n    sctx.globalAlpha = 1;\r\n    sparkleRAF = requestAnimationFrame(animateSparkle);\r\n  }\r\n  function startSparkle() {\r\n    if (!sctx || reducedMotion() || sparkleRAF) return;\r\n    sizeSparkle();\r\n    sparkles = [];\r\n    for (var i = 0; i < 70; i++) sparkles.push(newSparkle(true));\r\n    animateSparkle();\r\n  }\r\n  function stopSparkle() {\r\n    if (sparkleRAF) cancelAnimationFrame(sparkleRAF);\r\n    sparkleRAF = 0;\r\n    sparkles = [];\r\n    if (sctx) sctx.clearRect(0, 0, E.sparkle.width, E.sparkle.height);\r\n  }\r\n\r\n  \/* ---------- logo: fallback teks + pelat gelap jika logo berwarna terang ---------- *\/\r\n  function setupLogo() {\r\n    var img = E.logo;\r\n    if (!img) return;\r\n    function fail() { page.classList.add('bxsm-no-logo'); }\r\n    function ok() {\r\n      page.classList.remove('bxsm-no-logo');\r\n      try {\r\n        var w = 48, h = Math.max(1, Math.round(48 * img.naturalHeight \/ img.naturalWidth));\r\n        var c = document.createElement('canvas'); c.width = w; c.height = h;\r\n        var x = c.getContext('2d');\r\n        x.drawImage(img, 0, 0, w, h);\r\n        var px = x.getImageData(0, 0, w, h).data; \/\/ gagal (SecurityError) jika logo dari domain lain: abaikan\r\n        var sum = 0, cnt = 0;\r\n        for (var i = 0; i < px.length; i += 4) {\r\n          if (px[i + 3] < 90) continue;\r\n          sum += (0.2126 * px[i] + 0.7152 * px[i + 1] + 0.0722 * px[i + 2]) \/ 255;\r\n          cnt++;\r\n        }\r\n        if (cnt && sum \/ cnt > 0.78 && cnt \/ (w * h) < 0.95) E.logoWrap.classList.add('is-plate');\r\n      } catch (e) { \/* tetap pakai bayangan tipis *\/ }\r\n    }\r\n    img.addEventListener('load', ok);\r\n    img.addEventListener('error', fail);\r\n    if (img.complete) { if (img.naturalWidth > 0) ok(); else if (img.getAttribute('src')) fail(); }\r\n  }\r\n\r\n  \/* ==========================================================================\r\n     EVENT\r\n     ========================================================================== *\/\r\n  E.drawBtn.addEventListener('click', onDrawClick);\r\n  E.recCheck.addEventListener('click', checkRecovery);\r\n  E.recAbandon.addEventListener('click', abandonRecovery);\r\n  E.refresh.addEventListener('click', async function () {\r\n    if (S.drawing || S.loadingPool || S.recoveryChecking) return;\r\n    var wasExpired = S.sessionExpired;\r\n    S.sessionExpired = false;\r\n    restNonce = '';\r\n    setStatus('Memuat ulang data...');\r\n    var ok = await syncAll();\r\n    if (ok && E.status.className.indexOf('is-') === -1) setStatus(S.total > 0 ? 'Data diperbarui.' : '', 'info');\r\n    if (ok && wasExpired && S.recovery) recMessage('Login berhasil dipulihkan. Klik Periksa Hasil.');\r\n  });\r\n  E.resetBtn.addEventListener('click', function () {\r\n    if (S.drawing) return;\r\n    if (!window.confirm('Reset list pemenang? Data pemenang di database tetap aman, hanya tampilan list ini yang dikosongkan.')) return;\r\n    S.winners.forEach(function (w) { S.hidden[w.id] = true; });\r\n    renderWinners();\r\n    toast('List pemenang direset.');\r\n  });\r\n  E.winnersList.addEventListener('click', function (e) {\r\n    var b = e.target.closest ? e.target.closest('.bxsm-detail') : null;\r\n    if (b && !b.disabled) openDetail(Number(b.getAttribute('data-id')));\r\n  });\r\n  E.mConfirm.addEventListener('click', onConfirm);\r\n  E.mCancel.addEventListener('click', onCancelWinner);\r\n  E.mClose.addEventListener('click', onCloseX);\r\n  E.mCopy.addEventListener('click', copyWA);\r\n  E.mFoto.addEventListener('click', openLightbox);\r\n  E.mPrize.addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); onConfirm(); } });\r\n  E.lightbox.addEventListener('click', function (e) { if (e.target !== E.lightboxImg) closeLightbox(); });\r\n  document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && !E.lightbox.hidden) closeLightbox(); });\r\n\r\n  var resizeTimer = 0;\r\n  window.addEventListener('resize', function () {\r\n    clearTimeout(resizeTimer);\r\n    resizeTimer = setTimeout(function () {\r\n      if (sparkleRAF) sizeSparkle();\r\n      machine.onResize();\r\n      \/\/ Tampilan siap-undi menyesuaikan lebar layar (misalnya HP diputar).\r\n      if (machine.isIdle() && !S.drawing) {\r\n        var lay = readyLayout();\r\n        if (lay.key !== machine.currentKey()) machine.showIdle(lay);\r\n      }\r\n    }, 150);\r\n  });\r\n  if (document.fonts && document.fonts.ready) document.fonts.ready.then(function () { machine.onResize(); });\r\n  E.machine.addEventListener('transitionend', function (e) { if (e.target === E.machine && e.propertyName === 'width') machine.onResize(); });\r\n\r\n  \/* ==========================================================================\r\n     MULAI\r\n     ========================================================================== *\/\r\n  setupLogo();\r\n  machine.showIdle(machine.idleLayout(IDLE_REELS));\r\n  setCaption('public');\r\n  setStatus('Memuat data undian...');\r\n  updateUI();\r\n  (async function init() {\r\n    await Promise.all([loadPool(), loadWinners()]);\r\n    if (!S.isAdmin) { if (!S.publicMode && E.status.textContent === 'Memuat data undian...') setStatus(''); return; }\r\n    if (!S.winnersSynced && !S.sessionExpired) setStatus('Gagal memuat daftar pemenang. Klik \"Muat ulang data\" sebelum mengundi.', 'error');\r\n    var rec = readRecovery();\r\n    if (rec && !S.recovery && !S.drawing) {\r\n      enterRecovery(rec, 'Halaman dibuka ulang saat hasil undian sebelumnya belum pasti. Klik Periksa Hasil sebelum mengundi lagi.');\r\n      showReadyMachine();\r\n    }\r\n  })();\r\n})();\r\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-9456","page","type-page","status-publish","hentry"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Slot Machine - Bingxue<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/bingxueindonesia.co.id\/en\/slot-machine\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Slot Machine - Bingxue\" \/>\n<meta property=\"og:url\" content=\"https:\/\/bingxueindonesia.co.id\/en\/slot-machine\/\" \/>\n<meta property=\"og:site_name\" content=\"Bingxue\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/bingxueindonesia\/\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-01T10:23:52+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/bingxueindonesia.co.id\/slot-machine\/\",\"url\":\"https:\/\/bingxueindonesia.co.id\/slot-machine\/\",\"name\":\"Slot Machine - Bingxue\",\"isPartOf\":{\"@id\":\"https:\/\/bingxueindonesia.co.id\/#website\"},\"datePublished\":\"2026-10-01T09:43:09+00:00\",\"dateModified\":\"2026-10-01T10:23:52+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/bingxueindonesia.co.id\/slot-machine\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/bingxueindonesia.co.id\/slot-machine\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/bingxueindonesia.co.id\/slot-machine\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/bingxueindonesia.co.id\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Slot Machine\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/bingxueindonesia.co.id\/#website\",\"url\":\"https:\/\/bingxueindonesia.co.id\/\",\"name\":\"Bingxue\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/bingxueindonesia.co.id\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/bingxueindonesia.co.id\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/bingxueindonesia.co.id\/#organization\",\"name\":\"Bingxue\",\"url\":\"https:\/\/bingxueindonesia.co.id\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/bingxueindonesia.co.id\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/bingxueindonesia.co.id\/wp-content\/uploads\/2026\/08\/cropped-1@2x-1.png\",\"contentUrl\":\"https:\/\/bingxueindonesia.co.id\/wp-content\/uploads\/2026\/08\/cropped-1@2x-1.png\",\"width\":1901,\"height\":410,\"caption\":\"Bingxue\"},\"image\":{\"@id\":\"https:\/\/bingxueindonesia.co.id\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/bingxueindonesia\/\",\"https:\/\/www.instagram.com\/bingxue.indonesia\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Slot Machine - Bingxue","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/bingxueindonesia.co.id\/en\/slot-machine\/","og_locale":"en_US","og_type":"article","og_title":"Slot Machine - Bingxue","og_url":"https:\/\/bingxueindonesia.co.id\/en\/slot-machine\/","og_site_name":"Bingxue","article_publisher":"https:\/\/www.facebook.com\/bingxueindonesia\/","article_modified_time":"2026-10-01T10:23:52+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/bingxueindonesia.co.id\/slot-machine\/","url":"https:\/\/bingxueindonesia.co.id\/slot-machine\/","name":"Slot Machine - Bingxue","isPartOf":{"@id":"https:\/\/bingxueindonesia.co.id\/#website"},"datePublished":"2026-10-01T09:43:09+00:00","dateModified":"2026-10-01T10:23:52+00:00","breadcrumb":{"@id":"https:\/\/bingxueindonesia.co.id\/slot-machine\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/bingxueindonesia.co.id\/slot-machine\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/bingxueindonesia.co.id\/slot-machine\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/bingxueindonesia.co.id\/"},{"@type":"ListItem","position":2,"name":"Slot Machine"}]},{"@type":"WebSite","@id":"https:\/\/bingxueindonesia.co.id\/#website","url":"https:\/\/bingxueindonesia.co.id\/","name":"Bingxue","description":"","publisher":{"@id":"https:\/\/bingxueindonesia.co.id\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/bingxueindonesia.co.id\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/bingxueindonesia.co.id\/#organization","name":"Bingxue","url":"https:\/\/bingxueindonesia.co.id\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/bingxueindonesia.co.id\/#\/schema\/logo\/image\/","url":"https:\/\/bingxueindonesia.co.id\/wp-content\/uploads\/2026\/08\/cropped-1@2x-1.png","contentUrl":"https:\/\/bingxueindonesia.co.id\/wp-content\/uploads\/2026\/08\/cropped-1@2x-1.png","width":1901,"height":410,"caption":"Bingxue"},"image":{"@id":"https:\/\/bingxueindonesia.co.id\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/bingxueindonesia\/","https:\/\/www.instagram.com\/bingxue.indonesia\/"]}]}},"jetpack_sharing_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/bingxueindonesia.co.id\/en\/wp-json\/wp\/v2\/pages\/9456","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bingxueindonesia.co.id\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/bingxueindonesia.co.id\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/bingxueindonesia.co.id\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bingxueindonesia.co.id\/en\/wp-json\/wp\/v2\/comments?post=9456"}],"version-history":[{"count":7,"href":"https:\/\/bingxueindonesia.co.id\/en\/wp-json\/wp\/v2\/pages\/9456\/revisions"}],"predecessor-version":[{"id":9469,"href":"https:\/\/bingxueindonesia.co.id\/en\/wp-json\/wp\/v2\/pages\/9456\/revisions\/9469"}],"wp:attachment":[{"href":"https:\/\/bingxueindonesia.co.id\/en\/wp-json\/wp\/v2\/media?parent=9456"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}