<?php
// Remote App Interface
define('DATA_DIR', __DIR__ . '/data');
$_settings_raw = file_exists(DATA_DIR . '/settings.json') ? file_get_contents(DATA_DIR . '/settings.json') : '{}';
$_branding = json_decode($_settings_raw, true) ?: [];

// Branding variables with defaults
$_protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$_host = $_SERVER['HTTP_HOST'] ?? 'localhost:8080';
$_base_url = $_protocol . '://' . $_host;
$b_logo       = $_branding['login_logo_url'] ?? $_base_url . '/inject/app_icons/default.png';
$b_name       = $_branding['login_brand_name'] ?? 'TEXA APPS';
$b_subtitle   = $_branding['login_brand_subtitle'] ?? 'Premium AI Workflow Suite';
$b_catalog    = $_branding['login_catalog_text'] ?? 'Full Katalog AI Tools';
$b_label      = $_branding['login_input_label'] ?? 'Access Code';
$b_placeholder= $_branding['login_input_placeholder'] ?? 'Enter access code...';
$b_error      = $_branding['login_error_text'] ?? 'Kode akses tidak valid.';
$b_btn        = $_branding['login_btn_text'] ?? 'Masuk';
$b_takeover   = $_branding['login_takeover_text'] ?? 'Ambil Alih Sesi';
$b_footer     = $_branding['login_footer_text'] ?? 'TEXA.STUDIO © 2026 copyright.';
$b_title      = $_branding['login_page_title'] ?? 'TEXA Apps';
$b_dash_name  = $_branding['login_dash_name'] ?? 'TEXA Apps';
?>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title><?php echo htmlspecialchars($b_title); ?></title>
  <style>
/* ============================================
   TEXA Apps - Dark Ocean Blue Theme
   ============================================ */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;900&display=swap');

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

:root {
  --ocean-deep: #020c18;
  --ocean-mid: #041628;
  --ocean-surface: #061e38;
  --blue-glow: #0066ff;
  --blue-bright: #0099ff;
  --blue-light: #4db8ff;
  --blue-pale: #a8d8ff;
  --accent-cyan: #00d4ff;
  --text-primary: #e8f4ff;
  --text-secondary: #7aaed4;
  --text-muted: #4a7a9b;
  --glass-bg: rgba(4, 22, 48, 0.85);
  --glass-border: rgba(0, 150, 255, 0.18);
  --glass-border-hover: rgba(0, 200, 255, 0.4);
  --error-color: #ff4d4d;
  --success-color: #00e676;
  --radius-lg: 20px;
  --radius-md: 14px;
  --radius-sm: 10px;
  --popup-w: 400px;
  --popup-h: 580px;
  --dash-h: 540px;
}

html,
body {
  width: 100%;
  min-height: 100vh;
  margin: 0;
  padding: 0;
  background: var(--ocean-deep);
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--text-primary);
  overflow-x: hidden;
  overflow-y: auto;
  position: relative;
}

/* ---- ANIMATED BACKGROUND ---- */
.bg-canvas {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

.orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(70px);
  opacity: 0.35;
  animation: floatOrb 10s ease-in-out infinite alternate;
}

.orb-1 {
  width: 280px;
  height: 280px;
  background: radial-gradient(circle, #0055cc, transparent 70%);
  top: -80px;
  left: -60px;
  animation-duration: 12s;
}

.orb-2 {
  width: 220px;
  height: 220px;
  background: radial-gradient(circle, #003399, transparent 70%);
  bottom: 20px;
  right: -50px;
  animation-duration: 9s;
  animation-delay: -3s;
}

.orb-3 {
  width: 160px;
  height: 160px;
  background: radial-gradient(circle, #00aaff, transparent 70%);
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  opacity: 0.15;
  animation-duration: 15s;
  animation-delay: -7s;
}

@keyframes floatOrb {
  0% {
    transform: translate(0, 0) scale(1);
  }

  33% {
    transform: translate(20px, -25px) scale(1.08);
  }

  66% {
    transform: translate(-15px, 20px) scale(0.95);
  }

  100% {
    transform: translate(10px, -10px) scale(1.05);
  }
}

/* Orb-3 needs its own transform override */
.orb-3 {
  animation-name: floatOrb3;
}

@keyframes floatOrb3 {
  0% {
    transform: translate(-50%, -50%) scale(1);
  }

  50% {
    transform: translate(-50%, -50%) scale(1.3);
  }

  100% {
    transform: translate(-50%, -50%) scale(0.85);
  }
}

/* Particles */
.particles {
  position: absolute;
  inset: 0;
}

.particle {
  position: absolute;
  border-radius: 50%;
  background: var(--blue-light);
  opacity: 0;
  animation: twinkle var(--dur, 4s) ease-in-out var(--delay, 0s) infinite;
}

@keyframes twinkle {

  0%,
  100% {
    opacity: 0;
    transform: scale(0.5);
  }

  50% {
    opacity: 0.6;
    transform: scale(1);
  }
}

/* Scan line effect */
.bg-canvas::after {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(0deg,
      rgba(0, 80, 200, 0.03) 0px,
      rgba(0, 80, 200, 0.03) 1px,
      transparent 1px,
      transparent 3px);
  pointer-events: none;
}

/* ---- SCREENS ---- */
.screen {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 24px;
  box-sizing: border-box;
  animation: screenIn 0.4s ease-out;
}

.screen.hidden {
  display: none !important;
}

@keyframes screenIn {
  from {
    opacity: 0;
    transform: translateY(12px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ---- GLASS CARD ---- */
.glass-card {
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow:
    0 0 0 1px rgba(0, 100, 200, 0.08) inset,
    0 20px 60px rgba(0, 0, 0, 0.5),
    0 0 30px rgba(0, 80, 200, 0.12);
  padding: 36px 32px;
  width: 100%;
  max-width: 340px;
}

/* ---- BRAND / LOGO ---- */
.brand-logo {
  display: flex;
  justify-content: center;
  margin-bottom: 16px;
}

.logo-icon {
  width: 56px;
  height: 56px;
  border-radius: 16px;
  box-shadow: 0 0 20px rgba(0, 150, 255, 0.5), 0 8px 24px rgba(0, 0, 0, 0.4);
  position: relative;
  overflow: hidden;
}

.logo-icon::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.2), transparent);
}

.logo-icon.sm {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  box-shadow: 0 0 12px rgba(0, 150, 255, 0.4);
}

.brand-name {
  text-align: center;
  font-size: 22px;
  font-weight: 800;
  background: linear-gradient(135deg, #e0f0ff 30%, #4db8ff 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  letter-spacing: -0.5px;
  margin-bottom: 4px;
}

.brand-sub {
  text-align: center;
  font-size: 12px;
  color: var(--text-muted);
  letter-spacing: 0.5px;
  margin-bottom: 28px;
}

/* ---- INPUT GROUP ---- */
.input-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 20px;
}

.input-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--blue-light);
  letter-spacing: 1px;
  text-transform: uppercase;
}

.input-wrapper {
  position: relative;
  display: flex;
  align-items: center;
}

.input-icon {
  position: absolute;
  left: 12px;
  width: 15px;
  height: 15px;
  color: var(--text-muted);
  pointer-events: none;
  flex-shrink: 0;
}

.code-input {
  width: 100%;
  padding: 12px 44px 12px 38px;
  background: rgba(0, 20, 50, 0.6);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 14px;
  font-family: inherit;
  outline: none;
  transition: border-color 0.25s, box-shadow 0.25s;
  letter-spacing: 1px;
}

.code-input::placeholder {
  color: var(--text-muted);
  letter-spacing: 0;
}

.code-input:focus {
  border-color: var(--blue-bright);
  box-shadow: 0 0 0 3px rgba(0, 150, 255, 0.15), 0 0 16px rgba(0, 120, 255, 0.2);
}

.toggle-eye {
  position: absolute;
  right: 10px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  display: flex;
  align-items: center;
  padding: 4px;
  border-radius: 6px;
  transition: color 0.2s;
}

.toggle-eye:hover {
  color: var(--blue-light);
}

.input-error {
  display: none;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  color: var(--error-color);
  font-weight: 500;
  animation: none;
}

.input-error.visible {
  display: flex;
  animation: shakeError 0.4s ease-out;
}

@keyframes shakeError {

  0%,
  100% {
    transform: translateX(0);
  }

  20% {
    transform: translateX(-6px);
  }

  40% {
    transform: translateX(6px);
  }

  60% {
    transform: translateX(-4px);
  }

  80% {
    transform: translateX(4px);
  }
}

.input-wrapper.shake {
  animation: shakeInput 0.4s ease-out;
}

@keyframes shakeInput {

  0%,
  100% {
    transform: translateX(0);
  }

  20% {
    transform: translateX(-8px);
  }

  40% {
    transform: translateX(8px);
  }

  60% {
    transform: translateX(-5px);
  }

  80% {
    transform: translateX(5px);
  }
}

/* ---- BUTTONS ---- */
.btn-primary {
  width: 100%;
  padding: 13px 20px;
  background: linear-gradient(135deg, #0055cc, #0088ff);
  border: none;
  border-radius: var(--radius-sm);
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  font-family: inherit;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  letter-spacing: 0.3px;
  position: relative;
  overflow: hidden;
  transition: transform 0.2s, box-shadow 0.2s;
  box-shadow: 0 4px 20px rgba(0, 100, 255, 0.4);
}

.btn-primary::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), transparent);
  opacity: 0;
  transition: opacity 0.2s;
}

.btn-primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 28px rgba(0, 120, 255, 0.55);
}

.btn-primary:hover::before {
  opacity: 1;
}

.btn-primary:active {
  transform: translateY(1px);
}

.footer-text {
  text-align: center;
  font-size: 10px;
  color: var(--text-muted);
  margin-top: 20px;
  opacity: 0.6;
}

/* ---- LOGO LOOP ANIMATION ---- */
.logoloop-container.top-banner {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 40px;
  margin: 0;
  border-radius: 0;
  border-left: none;
  border-right: none;
  border-top: none;
  background: rgba(0, 10, 30, 0.8);
  border-bottom: 1px solid rgba(0, 150, 255, 0.2);
  z-index: 100;
  backdrop-filter: blur(10px);
}

.logoloop-container.top-banner:hover {
  background: rgba(0, 20, 50, 0.9);
  box-shadow: 0 4px 20px rgba(0, 150, 255, 0.1);
}

.logoloop-track {
  display: flex;
  width: max-content;
  animation: scroll-left 15s linear infinite;
  padding: 8px 0;
}

.logoloop-container:hover .logoloop-track {
  animation-play-state: paused;
}

.logoloop-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 20px;
}

.logoloop-item img {
  width: 20px;
  height: 20px;
  border-radius: 4px;
  object-fit: cover;
}

.logoloop-item span {
  font-size: 12px;
  font-weight: 600;
  color: #e0f0ff;
  white-space: nowrap;
}

@keyframes scroll-left {
  0% { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}

/* Gradient Fade Edges */
.logoloop-container::before,
.logoloop-container::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 50px;
  z-index: 2;
  pointer-events: none;
}

.logoloop-container::before {
  left: 0;
  background: linear-gradient(to right, rgba(0, 10, 30, 1) 0%, transparent 100%);
}

.logoloop-container::after {
  right: 0;
  background: linear-gradient(to left, rgba(0, 10, 30, 1) 0%, transparent 100%);
}

