<?php
// TEXA Apps - Hosted Web Version
// File ini merupakan gabungan dari popup.html, popup.css, dan popup.js
?>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>TEXA Apps Launcher</title>
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;900&display=swap" rel="stylesheet" />
  <style>
    /* ============================================
       TEXA Apps - Dark Ocean Blue Theme
       ============================================ */

    *,
    *::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 */
    }

    .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>
</head>

<body>

  <!-- ========= 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">
          <img src="icons/icon128.png" alt="TEXA Apps Logo" onerror="this.style.display='none'"
            style="width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block;">
        </div>
      </div>
      <h1 class="brand-name">TEXA APPS</h1>
      <p class="brand-sub">Premium AI Workflow Suite</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>
        Full Katalog AI Tools
      </a>

      <div class="input-group">
        <label class="input-label">Access Code</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="Enter access code..."
            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>Kode akses tidak valid.</span>
        </div>
      </div>

      <button class="btn-primary" id="login-btn">
        <span>Masuk</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>Ambil Alih Sesi</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">TEXA.STUDIO &copy; 2026 copyright.</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="icons/icon48.png" alt="Logo" onerror="this.style.display='none'"
            style="width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block;">
        </div>
        <span>TEXA Apps</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 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>
      <button class="btn-primary" id="custom-alert-close" style="width: 100%;">Tutup</button>
    </div>
  </div>

  <script>
    // TEXA Apps - Hosted Popup Logic
    'use strict';

    // Helper functions to mock chrome API gracefully for web environment
    const isExtension = typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.id;
    
    const ExtMock = {
      getURL: (path) => isExtension ? chrome.runtime.getURL(path) : path,
      sendMessage: (msg, callback) => {
        if (isExtension && chrome.runtime.sendMessage) {
          chrome.runtime.sendMessage(msg, callback);
        } else {
          console.log('[Web Mock] Message sent to extension:', msg);
          if (callback) callback({ success: true, mocked: true });
        }
      },
      storageSet: (obj, callback) => {
        if (isExtension && chrome.storage && chrome.storage.local) {
          chrome.storage.local.set(obj, callback);
        } else {
          // Fallback to localStorage if running on web
          for (let key in obj) {
            if (typeof obj[key] === 'object') {
              localStorage.setItem(key, JSON.stringify(obj[key]));
            } else {
              localStorage.setItem(key, obj[key]);
            }
          }
          if (callback) callback();
        }
      },
      storageRemove: (keys, callback) => {
        if (isExtension && chrome.storage && chrome.storage.local) {
          chrome.storage.local.remove(keys, callback);
        } else {
          let kArray = Array.isArray(keys) ? keys : [keys];
          kArray.forEach(k => localStorage.removeItem(k));
          if (callback) callback();
        }
      }
    };

    // API URL — menggunakan Cloudflare Worker sebagai proxy atau URL backend langsung
    const API_URL = 'https://texa-app.teknoaiglobal.workers.dev/';

    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) {
        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) : '');
        } else {
            ['texa_auth', 'texa_name', 'texa_expiry', 'texa_apps'].forEach(k => localStorage.removeItem(k));
        }
    }

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

        // Sembunyikan banner logo loop saat masuk ke App Launcher
        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');
            ExtMock.storageRemove(['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);

        if (msg.includes('perangkat lain')) {
            const takeoverBtn = document.getElementById('takeover-btn');
            if (takeoverBtn) {
                takeoverBtn.style.display = 'flex';
                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) {
                            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;
        }

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

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

            if (!res.ok) throw new Error('Server error');

            const data = await res.json();

            if (data.valid) {
                localStorage.setItem('texa_code', code); 
                ExtMock.storageSet({
                    'texa_code': code,
                    'texa_device_id': getDeviceId(),
                    'texa_apps': data.apps || []
                });
                setLoggedIn(true, data.name, data.expiry, data.apps || null);
                goToDashboard();
                renderAppGrid();
                
                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.');

                const takeoverBtn = document.getElementById('takeover-btn');
                if (takeoverBtn) {
                    if (data.reason === 'device_in_use') {
                        takeoverBtn.style.display = 'flex';
                    } else {
                        takeoverBtn.style.display = 'none';
                    }
                }
            }
        } catch (err) {
            showError('Gagal terhubung ke server. Periksa koneksi internet.');
        } finally {
            loginBtn.disabled = false;
            loginBtn.querySelector('span').textContent = 'Masuk';
        }
    }

    // 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
    // =============================================
    function renderAppGrid() {
        appGrid.innerHTML = '';

        let appsConfig = [];
        try {
            const raw = localStorage.getItem('texa_apps');
            if (raw) appsConfig = JSON.parse(raw);
        } catch (e) { appsConfig = []; }

        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;
        }

        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');
                img.src = app.icon_value.startsWith('http') ? app.icon_value : ExtMock.getURL(app.icon_value);
                img.alt = app.name;
                img.style.cssText = 'width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block;';

                img.onerror = function () {
                    this.onerror = null; 
                    this.src = ExtMock.getURL('icons/icon48.png');
                };

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

            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) {
            const imgSrc = app.icon_value.startsWith('http') ? app.icon_value : ExtMock.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='${ExtMock.getURL('icons/icon48.png')}'">`;
        } else {
            modalAppIcon.style.background = app.color || 'var(--card-bg)';
            modalAppIcon.textContent = app.icon_value;
        }

        serverList.innerHTML = '';
        const count = Math.min(app.servers, SERVER_REGIONS.length);

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

            let iconHtml = '';
            if (app.icon_type === 'image' && app.icon_value) {
                const imgSrc = app.icon_value.startsWith('http') ? app.icon_value : ExtMock.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='${ExtMock.getURL('icons/icon48.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;
                const originalHtml = serverBtn.innerHTML;
                const originalColor = serverBtn.style.color;
                serverBtn.style.color = '#4db8ff';
                serverBtn.innerHTML = `
                    <div style="display:flex;align-items:center;justify-content:center;width:100%;gap:10px;">
                       <svg style="animation:spin 1s linear infinite;width:16px;height:16px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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" stroke-linecap="round"/></svg>
                       <span>Menghubungkan Server...</span>
                    </div>
                `;
                serverBtn.disabled = true;

                launchFloatingTab(app, srvName, i + 1).finally(() => {
                    serverBtn.innerHTML = originalHtml;
                    serverBtn.style.color = originalColor;
                    serverBtn.disabled = false;
                    closeModal();
                });
            });
            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
    // =============================================
    function launchFloatingTab(app, serverName, serverIndex) {
        return new Promise(async (resolve) => {
            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 : ExtMock.getURL(app.icon_value);
            const iconData = {
                type: app.icon_type,
                src: app.icon_type === 'image' ? iconSrc : app.icon_value,
                color: app.color
            };

            let finalUrl = app.url;
            const requiresCookies = app.requires_cookie;

            if (requiresCookies) {
                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'
                    });

                    const data = await res.json();

                    if (!data.valid) {
                        showCustomAlert('Koneksi Ditolak', `Gagal terhubung ke jaringan: ${data.reason}`, '⚠️');
                        resolve(); 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(); return;
                    } else if (!Array.isArray(data.cookies)) {
                        showCustomAlert('Konfigurasi Rusak', `Sistem mendeteksi konfigurasi kredensial yang tidak valid untuk ${app.name}. Harap hubungi Administrator.`, '❌');
                        resolve(); return;
                    }

                    if (data.target_url) {
                        finalUrl = data.target_url;
                    }

                    // Beritahu background script (jika di ekstensi) untuk sinkronisasi injeksi cookies
                    await new Promise((resolveInject) => {
                        ExtMock.sendMessage({
                            action: 'inject_cookies',
                            cookies: data.cookies,
                            url: finalUrl
                        }, (response) => {
                            resolveInject();
                        });
                    });
                } catch (e) {
                    console.warn('Gagal fetch cookies:', e);
                    showCustomAlert('Koneksi Gagal', 'Gagal membongkar cookie dari server utama. Pastikan koneksi internet stabil.', '🌐');
                    resolve(); return;
                }
            }

            if (!finalUrl) {
                showCustomAlert('Konfigurasi Error', 'URL target tidak ditemukan. Harap administrator memperbarui URL aplikasi.', '🔗');
                resolve();
                return;
            }

            ExtMock.storageSet({
                'texa_active_server': serverName,
                'texa_active_icon': JSON.stringify(iconData),
                'texa_active_app': JSON.stringify(app),
                'texa_popup_url': window.location.href // use current web URL
            }, () => {
                ExtMock.sendMessage({ action: 'register_active_app_url', url: finalUrl });
                
                // Jika ini adalah website biasa, kita tidak memiliki kontrol terhadap tab lain sebaik ekstensi.
                // Untuk desktop maupun mobile, kita arahkan secara langsung, atau buka di tab baru
                if (!isExtension) {
                    // Coba buka di tab baru agar tidak kehilangan launcher web ini
                    window.open(finalUrl, '_blank');
                } else {
                    if (isMobile) {
                        chrome.tabs.update(null, { url: finalUrl });
                    } else {
                        window.location.href = finalUrl;
                    }
                }
                resolve();
            });
        });
    }

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

    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;
                        if (!loginScreen.classList.contains('hidden')) {
                            catalogLinkTop.style.display = 'block';
                        }
                    }
                    
                    if (catalogLinkStatic) {
                        catalogLinkStatic.href = data.url;
                        catalogLinkStatic.style.display = 'flex';
                    }

                    if (data.apps && Array.isArray(data.apps) && data.apps.length > 0) {
                        const track = document.getElementById('logoloop-track-top');
                        if (track) {
                            let html = '';
                            const loopItems = [...data.apps, ...data.apps];
                            
                            loopItems.forEach(app => {
                                if (!app.is_active) return;
                                let imgSrc = 'icons/icon48.png'; 
                                if (app.icon_type === 'image') {
                                    imgSrc = app.icon_value.startsWith('http') ? app.icon_value : ExtMock.getURL(app.icon_value);
                                }
                                html += `
                                    <div class="logoloop-item">
                                        <img src="${imgSrc}" onerror="this.src='${ExtMock.getURL('icons/icon48.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);
    if (urlParams.get('fresh') === '1') {
        setLoggedIn(false); 
        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');
        ExtMock.storageRemove(['texa_code', 'texa_device_id', 'texa_active_server', 'texa_active_icon', 'texa_active_app', 'texa_popup_url']);
        setLoggedIn(false);
        goToLogin();
        
        setTimeout(() => {
            showCustomAlert('Sesi Berakhir', 'Sesi login Anda telah direbut dan digunakan di perangkat atau jendela aplikasi lain.', '🚫');
        }, 100);

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

    if (isLoggedIn()) {
        goToDashboard();
        renderAppGrid();
        
        const topBanner = document.getElementById('catalog-link-top');
        if (topBanner) topBanner.style.display = 'none';
    }

    // Custom Element Logic
    const customAlertEl = document.getElementById('custom-alert');
    const customAlertCloseBtn = document.getElementById('custom-alert-close');
    function showCustomAlert(title, message, icon) {
        document.getElementById('custom-alert-title').textContent = title;
        document.getElementById('custom-alert-msg').textContent = message;
        document.getElementById('custom-alert-icon').textContent = icon || '⚠️';
        customAlertEl.classList.remove('hidden');
    }
    customAlertCloseBtn.addEventListener('click', () => {
        customAlertEl.classList.add('hidden');
    });
  </script>
</body>

</html>