/* ---- CATALOG BUTTON STATIC ---- */
.catalog-btn-static {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: #fff;
  text-decoration: none;
  font-size: 14px;
  font-weight: 600;
  margin: 10px auto 24px auto;
  padding: 10px 20px;
  border-radius: 12px;
  background: rgba(0, 150, 255, 0.15);
  border: 1px solid rgba(0, 150, 255, 0.4);
  box-shadow: 0 4px 15px rgba(0, 150, 255, 0.2);
  transition: all 0.3s ease;
  width: max-content;
}

.catalog-btn-static:hover {
  background: rgba(0, 150, 255, 0.25);
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(0, 150, 255, 0.3);
}

/* Push login card down to make space for top banner */
.bg-canvas {
  top: 40px;
}

.screen {
  padding-top: 40px;
}

/* ---- DASHBOARD ---- */
#dashboard-screen {
  justify-content: flex-start;
  min-height: 100vh;
  padding: 0;
  width: 100%;
}

.dash-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 20px;
  border-bottom: 1px solid var(--glass-border);
  background: rgba(2, 14, 30, 0.7);
  backdrop-filter: blur(10px);
  width: 100%;
  flex-shrink: 0;
}

.dash-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 15px;
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: 0.3px;
}

.header-badge {
  padding: 5px 10px;
  border-radius: 20px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  display: flex;
  align-items: center;
  gap: 5px;
  background: rgba(0, 40, 100, 0.3);
  border: 1px solid var(--glass-border);
  color: var(--text-primary);
}

.header-badge.ok {
  background: rgba(0, 230, 118, 0.15);
  border-color: rgba(0, 230, 118, 0.3);
  color: #00e676;
}

.header-badge.warn {
  background: rgba(255, 170, 0, 0.15);
  border-color: rgba(255, 170, 0, 0.3);
  color: #ffcc44;
}

.header-badge.err {
  background: rgba(255, 77, 77, 0.15);
  border-color: rgba(255, 77, 77, 0.3);
  color: #ff8080;
}

.dash-title {
  padding: 18px 20px 10px;
  width: 100%;
}

.dash-title h2 {
  font-size: 17px;
  font-weight: 700;
  background: linear-gradient(135deg, #e0f0ff, #4db8ff);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  margin-bottom: 2px;
}

.logout-btn {
  background: rgba(255, 60, 60, 0.1);
  border: 1px solid rgba(255, 60, 60, 0.2);
  color: #ff6b6b;
  width: 28px;
  height: 28px;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s;
}

.logout-btn:hover {
  background: #ff4d4d;
  color: white;
  transform: scale(1.05);
}

.dash-title p {
  font-size: 11px;
  color: var(--text-muted);
}

/* ---- APP LIST ---- */
.app-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  padding: 10px 20px 24px;
  width: 100%;
  flex: 1;
  overflow-y: visible;
  padding-bottom: 40px;
  /* buffer for bottom scrolling */
}

@media (min-width: 768px) {
  .app-grid {
    grid-template-columns: repeat(var(--desktop-cols, 4), 1fr);
    gap: 20px;
    padding: 20px 40px;
  }
}

.app-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  padding: 10px;
  border-radius: var(--radius-md);
  background: transparent;
  transition: all 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
  animation: appItemIn 0.5s ease-out both;
  position: relative;
}

.app-item:hover {
  transform: translateY(-4px) scale(1.05);
}

.app-item:hover .app-icon-wrap {
  box-shadow: 0 8px 24px rgba(0, 150, 255, 0.4), 0 0 0 1px rgba(0, 200, 255, 0.3) inset;
}

.app-item:active {
  transform: translateY(0) scale(0.97);
}

@keyframes appItemIn {
  from {
    opacity: 0;
    transform: translateY(16px) scale(0.92);
  }

  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.app-icon-wrap {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  position: relative;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
  transition: all 0.25s ease;
}

.app-icon-wrap::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
}

.app-name {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--text-primary);
  text-align: center;
  line-height: 1.2;
  letter-spacing: 0.3px;
}

.app-badge {
  opacity: 1;
}

/* ---- SERVER MODAL ---- */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(1, 8, 20, 0.75);
  backdrop-filter: blur(8px);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  animation: fadeIn 0.2s ease-out;
}

.modal-overlay.hidden {
  display: none !important;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

.modal-card {
  max-width: 340px;
  width: 100%;
  animation: modalSlideIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes modalSlideIn {
  from {
    opacity: 0;
    transform: scale(0.85) translateY(20px);
  }

  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20px;
}

.modal-title-group {
  display: flex;
  align-items: center;
  gap: 12px;
}

.modal-app-icon {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  flex-shrink: 0;
  position: relative;
  overflow: hidden;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}

.modal-app-icon::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
}

.modal-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 2px;
}

.modal-sub {
  font-size: 11px;
  color: var(--text-muted);
}

.btn-icon-close {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  border: 1px solid var(--glass-border);
  background: rgba(0, 30, 70, 0.5);
  color: var(--text-secondary);
  cursor: pointer;
  font-size: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s;
}

.btn-icon-close:hover {
  background: rgba(200, 30, 30, 0.3);
  border-color: rgba(255, 60, 60, 0.3);
  color: #ff8080;
}

/* ---- SERVER LIST ---- */
.server-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.server-btn {
  width: 100%;
  padding: 11px 16px;
  background: rgba(0, 30, 80, 0.4);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 12px;
  transition: all 0.2s;
  text-align: left;
  position: relative;
  overflow: hidden;
}

.server-btn::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: linear-gradient(180deg, var(--blue-glow), var(--accent-cyan));
  border-radius: 2px;
  opacity: 0;
  transition: opacity 0.2s;
}

.server-btn:hover {
  background: rgba(0, 60, 160, 0.4);
  border-color: rgba(0, 150, 255, 0.45);
  box-shadow: 0 4px 16px rgba(0, 80, 200, 0.22);
  transform: translateX(3px);
}

.server-btn:hover::before {
  opacity: 1;
}

.server-btn:active {
  transform: translateX(1px);
}

.server-app-icon {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  flex-shrink: 0;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  overflow: hidden;
  position: relative;
}

.server-app-icon::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
}

.server-info {
  flex: 1;
}

.server-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
}

.server-desc {
  font-size: 10px;
  color: var(--text-muted);
  margin-top: 1px;
}

.server-arrow {
  color: var(--text-muted);
  font-size: 12px;
  transition: transform 0.2s, color 0.2s;
}

.server-btn:hover .server-arrow {
  transform: translateX(3px);
  color: var(--blue-light);
}

/* Scrollbar */
::-webkit-scrollbar {
  width: 4px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: rgba(0, 100, 200, 0.3);
  border-radius: 2px;
}

::-webkit-scrollbar-thumb:hover {
  background: rgba(0, 150, 255, 0.5);
}
</style>
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;900&display=swap"
    rel="stylesheet" /></style>
</head>
<body>

  <!-- ========= LOADING OVERLAY ========= -->
  <div id="loadingOverlay" class="loading-overlay">
    <div class="loading-content">
      <img src="https://media.giphy.com/media/2P0BqNiMBw4HCix9xW/giphy.gif" alt="Loading Logo" class="loading-logo">
      <div class="loading-text">Sedang Mencari server terbaik...</div>
      <div class="loading-spinner"></div>
      
      <!-- Error State -->
      <div id="loadingErrorState" class="loading-error-state" style="display: none;">
        <div class="loading-error-text">Gagal memuat server. Silakan ulangi melalui app launcher</div>
        <button class="loading-retry-btn" onclick="retryLoading()">Ulangi Pilih Aplikasi</button>
      </div>
    </div>
  </div>
  
  <style>
    .loading-overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100vw;
      height: 100vh;
      background: rgba(10, 15, 30, 0.85); /* Semi-transparent dark background */
      backdrop-filter: blur(8px); /* Blur effect on background content */
      -webkit-backdrop-filter: blur(8px);
      z-index: 99999; /* Always on top */
      display: flex;
      justify-content: center;
      align-items: center;
      opacity: 0;
      visibility: hidden;
      transition: opacity 0.4s ease, visibility 0.4s ease;
      pointer-events: none; /* Default no interaction */
    }

    .loading-overlay.active {
      opacity: 1;
      visibility: visible;
      pointer-events: all; /* Block all clicks underneath */
    }

    .loading-content {
      display: flex;
      flex-direction: column;
      align-items: center;
      text-align: center;
      transform: translateY(20px);
      transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    }

    .loading-overlay.active .loading-content {
      transform: translateY(0);
    }

    .loading-logo {
      width: 150px;
      height: 150px;
      object-fit: cover;
      border-radius: 50%;
      margin-bottom: 20px;
      box-shadow: 0 0 30px rgba(0, 204, 255, 0.2);
      border: 2px solid rgba(0, 204, 255, 0.1);
    }

    .loading-text {
      color: #ffffff;
      font-family: 'Inter', sans-serif;
      font-size: 1.1rem;
      font-weight: 500;
      letter-spacing: 0.5px;
      margin-bottom: 15px;
      text-shadow: 0 2px 4px rgba(0,0,0,0.5);
      animation: pulseText 1.5s infinite alternate;
    }

    @keyframes pulseText {
      from { opacity: 0.7; }
      to { opacity: 1; }
    }

    .loading-spinner {
      width: 40px;
      height: 40px;
      border: 3px solid rgba(0, 204, 255, 0.1);
      border-radius: 50%;
      border-top-color: #00ccff;
      animation: spin 1s ease-in-out infinite;
    }

    @keyframes spin {
      to { transform: rotate(360deg); }
    }

    /* Error State Styling */
    .loading-error-state {
      margin-top: 15px;
      animation: fadeIn 0.3s ease;
    }

    @keyframes fadeIn {
      from { opacity: 0; transform: translateY(-10px); }
      to { opacity: 1; transform: translateY(0); }
    }

    .loading-error-text {
      color: #ff4d4d;
      font-size: 0.95rem;
      margin-bottom: 15px;
      font-weight: 500;
    }

    .loading-retry-btn {
      background: linear-gradient(135deg, #0072ff 0%, #00c6ff 100%);
      color: white;
      border: none;
      padding: 10px 20px;
      border-radius: 8px;
      font-family: 'Inter', sans-serif;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.2s ease;
      box-shadow: 0 4px 15px rgba(0, 114, 255, 0.3);
    }

    .loading-retry-btn:hover {
      transform: translateY(-2px);
      box-shadow: 0 6px 20px rgba(0, 114, 255, 0.4);
    }

    .loading-retry-btn:active {
      transform: translateY(0);
    }
    
    /* Blur background wrapper when loading is active */
    body.is-loading > *:not(.loading-overlay) {
      filter: blur(5px);
      pointer-events: none;
      user-select: none;
      transition: filter 0.4s ease;
    }
  </style>

  <!-- ========= ANIMATED BACKGROUND ========= -->
  <div class="bg-canvas">
    <div class="orb orb-1"></div>
    <div class="orb orb-2"></div>
    <div class="orb orb-3"></div>
    <div class="particles" id="particles"></div>
  </div>

  <!-- Katalog Link Top Banner (Full Width) -->
  <a href="#" id="catalog-link-top" class="logoloop-container top-banner" target="_blank" style="display: none;">
    <div class="logoloop-track" id="logoloop-track-top">
      <!-- Logo items will be injected here by JS -->
    </div>
  </a>

  <!-- ========= LOGIN SCREEN ========= -->
  <div class="screen" id="login-screen">
    <div class="login-card glass-card">
      <div class="brand-logo">
        <div class="logo-icon" id="dynamic-login-logo">
          <img src="<?php echo htmlspecialchars($b_logo); ?>" alt="<?php echo htmlspecialchars($b_name); ?> Logo"
            style="width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block;"
            onerror="this.src='<?php echo $_base_url; ?>/inject/app_icons/default.png'">
        </div>
      </div>
      <h1 class="brand-name" id="dynamic-login-title"><?php echo htmlspecialchars($b_name); ?></h1>
      <p class="brand-sub" id="dynamic-login-subtitle"><?php echo htmlspecialchars($b_subtitle); ?></p>

      <!-- Tombol Katalog Statis di bawah judul -->
      <a href="#" id="catalog-link-static" class="catalog-btn-static" target="_blank" style="display: none;">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18">
          <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
          <polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline>
          <line x1="12" y1="22.08" x2="12" y2="12"></line>
        </svg>
        <?php echo htmlspecialchars($b_catalog); ?>
      </a>

      <div class="input-group">
        <label class="input-label"><?php echo htmlspecialchars($b_label); ?></label>
        <div class="input-wrapper" id="input-wrapper">
          <svg class="input-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <rect x="3" y="11" width="18" height="11" rx="2" />
            <path d="M7 11V7a5 5 0 0 1 10 0v4" />
          </svg>
          <input type="password" id="access-code" class="code-input" placeholder="<?php echo htmlspecialchars($b_placeholder); ?>"
            autocomplete="off" />
          <button class="toggle-eye" id="toggle-eye" title="Show/Hide">
            <svg id="eye-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16"
              height="16">
              <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
              <circle cx="12" cy="12" r="3" />
            </svg>
          </button>
        </div>
        <div class="input-error" id="login-error">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="13" height="13">
            <circle cx="12" cy="12" r="10" />
            <line x1="12" y1="8" x2="12" y2="12" />
            <line x1="12" y1="16" x2="12.01" y2="16" />
          </svg>
          <span><?php echo htmlspecialchars($b_error); ?></span>
        </div>
      </div>

      <button class="btn-primary" id="login-btn">
        <span><?php echo htmlspecialchars($b_btn); ?></span>
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" width="16" height="16">
          <path d="M5 12h14M12 5l7 7-7 7" />
        </svg>
      </button>

      <button class="btn-primary" id="takeover-btn" style="display: none; background: #ff4d4d; margin-top: 10px; box-shadow: 0 4px 15px rgba(255, 77, 77, 0.4); color: white; border: none; align-items: center; justify-content: center; gap: 8px;">
        <span><?php echo htmlspecialchars($b_takeover); ?></span>
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" width="16" height="16">
          <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
          <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
        </svg>
      </button>

      <p class="footer-text"><?php echo htmlspecialchars($b_footer); ?></p>
    </div>
  </div>

  <!-- ========= DASHBOARD SCREEN ========= -->
  <div class="screen hidden" id="dashboard-screen">
    <header class="dash-header">
      <div class="dash-logo">
        <div class="logo-icon sm">
          <img src="<?php echo htmlspecialchars($b_logo); ?>" alt="Logo"
            style="width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block;">
        </div>
        <span><?php echo htmlspecialchars($b_dash_name); ?></span>
      </div>
      <div style="display:flex; gap:8px; align-items:center;">
        <div id="header-badge" class="header-badge"></div>
        <button id="logout-btn" class="logout-btn" title="Keluar dari Perangkat ini">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
            <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
            <polyline points="16 17 21 12 16 7"></polyline>
            <line x1="21" y1="12" x2="9" y2="12"></line>
          </svg>
        </button>
      </div>
    </header>

    <div class="dash-title">
      <h2>App Launcher</h2>
      <p id="member-info">Pilih aplikasi untuk memulai</p>
    </div>

    <div class="app-grid" id="app-grid">
      <!-- App drawers are injected by popup.js -->
    </div>
  </div>

  <!-- ========= SERVER MODAL ========= -->
  <div class="modal-overlay hidden" id="modal-overlay">
    <div class="modal-card glass-card">
      <div class="modal-header">
        <div class="modal-title-group">
          <div class="modal-app-icon" id="modal-app-icon"></div>
          <div>
            <h3 class="modal-title" id="modal-title">Select Server</h3>
            <p class="modal-sub">Pilih server untuk terhubung</p>
          </div>
        </div>
        <button class="btn-icon-close" id="modal-close">&#10005;</button>
      </div>
      <div class="server-list" id="server-list">
        <!-- Servers injected by JS -->
      </div>
    </div>
  </div>

  <!-- ========= CUSTOM ALERT MODAL ========= -->
  <div class="modal-overlay hidden" id="custom-alert">
    <div class="modal-card glass-card" style="text-align:center; padding: 24px;">
      <div id="custom-alert-icon" style="font-size: 32px; margin-bottom: 12px;">⚠️</div>
      <h3 class="modal-title" id="custom-alert-title" style="margin-bottom: 8px;">Peringatan</h3>
      <p class="modal-sub" id="custom-alert-msg" style="margin-bottom: 20px; line-height: 1.5; color: #a1a1aa;"></p>
      <a href="#" target="_blank" class="btn-primary" id="custom-alert-action" style="width: 100%; margin-bottom: 12px; display: none; text-decoration: none; background: linear-gradient(135deg, #ffcc00, #ffaa00); color: #000; box-shadow: 0 4px 15px rgba(255, 170, 0, 0.3);">Action</a>
      <button class="btn-primary" id="custom-alert-close" style="width: 100%;">Tutup</button>
    </div>
  </div>

  
<script>
(function() {
    const urlParamsMock = new URLSearchParams(window.location.search);
    let extId = urlParamsMock.get('ext_id');
    if (extId) {
        localStorage.setItem('texa_ext_id', extId);
    } else {
        extId = localStorage.getItem('texa_ext_id');
    }

    // To avoid infinite loop when calling original chrome API
    window.__origChrome = window.chrome;

    // ============================================================
    // postMessage Bridge Helper (untuk Orion iOS / Safari Extension)
    // Menggantikan externally_connectable yang tidak tersedia di WebKit
    // ============================================================
    const _pendingBridgeCalls = {};
    let _bridgeRequestId = 0;

    // Listener untuk menerima response dari content.js
    window.addEventListener('message', (event) => {
        if (!event.data || !event.data.__texa_bridge_response) return;
        const { requestId, response } = event.data;
        if (requestId !== undefined && _pendingBridgeCalls[requestId]) {
            const cb = _pendingBridgeCalls[requestId];
            delete _pendingBridgeCalls[requestId];
            cb(response || {});
        }
    });

    function sendViaBridge(msg, callback) {
        const requestId = _bridgeRequestId++;
        if (callback) {
            _pendingBridgeCalls[requestId] = callback;
            // Timeout fallback: jika content.js tidak merespons dalam 3 detik
            setTimeout(() => {
                if (_pendingBridgeCalls[requestId]) {
                    delete _pendingBridgeCalls[requestId];
                    callback({});
                }
            }, 3000);
        }
        window.postMessage({
            __texa_bridge: true,
            requestId: requestId,
            payload: msg
        }, window.location.origin);
    }

    // PENTING: Expose ke window agar bisa diakses dari script block lain
    window.sendViaBridge = sendViaBridge;

    function sendMessage(msg, callback) {
        // ORION iOS FIX: externally_connectable dihapus dari manifest.
        // chrome.runtime.sendMessage(extId, ...) dari web page TIDAK BISA digunakan.
        // Gunakan postMessage bridge (content.js relay) sebagai satu-satunya jalur komunikasi.
        // Content.js sudah diinjeksikan oleh extension ke halaman ini (<all_urls> match).
        sendViaBridge(msg, callback);
    }



    // Setup mock chrome API
    const chromeMock = {
        runtime: {
        id: extId,
        getURL: function(path) {
            if (path === 'popup.html') {
                return '<?php echo $_base_url; ?>/inject/remote_app.php?ext_id=' + (extId || '');
            }
            return '<?php echo $_base_url; ?>/inject/' + path;
        },
        sendMessage: function(msg, callback) {
            sendMessage(msg, callback);
        }
    },
    storage: {
        local: {
            set: function(data, cb) {
                for (let key in data) {
                    localStorage.setItem('mock_storage_' + key, typeof data[key] === 'string' ? data[key] : JSON.stringify(data[key]));
                }
                sendMessage({action: 'save_storage', data: data}, cb);
            },
            get: function(keys, cb) {
                const fallbackGet = () => {
                    let res = {};
                    keys.forEach(k => {
                        let val = localStorage.getItem('mock_storage_' + k);
                        try { 
                            if (val && (val.startsWith('{') || val.startsWith('['))) val = JSON.parse(val); 
                        } catch(e){}
                        if (val !== null) res[k] = val;
                    });
                    if (cb) cb(res);
                };

                sendMessage({action: 'get_storage', keys: keys}, (response) => {
                    if (response && response.success && response.data) {
                        // sync to localstorage just in case
                        for (let k in response.data) {
                            localStorage.setItem('mock_storage_' + k, typeof response.data[k] === 'string' ? response.data[k] : JSON.stringify(response.data[k]));
                        }
                        if (cb) cb(response.data);
                    } else {
                        fallbackGet();
                    }
                });
            },
            remove: function(keys, cb) {
                let data = {};
                keys.forEach(k => {
                    localStorage.removeItem('mock_storage_' + k);
                    data[k] = ''; // empty out in extension
                });
                sendMessage({action: 'save_storage', data: data}, cb);
            }
        },
        session: {
            get: function(keys, cb) { if (cb) cb({}); },
            set: function(data, cb) { if (cb) cb(); },
            remove: function(keys, cb) { if (cb) cb(); }
        }
    },
    tabs: {
        update: function(tabId, info) {
            window.location.href = info.url;
        },
        getCurrent: function(cb) {
            cb(null);
        },
        query: function(q, cb) {
            cb([]);
        }
    }
};
    
    // PENTING: Selalu install chromeMock ke window.chrome tanpa syarat extId.
    // Jika extId null (URL dibuka manual tanpa ?ext_id=), chromeMock tetap akan diinstall.
    // Ini mencegah TypeError "must specify Extension ID" saat webpage memanggil chrome.runtime.sendMessage.
    // Semua panggilan akan disalurkan via sendViaBridge (postMessage bridge ke content.js).
    window.chrome = chromeMock;
})();

</script>
<script>
// To make sure all functions in this block use our chromeMock
const chromeMockRef = window.chrome;

// TEXA Apps - Popup Logic
// Handles: Login, Dashboard, Server Modal, Floating Tab Launch

'use strict';

// API URL — auto-detected dari PHP
const TEXA_SERVER = '<?php echo $_base_url; ?>';
const API_URL = TEXA_SERVER + '/inject/index.php';


// APPS kini dideklarasikan kosong dan akan diisi langsung dari backend ketika user Login
let APPS = [];

const SERVER_REGIONS = [
    { name: 'Server US-East 1', desc: 'Virginia · Low latency' },
    { name: 'Server US-West 2', desc: 'Oregon · High throughput' },
    { name: 'Server EU-Central', desc: 'Frankfurt · GDPR compliant' },
    { name: 'Server AP-Southeast', desc: 'Singapore · Asia Pacific' },
    { name: 'Server Global CDN', desc: 'Multi-region · Balanced' }
];

// =============================================
// DOM REFERENCES
// =============================================
const loginScreen = document.getElementById('login-screen');
const dashScreen = document.getElementById('dashboard-screen');
const accessCodeInput = document.getElementById('access-code');
const loginBtn = document.getElementById('login-btn');
const loginError = document.getElementById('login-error');
const inputWrapper = document.getElementById('input-wrapper');
const appGrid = document.getElementById('app-grid');
const modalOverlay = document.getElementById('modal-overlay');
const modalClose = document.getElementById('modal-close');
const serverList = document.getElementById('server-list');
const modalTitle = document.getElementById('modal-title');
const modalAppIcon = document.getElementById('modal-app-icon');
const dashLogoutBtn = document.getElementById('logout-btn');

// =============================================
// PARTICLES
// =============================================
function generateParticles() {
    const container = document.getElementById('particles');
    if (!container) return;
    for (let i = 0; i < 30; i++) {
        const p = document.createElement('div');
        p.className = 'particle';
        const size = Math.random() * 2.5 + 1;
        p.style.cssText = `
            width: ${size}px;
            height: ${size}px;
            left: ${Math.random() * 100}%;
            top: ${Math.random() * 100}%;
            --dur: ${Math.random() * 4 + 2.5}s;
            --delay: -${Math.random() * 6}s;
        `;
        container.appendChild(p);
    }
}

// =============================================
// SESSION
// =============================================
function isLoggedIn() {
    return localStorage.getItem('texa_auth') === '1';
}

function setLoggedIn(v, name = '', expiry = '', appsConfig = null, desktopCols = 4) {
    if (v) {
        localStorage.setItem('texa_auth', '1');
        localStorage.setItem('texa_name', name);
        localStorage.setItem('texa_expiry', expiry);
        localStorage.setItem('texa_apps', appsConfig ? JSON.stringify(appsConfig) : '');
        localStorage.setItem('texa_desktop_cols', desktopCols);
        document.documentElement.style.setProperty('--desktop-cols', desktopCols);
    } else {
        ['texa_auth', 'texa_name', 'texa_expiry', 'texa_apps', 'texa_desktop_cols'].forEach(k => localStorage.removeItem(k));
        document.documentElement.style.removeProperty('--desktop-cols');
    }
}

function goToDashboard() {
    loginScreen.classList.add('hidden');
    dashScreen.classList.remove('hidden');

    // Sembunyikan banner logo loop saat masuk ke App Launcher (jika sudah login dari awal)
    const topBanner = document.getElementById('catalog-link-top');
    if (topBanner) topBanner.style.display = 'none';

    // Show member name + remaining days
    const name = localStorage.getItem('texa_name') || '';
    const expiry = localStorage.getItem('texa_expiry') || '';
    const badgeEl = document.getElementById('header-badge');
    const infoEl = document.getElementById('member-info');

    if (infoEl && name) {
        infoEl.textContent = `Pilih aplikasi untuk memulai`;
    }

    if (badgeEl && name) {
        let sisaText = '';
        let badgeClass = 'ok';

        if (expiry) {
            const today = new Date();
            const expDate = new Date(expiry);
            today.setHours(0, 0, 0, 0);
            expDate.setHours(0, 0, 0, 0);
            const diff = Math.ceil((expDate - today) / (1000 * 60 * 60 * 24));

            if (diff <= 0) {
                sisaText = 'Expired';
                badgeClass = 'err';
            } else if (diff <= 3) {
                sisaText = `${diff} hari`;
                badgeClass = 'warn';
            } else {
                sisaText = `${diff} hari`;
                badgeClass = 'ok';
            }
        }

        badgeEl.className = `header-badge ${badgeClass}`;
        badgeEl.innerHTML = `👤 ${name} <span style="opacity:0.6;margin:0 2px">•</span> ${sisaText}`;
    }
}

function goToLogin() {
    dashScreen.classList.add('hidden');
    loginScreen.classList.remove('hidden');

    // Tampilkan kembali banner logo loop saat di halaman login
    const topBanner = document.getElementById('catalog-link-top');
    if (topBanner && topBanner.getAttribute('href') !== '#') {
        topBanner.style.display = 'block';
    }
}

// =============================================
// LOGOUT
// =============================================
if (dashLogoutBtn) {
    dashLogoutBtn.addEventListener('click', async () => {
        const code = localStorage.getItem('texa_code');
        const deviceId = getDeviceId();
        dashLogoutBtn.disabled = true;

        if (code && deviceId) {
            try {
                await fetch(`${API_URL}?action=logout&code=${encodeURIComponent(code)}&device_id=${encodeURIComponent(deviceId)}`, { cache: 'no-store' });
            } catch (e) {
                console.error('Logout pings failed:', e);
            }
        }

        localStorage.removeItem('texa_code');
        if (chromeMockRef && chromeMockRef.storage && chromeMockRef.storage.local) {
            chromeMockRef.storage.local.remove(['texa_code', 'texa_device_id', 'texa_apps']);
        }
        setLoggedIn(false);
        dashLogoutBtn.disabled = false;
        goToLogin();
    });
}

// =============================================
// LOGIN
// =============================================
function showError(msg = 'Kode akses tidak valid.') {
    inputWrapper.classList.add('shake');
    loginError.classList.add('visible');
    const span = loginError.querySelector('span');
    if (span) span.textContent = msg;
    accessCodeInput.style.borderColor = '#ff4d4d';
    accessCodeInput.style.boxShadow = '0 0 0 3px rgba(255, 77, 77, 0.15)';
    setTimeout(() => inputWrapper.classList.remove('shake'), 500);

    // Deteksi jika pesan error mengindikasikan sesi aktif di perangkat lain
    if (msg.includes('perangkat lain')) {
        const takeoverBtn = document.getElementById('takeover-btn');
        if (takeoverBtn) {
            takeoverBtn.style.display = 'flex';
            
            // Hapus listener lama jika ada, lalu pasang yang baru
            takeoverBtn.replaceWith(takeoverBtn.cloneNode(true));
            const newTakeoverBtn = document.getElementById('takeover-btn');
            
            newTakeoverBtn.addEventListener('click', async () => {
                newTakeoverBtn.disabled = true;
                newTakeoverBtn.innerHTML = '<span>Mengambil Alih...</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"><animateTransform attributeName="transform" type="rotate" from="0 12 12" to="360 12 12" dur="1s" repeatCount="indefinite"/></path></svg>';
                
                try {
                    const code = accessCodeInput.value.trim();
                    const deviceId = getDeviceId();
                    const res = await fetch(`${API_URL}?action=takeover&code=${encodeURIComponent(code)}&device_id=${encodeURIComponent(deviceId)}`, {
                        cache: 'no-store'
                    });
                    
                    const data = await res.json();
                    if (data.valid) {
                        // Sesi berhasil diambil alih, lanjut login biasa
                        handleLogin();
                    } else {
                        showError(data.reason || 'Gagal mengambil alih sesi.');
                        newTakeoverBtn.style.display = 'none';
                    }
                } catch (e) {
                    showError('Terjadi kesalahan koneksi.');
                    newTakeoverBtn.style.display = 'none';
                }
            });
        }
    }
}

function clearError() {
    loginError.classList.remove('visible');
    accessCodeInput.style.borderColor = '';
    accessCodeInput.style.boxShadow = '';
    const takeoverBtn = document.getElementById('takeover-btn');
    if (takeoverBtn) takeoverBtn.style.display = 'none';
}

loginBtn.addEventListener('click', handleLogin);
accessCodeInput.addEventListener('keypress', (e) => {
    if (e.key === 'Enter') handleLogin();
});
accessCodeInput.addEventListener('input', clearError);

function getDeviceId() {
    let id = localStorage.getItem('texa_device_id');
    if (!id) {
        id = crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2) + Date.now().toString(36);
        localStorage.setItem('texa_device_id', id);
    }
    return id;
}

async function handleLogin() {
    const code = accessCodeInput.value.trim();
    if (!code) {
        showError('Masukkan kode akses terlebih dahulu.');
        return;
    }

    // Loading state
    loginBtn.disabled = true;
    loginBtn.querySelector('span').textContent = 'Memverifikasi...';
    clearError();

    try {
        // Fallback agar tetap bisa login jika CORS/fetch block terjadi karena mode iframe strict
        let fetchUrl = `${API_URL}?action=validate&code=${encodeURIComponent(code)}&device_id=${encodeURIComponent(getDeviceId())}`;
        const res = await fetch(fetchUrl, {
            method: 'GET',
            mode: 'cors',
            cache: 'no-store',
            headers: {
                'Accept': 'application/json'
            }
        });

        if (!res.ok) {
            throw new Error('Server response was not ok: ' + res.status);
        }

        const data = await res.json();

        if (data.valid) {
            localStorage.setItem('texa_code', code); // Save for logout

            // Build hidden elements mapping per hostname
            const hiddenElementsConfig = {};
            if (data.apps) {
                data.apps.forEach(app => {
                    try {
                        let targetHostname = app.original_hostname;
                        if (!targetHostname) {
                            const urlObj = new URL(app.url);
                            targetHostname = urlObj.hostname;
                        }
                        if (app.hidden_elements && targetHostname) {
                            hiddenElementsConfig[targetHostname] = app.hidden_elements;
                        }
                        
                        // Also map custom servers hostnames just in case they differ
                        if (app.custom_servers && Array.isArray(app.custom_servers)) {
                            app.custom_servers.forEach(cs => {
                                try {
                                    let csHostname = cs.original_hostname;
                                    if (!csHostname && cs.url) {
                                        const urlObj2 = new URL(cs.url);
                                        csHostname = urlObj2.hostname;
                                    }
                                    if (app.hidden_elements && csHostname) {
                                        hiddenElementsConfig[csHostname] = app.hidden_elements;
                                    }
                                } catch(e) {}
                            });
                        }
                    } catch(e) {}
                });
            }

            chromeMockRef.storage.local.set({
                'texa_code': code,
                'texa_device_id': getDeviceId(),
                'texa_apps': data.apps || [],
                'texa_hidden_elements': hiddenElementsConfig,
                'texa_desktop_cols': data.desktop_grid_columns || 4,
                'texa_kick_settings': data.kick_settings || null,
                'texa_show_exp_popup': data.show_exp_popup || false,
                'texa_exp_settings': data.exp_settings || null,
                'texa_promo_popup': data.promo_popup || null
            });

            // Set grid columns directly to CSS variable
            document.documentElement.style.setProperty('--desktop-cols', data.desktop_grid_columns || 4);

            setLoggedIn(true, data.name, data.expiry, data.apps || null, data.desktop_grid_columns || 4);
            goToDashboard();
            renderAppGrid();
            
            // Tampilkan popup notifikasi expiry jika ada
            if (data.show_exp_popup && data.exp_settings) {
                if (!sessionStorage.getItem('exp_popup_shown')) {
                    showExpiryPopup(data.exp_settings, code);
                    sessionStorage.setItem('exp_popup_shown', 'true');
                }
            }

            // Tampilkan promo popup jika ada
            if (data.promo_popup) {
                if (!sessionStorage.getItem('promo_popup_shown')) {
                    setTimeout(() => {
                        showPromoPopup(data.promo_popup);
                        sessionStorage.setItem('promo_popup_shown', 'true');
                    }, data.show_exp_popup ? 1000 : 500);
                }
            }
            
            // Sembunyikan banner logo loop saat masuk ke App Launcher
            const topBanner = document.getElementById('catalog-link-top');
            if (topBanner) topBanner.style.display = 'none';
        } else {
            const reasons = {
                'expired': `Kode expired sejak ${data.expiry || ''}.`,
                'not_found': 'Kode akses tidak ditemukan.',
                'inactive': 'Kode akses dinonaktifkan.',
                'empty_code': 'Masukkan kode akses.',
                'server_error': 'Terjadi kesalahan server.',
                'device_in_use': 'Kode sedang digunakan di perangkat lain.'
            };
            showError(reasons[data.reason] || 'Kode akses tidak valid.');

            // Show takeover button if device is in use
            const takeoverBtn = document.getElementById('takeover-btn');
            if (takeoverBtn) {
                if (data.reason === 'device_in_use') {
                    takeoverBtn.style.display = 'flex';
                } else {
                    takeoverBtn.style.display = 'none';
                }
            }

            if (data.reason === 'expired' && data.expired_settings) {
                showCustomAlert(
                    data.expired_settings.title, 
                    data.expired_settings.text, 
                    data.expired_settings.logo, 
                    {
                        text: data.expired_settings.btn_text,
                        link: data.expired_settings.btn_link
                    }
                );
            }
        }
    } catch (err) {
        console.error("Login Fetch Error:", err);
        showError('Gagal terhubung ke server. Periksa koneksi internet. (' + err.message + ')');
    } finally {
        loginBtn.disabled = false;
        loginBtn.querySelector('span').textContent = 'Masuk';
    }
}

// Handler for Takeover Button
const takeoverBtn = document.getElementById('takeover-btn');
if (takeoverBtn) {
    takeoverBtn.addEventListener('click', async () => {
        const code = accessCodeInput.value.trim();
        if (!code) return;

        takeoverBtn.disabled = true;
        const span = takeoverBtn.querySelector('span');
        const oldText = span.textContent;
        span.textContent = 'Mengambil alih...';

        try {
            const res = await fetch(`${API_URL}?action=takeover&code=${encodeURIComponent(code)}&device_id=${encodeURIComponent(getDeviceId())}`, {
                method: 'GET',
                cache: 'no-store'
            });

            const data = await res.json();
            if (data.success) {
                // Berhasil ambil alih, langsung login ulang
                await handleLogin();
            } else {
                showError('Gagal mengambil alih sesi.');
            }
        } catch (err) {
            showError('Gagal terhubung ke server.');
        } finally {
            takeoverBtn.disabled = false;
            span.textContent = oldText;
        }
    });
}

// Toggle password visibility
const toggleEye = document.getElementById('toggle-eye');
if (toggleEye) {
    toggleEye.addEventListener('click', () => {
        const isPass = accessCodeInput.type === 'password';
        accessCodeInput.type = isPass ? 'text' : 'password';
        const eyeIcon = document.getElementById('eye-icon');
        if (eyeIcon) {
            eyeIcon.innerHTML = isPass
                ? `<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/>`
                : `<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>`;
        }
    });
}

// =============================================
// DASHBOARD
// =============================================
// Logout button was replaced by header badge

    // --- Custom Loading Overlay Functions ---
    let loadingTimeout;
    
    function showCustomLoading() {
        document.body.classList.add('is-loading');
        document.getElementById('loadingOverlay').classList.add('active');
        document.querySelector('.loading-spinner').style.display = 'block';
        document.getElementById('loadingErrorState').style.display = 'none';
        
        // Setup timeout (20 seconds) in case background process hangs
        clearTimeout(loadingTimeout);
        loadingTimeout = setTimeout(() => {
            showLoadingError();
        }, 20000);
    }
    
    function hideCustomLoading() {
        clearTimeout(loadingTimeout);
        document.body.classList.remove('is-loading');
        document.getElementById('loadingOverlay').classList.remove('active');
    }
    
    function showLoadingError() {
        document.querySelector('.loading-spinner').style.display = 'none';
        document.getElementById('loadingErrorState').style.display = 'block';
    }
    
    function retryLoading() {
        hideCustomLoading();
        // Kembali ke daftar server/aplikasi
        document.getElementById('modal-overlay').classList.add('hidden');
    }

function renderAppGrid() {
    appGrid.innerHTML = '';

    // Load dynamic apps payload directly from session
    let appsConfig = [];
    try {
        const raw = localStorage.getItem('texa_apps');
        if (raw) appsConfig = JSON.parse(raw);
    } catch (e) { appsConfig = []; }

    // Fallback if no apps are loaded or parsed (protects against grid breaking)
    if (!Array.isArray(appsConfig) || appsConfig.length === 0) {
        const msg = document.createElement('div');
        msg.style.cssText = 'grid-column: 1/-1; text-align:center; padding: 20px; color: var(--muted-text); font-size:12px;';
        msg.textContent = 'Tidak ada aplikasi yang tersedia untuk akun Anda.';
        appGrid.appendChild(msg);
        return;
    }

    // Update the global APPS reference for other functions to use (like modal)
    APPS = appsConfig;

    APPS.forEach((app, index) => {
        const item = document.createElement('div');
        item.className = 'app-item';
        item.style.animationDelay = `${index * 0.07}s`;

        const iconWrap = document.createElement('div');
        iconWrap.className = 'app-icon-wrap';

        if (app.icon_type === 'image' && app.icon_value) {
            iconWrap.style.background = 'transparent';
            iconWrap.style.overflow = 'hidden';
            const img = document.createElement('img');
            // Check if it's an external URL (http) or local extension file
            img.src = app.icon_value.startsWith('http') ? app.icon_value : chrome.runtime.getURL(app.icon_value);
            img.alt = app.name;
            img.style.cssText = 'width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block;';

            // Log fallback if local image is missing
            img.onerror = function () {
                this.onerror = null; // prevent infinite loop
                this.src = '<?php echo $_base_url; ?>/inject/app_icons/default.png'; // default fallback tx icon
            };

            iconWrap.appendChild(img);
        } else {
            iconWrap.style.background = app.color || 'var(--card-bg)';
            iconWrap.textContent = app.icon_value; // Emoji text
        }

        const name = document.createElement('span');
        name.className = 'app-name';
        name.textContent = app.name.replace(/\\n/g, '\n');
        name.style.whiteSpace = 'pre-line';

        item.appendChild(iconWrap);
        item.appendChild(name);

        item.addEventListener('click', () => openServerModal(app));
        appGrid.appendChild(item);
    });
}

// =============================================
// SERVER MODAL
// =============================================
function openServerModal(app) {
    modalTitle.textContent = app.name.replace('\n', ' ');

    if (app.icon_type === 'image' && app.icon_value) {
        // Handle external or internal image URL
        const imgSrc = app.icon_value.startsWith('http') ? app.icon_value : chrome.runtime.getURL(app.icon_value);
        modalAppIcon.style.background = 'transparent';
        modalAppIcon.innerHTML = `<img src="${imgSrc}" alt="${app.name}" style="width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block;" onerror="this.src='<?php echo $_base_url; ?>/inject/app_icons/default.png'">`;
    } else {
        modalAppIcon.style.background = app.color || 'var(--card-bg)';
        modalAppIcon.textContent = app.icon_value;
    }

    serverList.innerHTML = '';
    const hasCustomServers = app.custom_servers && Array.isArray(app.custom_servers) && app.custom_servers.length > 0;
    // Fix bugs: Stop capping non-custom servers to SERVER_REGIONS.length (which was 5)
    const count = hasCustomServers ? app.custom_servers.length : app.servers;

    for (let i = 0; i < count; i++) {
        const srvName = hasCustomServers ? app.custom_servers[i].name : `${app.name.replace('\n', ' ')} ${i + 1}`;
        const srvDesc = srvName;
        const btn = document.createElement('button');
        btn.className = 'server-btn';

        // Generate icon HTML based on dynamic flag
        let iconHtml = '';
        if (app.icon_type === 'image' && app.icon_value) {
            const imgSrc = app.icon_value.startsWith('http') ? app.icon_value : chrome.runtime.getURL(app.icon_value);
            iconHtml = `<div class="server-app-icon" style="background: transparent;"><img src="${imgSrc}" alt="${app.name}" style="width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block;" onerror="this.src='<?php echo $_base_url; ?>/inject/app_icons/default.png'"></div>`;
        } else {
            iconHtml = `<div class="server-app-icon" style="background: ${app.color || 'var(--card-bg)'};">${app.icon_value}</div>`;
        }

        btn.innerHTML = `
            ${iconHtml}
            <div class="server-info">
                <div class="server-name">${srvName}</div>
                <div class="server-desc">Dedicated server</div>
            </div>
            <span class="server-arrow">›</span>
        `;
        btn.addEventListener('click', () => {
            const serverBtn = btn;
            
            // === ANTI-MULTI-TAB UI: Disable semua tombol server agar tidak bisa diklik ulang ===
            const allServerBtns = serverList.querySelectorAll('.server-btn');
            allServerBtns.forEach(b => { b.disabled = true; b.style.opacity = '0.5'; b.style.pointerEvents = 'none'; });

            // Aktifkan Loading Overlay Animasi
            showCustomLoading();

            launchFloatingTab(app, srvName, i + 1).then((success) => {
                if (!success) {
                    // Jika gagal (success=false), sembunyikan loading & re-enable buttons agar user bisa coba yang lain
                    hideCustomLoading();
                    allServerBtns.forEach(b => { b.disabled = false; b.style.opacity = ''; b.style.pointerEvents = ''; });
                } else {
                    // Berhasil — tutup modal agar tidak bisa diklik lagi
                    closeModal();
                }
            }).catch(() => {
                hideCustomLoading();
                allServerBtns.forEach(b => { b.disabled = false; b.style.opacity = ''; b.style.pointerEvents = ''; });
            });
        });
        serverList.appendChild(btn);
    }

    modalOverlay.classList.remove('hidden');
}

function closeModal() {
    modalOverlay.classList.add('hidden');
}

modalClose.addEventListener('click', closeModal);
modalOverlay.addEventListener('click', (e) => {
    if (e.target === modalOverlay) closeModal();
});
document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape') closeModal();
});

// =============================================
// LAUNCH FLOATING TAB
// Opens the target URL dynamically in the exact same popup window bounds,
// bypassing the need to trigger a new browser tab/window popup logic.
// =============================================
function launchFloatingTab(app, serverName, serverIndex) {
    return new Promise(async (resolve) => {
        const url = app.url;
        // Deteksi jika berjalan di browser mobile (Android/iOS)
        const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);

        const iconSrc = app.icon_value.startsWith('http') ? app.icon_value : chrome.runtime.getURL(app.icon_value);
        const iconData = {
            type: app.icon_type,
            src: app.icon_type === 'image' ? iconSrc : app.icon_value,
            color: app.color
        };

        // Fallback ke url static dari setting app db
        let finalUrl = app.url;
        let originalTargetUrl = app.url; // Simpan url aslinya sebelum diganti ke go.php
        const hasCustomServers = app.custom_servers && Array.isArray(app.custom_servers) && app.custom_servers.length > 0;
        
        // Di backend PHP, jika hide_url aktif, app.url dan app.custom_servers[i].url 
        // sudah dienkripsi menjadi URL domain go.php
        if (hasCustomServers && app.custom_servers[serverIndex - 1] && app.custom_servers[serverIndex - 1].url) {
            finalUrl = app.custom_servers[serverIndex - 1].url;
            originalTargetUrl = app.custom_servers[serverIndex - 1].original_hostname ? `https://${app.custom_servers[serverIndex - 1].original_hostname}` : finalUrl;
        }

        // Cek apakah aplikasi membutuhkan injeksi cookies (db switch)
        const requiresCookies = app.requires_cookie;

        if (requiresCookies) {
            // --- FETCH & INJECT COOKIES ---
            const code = localStorage.getItem('texa_code');
            const deviceId = getDeviceId();

            try {
                const fetchUrl = `${API_URL}?action=get_cookies&code=${encodeURIComponent(code)}&device_id=${encodeURIComponent(deviceId)}&app_id=${encodeURIComponent(app.id)}&server_index=${serverIndex}&t=${Date.now()}`;
                const res = await fetch(fetchUrl, {
                    method: 'GET',
                    cache: 'no-store',
                    credentials: 'include'
                });

                const data = await res.json();

                // --- ELEGANT ALERTS (Untuk menyembunyikan mekanisme cookie dari user) ---
                if (!data.valid) {
                    showCustomAlert('Koneksi Ditolak', `Gagal terhubung ke jaringan: ${data.reason}`, '⚠️');
                    resolve(false); return;
                } else if (!data.cookies || data.cookies.length === 0) {
                    showCustomAlert('Akses Belum Tersedia', `Akun jaringan untuk ${app.name} (Server ${serverIndex}) belum diatur. Harap hubungi Administrator untuk menghubungkan akun.`, '🔗');
                    resolve(false); return;
                } else if (!Array.isArray(data.cookies)) {
                    showCustomAlert('Konfigurasi Rusak', `Sistem mendeteksi konfigurasi kredensial yang tidak valid untuk ${app.name}. Harap hubungi Administrator.`, '❌');
                    resolve(false); return;
                }
                // ---------------------------------------------------

                // Dapatkan target_url asli dari API untuk injeksi cookie.
                // Tapi JANGAN timpa finalUrl jika fitur hide_url aktif (karena finalUrl sudah di-set ke go.php sebelumnya).
                let cookieTargetUrl = data.target_url || originalTargetUrl || finalUrl;

                // Beritahu background script untuk melakukan sinkronisasi injeksi cookies
        const injectResponse = await new Promise((resolveInject) => {
            chromeMockRef.runtime.sendMessage({
                action: 'inject_cookies',
                app_id: app.id,
                server_index: serverIndex,
                cookies: data.cookies,
                cookie_payloads: data.cookie_payloads, 
                url: cookieTargetUrl 
            }, (response) => {
                resolveInject(response); 
            });
        });
        
        // --- JEDA AMAN (TUNGGU COOKIE MERESAP) SEBELUM BUKA TAB ---
        await new Promise(r => setTimeout(r, 800)); // Tambahan jeda 800ms
        
        // Cek hasil injeksi dan verifikasi background
                if (injectResponse && injectResponse.success === false) {
                    // Payload habis/mati — cukup batalkan launch tanpa popup
                    console.warn('[TEXA] Inject gagal: semua payload mati.');
                    resolve(false);
                    return;
                }
            } catch (e) {
                console.warn('Gagal fetch cookies:', e);
                showCustomAlert('Koneksi Gagal', 'Gagal membongkar cookie dari server utama. Pastikan koneksi internet stabil.', '🌐');
                resolve(false); return;
            }
            // -----------------------------
        }

        // Cegah launch kosong (misal URL statis tidak ada dan URL db juga tidak ada)
        if (!finalUrl) {
            showCustomAlert('Konfigurasi Error', 'URL target tidak ditemukan. Harap administrator memperbarui URL aplikasi.', '🔗');
            resolve(false);
            return;
        }

        // Simpan history server yang dipilih agar content.js bisa membacanya dan tambahkan ekstensi URL as back button
        chromeMockRef.storage.local.set({
            'texa_active_server': serverName,
            'texa_active_icon': JSON.stringify(iconData),
            'texa_active_app': JSON.stringify(app),
            'texa_popup_url': chrome.runtime.getURL('popup.html'),
            'texa_code': localStorage.getItem('texa_code'),
            'texa_device_id': getDeviceId()
        }, () => {
        // Beritahu background.js bahwa URL ini adalah active app URL agar diproteksi anti-multi-tab
        chromeMockRef.runtime.sendMessage({ action: 'register_active_app_url', url: finalUrl });

        // Register session for go.php direct access prevention
        const code = localStorage.getItem('texa_code');
        const doNavigate = () => {
            // Coba via background script (extension installed path)
            // Jika bridge tidak aktif, fallback otomatis ke direct navigation setelah 1.5 detik
            let navigated = false;

            // Beri sinyal ke background script - gunakan window.sendViaBridge (global scope)
            const bridgeFn = window.sendViaBridge;
            if (typeof bridgeFn === 'function') {
                bridgeFn({ action: 'open_target_tab', url: finalUrl }, (response) => {
                    // Background script merespons = extension aktif, tab sudah dibuka oleh background
                    navigated = true;
                    hideCustomLoading();
                    resolve(true);
                });
            }

            // Fallback timeout: jika bridge tidak merespons dalam 1.5 detik
            // (extension tidak terinstall atau content.js belum siap)
            setTimeout(() => {
                if (!navigated) {
                    console.log('[TEXA] Bridge tidak merespons, fallback ke navigasi langsung.');
                    // Navigasi ke URL target langsung di tab ini
                    window.location.href = finalUrl;
                    resolve(true);
                }
            }, 1500);
        };


        fetch(`${API_URL}?action=register_session`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: `code=${encodeURIComponent(code)}`,
            credentials: 'include'
        }).then(() => {
            doNavigate();
        }).catch(err => {
            console.error('Session registration failed', err);
            doNavigate();
        });
    });
    });
}


// =============================================
// VERSION UPDATE CHECK & POPUP
// =============================================
function showVersionUpdatePopup(updateData, currentVersion) {
    if (!updateData || !updateData.title) return;

    // Guard: hanya tampilkan 1x per session
    if (sessionStorage.getItem('texa_update_popup_shown')) return;
    sessionStorage.setItem('texa_update_popup_shown', '1');

    const overlay = document.createElement('div');
    overlay.id = 'version-update-overlay';
    overlay.style.cssText = `
        position: fixed; top: 0; left: 0; width: 100%; height: 100%;
        background: rgba(0, 5, 15, 0.92); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
        z-index: 2147483647; display: flex; align-items: center; justify-content: center;
        font-family: 'Inter', system-ui, sans-serif; padding: 20px; box-sizing: border-box;
        animation: vuFadeIn 0.4s ease-out;
    `;

    let ctaHtml = '';
    if (updateData.btn_text && updateData.btn_link) {
        ctaHtml = `
            <a href="${updateData.btn_link}" target="_blank" id="vu-download-btn" style="
                display:flex; align-items:center; justify-content:center; gap:8px;
                width:100%; padding:14px 20px; margin-top:16px;
                background: linear-gradient(135deg, #ef4444, #f97316);
                color: #fff; text-decoration: none; border-radius: 14px;
                font-weight: 800; font-size: 15px; letter-spacing: 0.3px;
                box-shadow: 0 4px 20px rgba(239, 68, 68, 0.4);
                transition: all 0.3s; box-sizing: border-box;
            ">
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
                ${updateData.btn_text}
            </a>
        `;
    }

    overlay.innerHTML = `
        <style>
            @keyframes vuFadeIn { from { opacity:0; } to { opacity:1; } }
            @keyframes vuSlideUp { from { opacity:0; transform:translateY(30px) scale(0.95); } to { opacity:1; transform:translateY(0) scale(1); } }
            @keyframes vuPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } }
            @keyframes vuGlow { 0%, 100% { box-shadow: 0 0 20px rgba(239, 68, 68, 0.3); } 50% { box-shadow: 0 0 40px rgba(239, 68, 68, 0.6); } }
            #vu-download-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 28px rgba(239, 68, 68, 0.6); }
            #vu-close-btn:hover { background: rgba(255,255,255,0.08) !important; color: #fff !important; }
        </style>
        <div style="
            background: linear-gradient(160deg, rgba(30, 10, 10, 0.98), rgba(10, 4, 18, 0.98));
            border: 1px solid rgba(239, 68, 68, 0.3);
            border-radius: 24px; width: 100%; max-width: 420px;
            max-height: 90vh; overflow-y: auto;
            padding: 28px 24px; box-sizing: border-box;
            animation: vuSlideUp 0.5s cubic-bezier(0.16, 1, 0.3, 1);
        ">
            <!-- UPDATE ICON -->
            <div style="text-align:center; margin-bottom:16px;">
                <div style="
                    display:inline-flex; align-items:center; justify-content:center;
                    width:72px; height:72px; border-radius:20px;
                    background: linear-gradient(135deg, rgba(239, 68, 68, 0.15), rgba(249, 115, 22, 0.15));
                    border: 1px solid rgba(239, 68, 68, 0.3);
                    animation: vuGlow 3s ease-in-out infinite;
                ">
                    <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <path d="M12 2v10l4-4"/><path d="M12 12l-4-4"/>
                        <path d="M4 14.899A7 7 0 1 0 15.71 8h-1.79a4.5 4.5 0 0 0-8.42 2.5"/>
                    </svg>
                </div>
            </div>

            <!-- VERSION BADGE -->
            <div style="text-align:center; margin-bottom:12px;">
                <span style="
                    display:inline-block; padding:4px 14px; border-radius:20px;
                    background: rgba(239, 68, 68, 0.15); border: 1px solid rgba(239, 68, 68, 0.3);
                    color: #f87171; font-size:11px; font-weight:700; letter-spacing:0.5px;
                ">v${currentVersion} → v${updateData.latest_version}</span>
            </div>

            <!-- TITLE -->
            <h2 style="
                text-align:center; margin:0 0 10px; font-size:22px; font-weight:800;
                background: linear-gradient(135deg, #ef4444, #f97316);
                -webkit-background-clip: text; -webkit-text-fill-color: transparent;
                background-clip: text;
            ">${updateData.title}</h2>

            <!-- DESCRIPTION -->
            <p style="
                text-align:center; color:#d1d5db; font-size:14px; line-height:1.6;
                margin:0 0 8px;
            ">${updateData.desc}</p>

            <!-- CTA BUTTON -->
            ${ctaHtml}

            <!-- CLOSE -->
            <button id="vu-close-btn" style="
                display:block; width:100%; margin-top:12px; padding:10px;
                background: transparent; border: 1px solid rgba(255,255,255,0.1);
                color: #6b7280; border-radius:12px; cursor:pointer;
                font-size:12px; font-family:inherit; transition:all 0.2s;
            ">Nanti Saja</button>
        </div>
    `;

    document.body.appendChild(overlay);

    // Event listeners
    overlay.querySelector('#vu-close-btn').addEventListener('click', () => {
        overlay.style.animation = 'vuFadeIn 0.3s ease-out reverse';
        setTimeout(() => overlay.remove(), 280);
    });
}

async function checkExtensionVersion() {
    try {
        // ============================================================
        // STRATEGI DETEKSI VERSI (Orion iOS + Chrome Desktop):
        // 1. URL param ?ext_version= (paling reliable — dikirim langsung oleh background.js)
        // 2. Bridge sendMessage get_version (fallback jika URL param tidak ada)
        // 3. window.__origChrome.runtime.getManifest() (fallback terakhir, desktop only)
        // ============================================================
        let currentVersion = null;

        // --- PRIORITAS 1: Baca dari URL param (paling cepat & reliable) ---
        const _urlP = new URLSearchParams(window.location.search);
        const urlVersion = _urlP.get('ext_version');
        if (urlVersion && urlVersion.trim()) {
            currentVersion = urlVersion.trim();
            console.log(`[TEXA Version] Detected from URL param: ${currentVersion}`);
        }

        // --- PRIORITAS 2: localStorage (jika pernah berhasil sebelumnya) ---
        if (!currentVersion) {
            const cachedVersion = localStorage.getItem('texa_ext_version');
            if (cachedVersion) {
                currentVersion = cachedVersion;
                console.log(`[TEXA Version] Detected from localStorage cache: ${currentVersion}`);
            }
        }

        // --- PRIORITAS 3: Bridge sendMessage ke background.js ---
        if (!currentVersion) {
            const bridgeVersion = await new Promise((resolve) => {
                let resolved = false;
                const bridgeFn = window.sendViaBridge;
                if (typeof bridgeFn === 'function') {
                    bridgeFn({ action: 'get_version' }, (res) => {
                        resolved = true;
                        resolve(res && res.success ? res.version : null);
                    });
                } else {
                    resolve(null);
                }
                // Timeout 2 detik
                setTimeout(() => { if (!resolved) resolve(null); }, 2000);
            });
            if (bridgeVersion) {
                currentVersion = bridgeVersion;
                console.log(`[TEXA Version] Detected from bridge: ${currentVersion}`);
            }
        }

        // --- PRIORITAS 4: getManifest() (hanya di desktop Chrome, extension page) ---
        if (!currentVersion) {
            try {
                const realChrome = window.__origChrome;
                if (realChrome && realChrome.runtime && realChrome.runtime.getManifest) {
                    const manifest = realChrome.runtime.getManifest();
                    if (manifest && manifest.version) {
                        currentVersion = manifest.version;
                        console.log(`[TEXA Version] Detected from getManifest(): ${currentVersion}`);
                    }
                }
            } catch(e) {}
        }

        if (!currentVersion) {
            console.log('[TEXA Version] Could not detect extension version.');
            return;
        }

        // Simpan ke localStorage untuk request berikutnya
        localStorage.setItem('texa_ext_version', currentVersion);
        console.log(`[TEXA Version] Checking version: ${currentVersion}`);

        // 2. Cek dengan server API
        const res = await fetch(`${API_URL}?action=check_version&v=${encodeURIComponent(currentVersion)}`, {
            cache: 'no-store'
        });
        if (!res.ok) return;
        
        const data = await res.json();
        if (data.needs_update && data.update_data) {
            console.log(`[TEXA Version] Update tersedia! ${currentVersion} → ${data.update_data.latest_version}`);
            showVersionUpdatePopup(data.update_data, currentVersion);
        } else {
            console.log(`[TEXA Version] Extension sudah versi terbaru (${currentVersion}).`);
        }
    } catch (e) {
        console.warn('[TEXA Version] Version check failed:', e);
    }
}


// =============================================
// INIT
// =============================================
generateParticles();

// Check extension version on page load (with short delay to let extension connect)
setTimeout(checkExtensionVersion, 1500);

// Fetch Katalog URL dari Backend secara dinamis
async function fetchCatalogUrl() {
    try {
        const res = await fetch(`${API_URL}?action=get_catalog_url`, { cache: 'no-store' });
        if (res.ok) {
            const data = await res.json();
            const catalogLinkTop = document.getElementById('catalog-link-top');
            const catalogLinkStatic = document.getElementById('catalog-link-static');
            
            if (data.url) {
                if (catalogLinkTop) {
                    catalogLinkTop.href = data.url;
                    // Hanya munculkan jika kita sedang berada di layar login
                    if (!loginScreen.classList.contains('hidden')) {
                        catalogLinkTop.style.display = 'block';
                    }
                }
                
                if (catalogLinkStatic) {
                    catalogLinkStatic.href = data.url;
                    catalogLinkStatic.style.display = 'flex'; // Munculkan tombol static
                }

                // Jika data.apps tersedia dari backend (daftar aplikasi), kita buat loop-nya
                if (data.apps && Array.isArray(data.apps) && data.apps.length > 0) {
                    const track = document.getElementById('logoloop-track-top');
                    if (track) {
                        let html = '';
                        // Kita gandakan array 2x agar animasi loop CSS terlihat mulus (infinite marquee effect)
                        const loopItems = [...data.apps, ...data.apps];
                        
                        loopItems.forEach(app => {
                            if (!app.is_active) return;
                            let imgSrc = '<?php echo $_base_url; ?>/inject/app_icons/default.png'; // Fallback
                            if (app.icon_type === 'image') {
                                imgSrc = app.icon_value; // Use direct URL from database
                            } else if (app.icon_type === 'url') {
                                imgSrc = app.icon_value || imgSrc;
                            }
                            html += `
                                <div class="logoloop-item">
                                    <img src="${imgSrc}" onerror="this.src='<?php echo $_base_url; ?>/inject/app_icons/default.png'" alt="${app.name}">
                                    <span>${app.name}</span>
                                </div>
                            `;
                        });
                        track.innerHTML = html;
                    }
                }
            }
        }
    } catch (e) {
        console.warn('Gagal memuat URL katalog:', e);
    }
}
fetchCatalogUrl();

const urlParams = new URLSearchParams(window.location.search);

// ⚡ PENTING: Simpan ext_version ke localStorage SEBELUM replaceState membersihkan URL
// Jika tidak, checkExtensionVersion (1.5 detik kemudian) tidak bisa baca ?ext_version lagi
const _rawExtVersion = urlParams.get('ext_version');
if (_rawExtVersion && _rawExtVersion.trim()) {
    // Selalu update cache dengan versi yang dikirim extension (bukan read-only)
    localStorage.setItem('texa_ext_version', _rawExtVersion.trim());
    console.log('[TEXA Version] Captured ext_version before replaceState:', _rawExtVersion.trim());
}

if (urlParams.get('fresh') === '1') {
    setLoggedIn(false); // Clear session if opened fresh from the extension icon
    // Bersihkan parameter dari URL agar reload halaman tidak memicu logout lagi
    const cleanUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
    window.history.replaceState({ path: cleanUrl }, '', cleanUrl);
}

if (urlParams.get('kicked_out') === '1') {
    localStorage.removeItem('texa_code');
    chromeMockRef.storage.local.remove(['texa_code', 'texa_device_id', 'texa_active_server', 'texa_active_icon', 'texa_active_app', 'texa_popup_url']);
    setLoggedIn(false);
    goToLogin();
    
    // Tunggu customAlert dideklarasi di bawah, jadi setTimeout kecil
    setTimeout(() => {
        chromeMockRef.storage.local.get(['texa_kick_settings'], (result) => {
            const ks = result.texa_kick_settings || {};
            const title = ks.title || 'Sesi Berakhir.!!!';
            const logo = ks.logo || '⚠️';
            showCustomAlert(title, 'Sesi login Anda telah direbut dan digunakan di perangkat atau jendela aplikasi lain.', logo);
        });
    }, 100);

    const cleanUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
    window.history.replaceState({ path: cleanUrl }, '', cleanUrl);
}

// Custom Element Logic
const customAlertEl = document.getElementById('custom-alert');
const customAlertCloseBtn = document.getElementById('custom-alert-close');
function showCustomAlert(title, message, icon, actionObj) {
    document.getElementById('custom-alert-title').textContent = title;
    document.getElementById('custom-alert-msg').textContent = message;
    document.getElementById('custom-alert-icon').textContent = icon || '⚠️';
    
    const actionBtn = document.getElementById('custom-alert-action');
    const closeBtn = document.getElementById('custom-alert-close');
    if (actionBtn) {
        if (actionObj && actionObj.link) {
            actionBtn.textContent = actionObj.text || 'Action';
            actionBtn.href = actionObj.link;
            actionBtn.style.display = 'flex';
            if (closeBtn) {
                closeBtn.style.background = 'transparent';
                closeBtn.style.border = '1px solid rgba(255,255,255,0.1)';
                closeBtn.style.boxShadow = 'none';
                closeBtn.style.color = '#9ca3af';
            }
        } else {
            actionBtn.style.display = 'none';
            if (closeBtn) {
                closeBtn.style.background = '';
                closeBtn.style.border = '';
                closeBtn.style.boxShadow = '';
                closeBtn.style.color = '';
            }
        }
    }
    
    customAlertEl.classList.remove('hidden');
}
customAlertCloseBtn.addEventListener('click', () => {
    customAlertEl.classList.add('hidden');
});

// Expiry Popup Logic
function showExpiryPopup(settings, code) {
    const alertOverlay = document.createElement('div');
    alertOverlay.style.cssText = `
        position: fixed; top: 0; left: 0; width: 100%; height: 100%;
        background: rgba(0, 5, 15, 0.85); backdrop-filter: blur(5px);
        z-index: 2147483647; display: flex; align-items: center; justify-content: center;
        font-family: 'Inter', system-ui, sans-serif;
    `;
    alertOverlay.innerHTML = `
        <div style="background: linear-gradient(145deg, #2a1b05, #110a01); border: 1px solid rgba(255, 170, 0, 0.4); border-radius: 20px; width: 90%; max-width: 400px; padding: 32px; text-align: center; box-shadow: 0 20px 50px rgba(0,0,0,0.8); animation: fadeInScale 0.3s ease-out forwards;">
            <div style="font-size: 48px; margin-bottom: 16px;">${settings.logo || '⏳'}</div>
            <h3 style="margin: 0 0 12px 0; color: #ffcc00; font-size: 22px; font-weight: bold;">${settings.title}</h3>
            <p style="margin: 0 0 20px 0; color: #d1d5db; font-size: 15px; line-height: 1.6;">
                ${settings.text}
            </p>
            <a href="${settings.btn_link}" target="_blank" id="exp-action-btn" style="display: block; width: 100%; padding: 14px; background: linear-gradient(135deg, #ffcc00, #ffaa00); color: #000; text-decoration: none; border-radius: 12px; font-weight: bold; font-size: 16px; margin-bottom: 12px; box-sizing: border-box; transition: all 0.2s; box-shadow: 0 4px 15px rgba(255, 170, 0, 0.3);">
                ${settings.btn_text}
            </a>
            <button id="exp-close-btn" style="width: 100%; padding: 14px; background: transparent; color: #9ca3af; border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; font-weight: bold; font-size: 14px; cursor: pointer; transition: all 0.2s;">
                Tutup Peringatan
            </button>
        </div>
    `;
    document.body.appendChild(alertOverlay);
    
    document.getElementById('exp-close-btn').addEventListener('click', () => {
        alertOverlay.remove();
    });

    document.getElementById('exp-action-btn').addEventListener('click', () => {
        alertOverlay.remove();
    });

    // Log the popup
    try {
        fetch(`${API_URL}?action=log_popup&code=${encodeURIComponent(code)}`, { cache: 'no-store' });
    } catch(e) {}
}

// ========== PROMO POPUP ==========
function convertToEmbedUrl(url) {
    if (!url) return '';
    // YouTube
    let m = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/shorts\/)([a-zA-Z0-9_-]{11})/);
    if (m) return `https://www.youtube.com/embed/${m[1]}?autoplay=1&rel=0`;
    // TikTok
    m = url.match(/tiktok\.com\/@[^\/]+\/video\/(\d+)/);
    if (m) return `https://www.tiktok.com/embed/v2/${m[1]}`;
    // Facebook video
    if (url.includes('facebook.com') && (url.includes('/videos/') || url.includes('/watch/'))) {
        return `https://www.facebook.com/plugins/video.php?href=${encodeURIComponent(url)}&show_text=false&width=560`;
    }
    // Instagram
    m = url.match(/instagram\.com\/(?:reel|p)\/([a-zA-Z0-9_-]+)/);
    if (m) return `https://www.instagram.com/p/${m[1]}/embed`;
    // Direct video (.mp4, .webm)
    if (url.match(/\.(mp4|webm|ogg)(\?|$)/i)) return url;
    // Already an embed URL
    if (url.includes('/embed')) return url;
    return url;
}

function showPromoPopup(promoData) {
    if (!promoData || (!promoData.title && !promoData.video_url && !promoData.desc)) return;

    const overlay = document.createElement('div');
    overlay.id = 'promo-popup-overlay';

    // Detect video orientation from URL
    let isVertical = false;
    let videoHtml = '';
    if (promoData.video_url) {
        const url = promoData.video_url.toLowerCase();
        isVertical = url.includes('tiktok.com') ||
                     url.includes('youtube.com/shorts') ||
                     url.includes('instagram.com/reel') ||
                     url.includes('instagram.com/stories');

        const embedUrl = convertToEmbedUrl(promoData.video_url);
        if (promoData.video_url.match(/\.(mp4|webm|ogg)(\?|$)/i)) {
            videoHtml = `<video src="${embedUrl}" controls autoplay muted playsinline class="promo-video ${isVertical ? 'vertical' : 'horizontal'}"></video>`;
        } else {
            videoHtml = `<iframe src="${embedUrl}" class="promo-video-iframe ${isVertical ? 'vertical' : 'horizontal'}" allow="autoplay; encrypted-media; picture-in-picture; gyroscope; accelerometer" allowfullscreen></iframe>`;
        }
    }

    // Build CTA button
    let ctaHtml = '';
    if (promoData.btn_text && promoData.btn_link) {
        ctaHtml = `
            <a href="${promoData.btn_link}" target="_blank" id="promo-cta-btn" class="promo-cta-btn">
                ${promoData.btn_text}
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
            </a>
        `;
    }

    overlay.innerHTML = `
        <style>
            @keyframes promoFadeIn { from { opacity:0; } to { opacity:1; } }
            @keyframes promoSlideUp { from { opacity:0; transform:translateY(30px) scale(0.95); } to { opacity:1; transform:translateY(0) scale(1); } }

            #promo-popup-overlay {
                position: fixed; top: 0; left: 0; width: 100%; height: 100%;
                background: rgba(0, 5, 15, 0.92); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
                z-index: 2147483647; display: flex; align-items: center; justify-content: center;
                font-family: 'Inter', system-ui, -apple-system, sans-serif;
                padding: 16px; box-sizing: border-box;
                animation: promoFadeIn 0.4s ease-out;
            }

            .promo-modal {
                background: linear-gradient(160deg, rgba(4, 22, 48, 0.98), rgba(2, 12, 24, 0.98));
                border: 1px solid rgba(0, 180, 255, 0.25);
                border-radius: 20px; width: 100%;
                max-height: 90vh; max-height: 90dvh;
                overflow-y: auto; overflow-x: hidden;
                box-shadow: 0 25px 60px rgba(0,0,0,0.8), 0 0 40px rgba(0,150,255,0.1);
                animation: promoSlideUp 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
                -webkit-overflow-scrolling: touch;
            }
            /* Scrollbar styling */
            .promo-modal::-webkit-scrollbar { width: 4px; }
            .promo-modal::-webkit-scrollbar-track { background: transparent; }
            .promo-modal::-webkit-scrollbar-thumb { background: rgba(0,150,255,0.3); border-radius: 4px; }

            /* === VIDEO CONTAINER === */
            .promo-video-wrap {
                padding: 0 12px; margin-bottom: 10px;
                display: flex; justify-content: center;
                max-height: calc(100vh - 220px);
                max-height: calc(100dvh - 220px);
                border-radius: 14px;
            }

            /* Horizontal video (YouTube, Facebook) — 16:9 */
            .promo-video-iframe.horizontal,
            .promo-video.horizontal {
                width: 100%; aspect-ratio: 16/9;
                max-height: calc(100vh - 220px);
                max-height: calc(100dvh - 220px);
                border: none; border-radius: 12px;
                background: #000; display: block;
                object-fit: contain;
            }

            /* Vertical video (TikTok, Shorts, Reels) — fits viewport height */
            .promo-video-iframe.vertical,
            .promo-video.vertical {
                width: auto; height: calc(100vh - 230px);
                height: calc(100dvh - 230px);
                max-width: 100%; aspect-ratio: 9/16;
                border: none; border-radius: 14px;
                background: #000; display: block;
                object-fit: contain; margin: 0 auto;
            }

            /* Header */
            .promo-header {
                display: flex; justify-content: flex-end;
                padding: 12px 14px 0; flex-shrink: 0;
            }
            .promo-close-x {
                background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1);
                color: #8ba4c9; width: 34px; height: 34px; border-radius: 10px;
                cursor: pointer; display: flex; align-items: center; justify-content: center;
                font-size: 18px; transition: all 0.2s; flex-shrink: 0;
            }
            .promo-close-x:hover { background: rgba(255,255,255,0.1); color: #fff; }

            /* Content — fixed at bottom, never scrolled away */
            .promo-content { padding: 0 20px 16px; flex-shrink: 0; }
            .promo-title {
                margin: 0 0 6px 0; font-size: 18px; font-weight: 900;
                background: linear-gradient(135deg, #ffffff 0%, #00d4ff 50%, #0088ff 100%);
                -webkit-background-clip: text; -webkit-text-fill-color: transparent;
                background-clip: text; line-height: 1.3; letter-spacing: -0.3px;
            }
            .promo-desc {
                color: #c8d6e5; font-size: 13px; line-height: 1.5; margin-bottom: 2px;
            }

            /* CTA Button */
            .promo-cta-btn {
                display: flex; align-items: center; justify-content: center; gap: 8px;
                width: 100%; padding: 12px 20px; margin-top: 10px;
                background: linear-gradient(135deg, #0066ff, #00ccff);
                color: #fff; text-decoration: none; border-radius: 14px;
                font-weight: 800; font-size: 15px; letter-spacing: 0.3px;
                box-shadow: 0 4px 20px rgba(0, 150, 255, 0.4);
                transition: all 0.3s; box-sizing: border-box;
            }
            .promo-cta-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 28px rgba(0, 150, 255, 0.6); }

            /* Dismiss Button */
            .promo-dismiss-btn {
                width: 100%; margin-top: 8px; padding: 10px;
                background: transparent; color: #6b7280;
                border: 1px solid rgba(255,255,255,0.06); border-radius: 12px;
                font-weight: 600; font-size: 13px; cursor: pointer;
                transition: all 0.2s;
            }
            .promo-dismiss-btn:hover { background: rgba(255,255,255,0.06); color: #fff; }

            /* === DESKTOP === */
            @media (min-width: 600px) {
                .promo-modal { max-width: 500px; }
                .promo-title { font-size: 20px; }
                .promo-content { padding: 0 24px 20px; }
            }

            /* === MOBILE === */
            @media (max-width: 599px) {
                #promo-popup-overlay { padding: 8px; }
                .promo-modal {
                    max-width: 100%; border-radius: 18px;
                    max-height: 98vh; max-height: 98dvh;
                }
                .promo-video-wrap {
                    max-height: calc(100vh - 200px);
                    max-height: calc(100dvh - 200px);
                }
                .promo-video-iframe.vertical,
                .promo-video.vertical {
                    height: calc(100vh - 210px);
                    height: calc(100dvh - 210px);
                }
                .promo-video-iframe.horizontal,
                .promo-video.horizontal {
                    max-height: calc(100vh - 200px);
                    max-height: calc(100dvh - 230px);
                }
                .promo-title { font-size: 16px; }
                .promo-desc { font-size: 12px; line-height: 1.5; }
                .promo-cta-btn { padding: 11px 16px; font-size: 14px; border-radius: 12px; }
                .promo-dismiss-btn { padding: 9px; font-size: 12px; }
                .promo-content { padding: 0 14px 14px; }
                .promo-header { padding: 10px 10px 0; }
            }
        </style>
        <div class="promo-modal">
            <!-- Close Button -->
            <div class="promo-header">
                <button class="promo-close-x" id="promo-close-btn">✕</button>
            </div>

            <!-- Video Section -->
            ${videoHtml ? `<div class="promo-video-wrap">${videoHtml}</div>` : ''}

            <!-- Content -->
            <div class="promo-content">
                ${promoData.title ? `<h2 class="promo-title">${promoData.title}</h2>` : ''}
                ${promoData.desc ? `<div class="promo-desc">${promoData.desc}</div>` : ''}
                ${ctaHtml}
                <button id="promo-dismiss-btn" class="promo-dismiss-btn">Tutup</button>
            </div>
        </div>
    `;

    document.body.appendChild(overlay);

    // Event listeners
    document.getElementById('promo-close-btn').addEventListener('click', () => overlay.remove());
    document.getElementById('promo-dismiss-btn').addEventListener('click', () => overlay.remove());
    const ctaBtn = document.getElementById('promo-cta-btn');
    if (ctaBtn) ctaBtn.addEventListener('click', () => setTimeout(() => overlay.remove(), 300));
    overlay.addEventListener('click', (e) => { if (e.target === overlay) overlay.remove(); });
}

if (isLoggedIn()) {
    const savedCols = localStorage.getItem('texa_desktop_cols');
    if (savedCols) {
        document.documentElement.style.setProperty('--desktop-cols', savedCols);
    }
    
    goToDashboard();
    renderAppGrid();
    
    // Cek popup expiry dari cache saat diload
    chromeMockRef.storage.local.get(['texa_show_exp_popup', 'texa_exp_settings', 'texa_code', 'texa_promo_popup'], (res) => {
        if (res.texa_show_exp_popup && res.texa_exp_settings) {
            if (!sessionStorage.getItem('exp_popup_shown')) {
                showExpiryPopup(res.texa_exp_settings, res.texa_code || 'Unknown');
                sessionStorage.setItem('exp_popup_shown', 'true');
            }
        }
        // Show promo popup (after expiry popup or standalone)
        if (res.texa_promo_popup) {
            if (!sessionStorage.getItem('promo_popup_shown')) {
                setTimeout(() => {
                    showPromoPopup(res.texa_promo_popup);
                    sessionStorage.setItem('promo_popup_shown', 'true');
                }, res.texa_show_exp_popup ? 1000 : 300);
            }
        }
    });
    
    // Pastikan disembunyikan saat aplikasi dimuat langsung ke dashboard
    const topBanner = document.getElementById('catalog-link-top');
    if (topBanner) topBanner.style.display = 'none';
}

</script>

</body>

</html>