Update WebApp

This commit is contained in:
2026-01-11 07:07:32 +03:00
parent 32d0f98a6e
commit 2b68dbac20
17 changed files with 3501 additions and 824 deletions

View File

@@ -0,0 +1,51 @@
.dark-high-contrast {
--md-sys-color-primary: rgb(235 240 255);
--md-sys-color-surface-tint: rgb(170 199 255);
--md-sys-color-on-primary: rgb(0 0 0);
--md-sys-color-primary-container: rgb(166 195 252);
--md-sys-color-on-primary-container: rgb(0 11 32);
--md-sys-color-secondary: rgb(235 240 255);
--md-sys-color-on-secondary: rgb(0 0 0);
--md-sys-color-secondary-container: rgb(186 195 216);
--md-sys-color-on-secondary-container: rgb(3 11 26);
--md-sys-color-tertiary: rgb(255 233 255);
--md-sys-color-on-tertiary: rgb(0 0 0);
--md-sys-color-tertiary-container: rgb(216 184 220);
--md-sys-color-on-tertiary-container: rgb(22 4 29);
--md-sys-color-error: rgb(255 236 233);
--md-sys-color-on-error: rgb(0 0 0);
--md-sys-color-error-container: rgb(255 174 164);
--md-sys-color-on-error-container: rgb(34 0 1);
--md-sys-color-background: rgb(17 19 24);
--md-sys-color-on-background: rgb(226 226 233);
--md-sys-color-surface: rgb(17 19 24);
--md-sys-color-on-surface: rgb(255 255 255);
--md-sys-color-surface-variant: rgb(68 71 78);
--md-sys-color-on-surface-variant: rgb(255 255 255);
--md-sys-color-outline: rgb(238 239 249);
--md-sys-color-outline-variant: rgb(192 194 204);
--md-sys-color-shadow: rgb(0 0 0);
--md-sys-color-scrim: rgb(0 0 0);
--md-sys-color-inverse-surface: rgb(226 226 233);
--md-sys-color-inverse-on-surface: rgb(0 0 0);
--md-sys-color-inverse-primary: rgb(41 72 120);
--md-sys-color-primary-fixed: rgb(214 227 255);
--md-sys-color-on-primary-fixed: rgb(0 0 0);
--md-sys-color-primary-fixed-dim: rgb(170 199 255);
--md-sys-color-on-primary-fixed-variant: rgb(0 17 43);
--md-sys-color-secondary-fixed: rgb(218 226 249);
--md-sys-color-on-secondary-fixed: rgb(0 0 0);
--md-sys-color-secondary-fixed-dim: rgb(190 198 220);
--md-sys-color-on-secondary-fixed-variant: rgb(8 17 33);
--md-sys-color-tertiary-fixed: rgb(250 216 253);
--md-sys-color-on-tertiary-fixed: rgb(0 0 0);
--md-sys-color-tertiary-fixed-dim: rgb(221 188 224);
--md-sys-color-on-tertiary-fixed-variant: rgb(29 8 35);
--md-sys-color-surface-dim: rgb(17 19 24);
--md-sys-color-surface-bright: rgb(78 80 86);
--md-sys-color-surface-container-lowest: rgb(0 0 0);
--md-sys-color-surface-container-low: rgb(29 32 36);
--md-sys-color-surface-container: rgb(46 48 54);
--md-sys-color-surface-container-high: rgb(57 59 65);
--md-sys-color-surface-container-highest: rgb(69 71 76);
}

View File

@@ -0,0 +1,51 @@
.dark-medium-contrast {
--md-sys-color-primary: rgb(205 221 255);
--md-sys-color-surface-tint: rgb(170 199 255);
--md-sys-color-on-primary: rgb(0 37 81);
--md-sys-color-primary-container: rgb(116 145 199);
--md-sys-color-on-primary-container: rgb(0 0 0);
--md-sys-color-secondary: rgb(212 220 242);
--md-sys-color-on-secondary: rgb(29 38 54);
--md-sys-color-secondary-container: rgb(136 145 165);
--md-sys-color-on-secondary-container: rgb(0 0 0);
--md-sys-color-tertiary: rgb(243 210 247);
--md-sys-color-on-tertiary: rgb(51 29 57);
--md-sys-color-tertiary-container: rgb(164 135 169);
--md-sys-color-on-tertiary-container: rgb(0 0 0);
--md-sys-color-error: rgb(255 210 204);
--md-sys-color-on-error: rgb(84 0 3);
--md-sys-color-error-container: rgb(255 84 73);
--md-sys-color-on-error-container: rgb(0 0 0);
--md-sys-color-background: rgb(17 19 24);
--md-sys-color-on-background: rgb(226 226 233);
--md-sys-color-surface: rgb(17 19 24);
--md-sys-color-on-surface: rgb(255 255 255);
--md-sys-color-surface-variant: rgb(68 71 78);
--md-sys-color-on-surface-variant: rgb(218 220 230);
--md-sys-color-outline: rgb(175 178 187);
--md-sys-color-outline-variant: rgb(142 144 153);
--md-sys-color-shadow: rgb(0 0 0);
--md-sys-color-scrim: rgb(0 0 0);
--md-sys-color-inverse-surface: rgb(226 226 233);
--md-sys-color-inverse-on-surface: rgb(40 42 47);
--md-sys-color-inverse-primary: rgb(41 72 120);
--md-sys-color-primary-fixed: rgb(214 227 255);
--md-sys-color-on-primary-fixed: rgb(0 17 43);
--md-sys-color-primary-fixed-dim: rgb(170 199 255);
--md-sys-color-on-primary-fixed-variant: rgb(19 54 101);
--md-sys-color-secondary-fixed: rgb(218 226 249);
--md-sys-color-on-secondary-fixed: rgb(8 17 33);
--md-sys-color-secondary-fixed-dim: rgb(190 198 220);
--md-sys-color-on-secondary-fixed-variant: rgb(46 54 71);
--md-sys-color-tertiary-fixed: rgb(250 216 253);
--md-sys-color-on-tertiary-fixed: rgb(29 8 35);
--md-sys-color-tertiary-fixed-dim: rgb(221 188 224);
--md-sys-color-on-tertiary-fixed-variant: rgb(69 46 74);
--md-sys-color-surface-dim: rgb(17 19 24);
--md-sys-color-surface-bright: rgb(67 68 74);
--md-sys-color-surface-container-lowest: rgb(6 7 12);
--md-sys-color-surface-container-low: rgb(27 30 34);
--md-sys-color-surface-container: rgb(38 40 45);
--md-sys-color-surface-container-high: rgb(49 50 56);
--md-sys-color-surface-container-highest: rgb(60 62 67);
}

View File

@@ -0,0 +1,53 @@
/* Dark Theme Tokens */
body.dark,
.dark {
--md-sys-color-primary: rgb(170 199 255);
--md-sys-color-surface-tint: rgb(170 199 255);
--md-sys-color-on-primary: rgb(10 48 95);
--md-sys-color-primary-container: rgb(40 71 119);
--md-sys-color-on-primary-container: rgb(214 227 255);
--md-sys-color-secondary: rgb(190 198 220);
--md-sys-color-on-secondary: rgb(40 49 65);
--md-sys-color-secondary-container: rgb(62 71 89);
--md-sys-color-on-secondary-container: rgb(218 226 249);
--md-sys-color-tertiary: rgb(221 188 224);
--md-sys-color-on-tertiary: rgb(63 40 68);
--md-sys-color-tertiary-container: rgb(87 62 92);
--md-sys-color-on-tertiary-container: rgb(250 216 253);
--md-sys-color-error: rgb(255 180 171);
--md-sys-color-on-error: rgb(105 0 5);
--md-sys-color-error-container: rgb(147 0 10);
--md-sys-color-on-error-container: rgb(255 218 214);
--md-sys-color-background: rgb(17 19 24);
--md-sys-color-on-background: rgb(226 226 233);
--md-sys-color-surface: rgb(17 19 24);
--md-sys-color-on-surface: rgb(226 226 233);
--md-sys-color-surface-variant: rgb(68 71 78);
--md-sys-color-on-surface-variant: rgb(196 198 208);
--md-sys-color-outline: rgb(142 144 153);
--md-sys-color-outline-variant: rgb(68 71 78);
--md-sys-color-shadow: rgb(0 0 0);
--md-sys-color-scrim: rgb(0 0 0);
--md-sys-color-inverse-surface: rgb(226 226 233);
--md-sys-color-inverse-on-surface: rgb(46 48 54);
--md-sys-color-inverse-primary: rgb(65 95 145);
--md-sys-color-primary-fixed: rgb(214 227 255);
--md-sys-color-on-primary-fixed: rgb(0 27 62);
--md-sys-color-primary-fixed-dim: rgb(170 199 255);
--md-sys-color-on-primary-fixed-variant: rgb(40 71 119);
--md-sys-color-secondary-fixed: rgb(218 226 249);
--md-sys-color-on-secondary-fixed: rgb(19 28 43);
--md-sys-color-secondary-fixed-dim: rgb(190 198 220);
--md-sys-color-on-secondary-fixed-variant: rgb(62 71 89);
--md-sys-color-tertiary-fixed: rgb(250 216 253);
--md-sys-color-on-tertiary-fixed: rgb(40 19 46);
--md-sys-color-tertiary-fixed-dim: rgb(221 188 224);
--md-sys-color-on-tertiary-fixed-variant: rgb(87 62 92);
--md-sys-color-surface-dim: rgb(17 19 24);
--md-sys-color-surface-bright: rgb(55 57 62);
--md-sys-color-surface-container-lowest: rgb(12 14 19);
--md-sys-color-surface-container-low: rgb(25 28 32);
--md-sys-color-surface-container: rgb(29 32 36);
--md-sys-color-surface-container-high: rgb(40 42 47);
--md-sys-color-surface-container-highest: rgb(51 53 58);
}

View File

@@ -0,0 +1,51 @@
.light-high-contrast {
--md-sys-color-primary: rgb(3 43 91);
--md-sys-color-surface-tint: rgb(65 95 145);
--md-sys-color-on-primary: rgb(255 255 255);
--md-sys-color-primary-container: rgb(42 73 122);
--md-sys-color-on-primary-container: rgb(255 255 255);
--md-sys-color-secondary: rgb(35 44 61);
--md-sys-color-on-secondary: rgb(255 255 255);
--md-sys-color-secondary-container: rgb(65 73 91);
--md-sys-color-on-secondary-container: rgb(255 255 255);
--md-sys-color-tertiary: rgb(58 36 64);
--md-sys-color-on-tertiary: rgb(255 255 255);
--md-sys-color-tertiary-container: rgb(89 64 94);
--md-sys-color-on-tertiary-container: rgb(255 255 255);
--md-sys-color-error: rgb(96 0 4);
--md-sys-color-on-error: rgb(255 255 255);
--md-sys-color-error-container: rgb(152 0 10);
--md-sys-color-on-error-container: rgb(255 255 255);
--md-sys-color-background: rgb(249 249 255);
--md-sys-color-on-background: rgb(25 28 32);
--md-sys-color-surface: rgb(249 249 255);
--md-sys-color-on-surface: rgb(0 0 0);
--md-sys-color-surface-variant: rgb(224 226 236);
--md-sys-color-on-surface-variant: rgb(0 0 0);
--md-sys-color-outline: rgb(41 44 51);
--md-sys-color-outline-variant: rgb(70 73 81);
--md-sys-color-shadow: rgb(0 0 0);
--md-sys-color-scrim: rgb(0 0 0);
--md-sys-color-inverse-surface: rgb(46 48 54);
--md-sys-color-inverse-on-surface: rgb(255 255 255);
--md-sys-color-inverse-primary: rgb(170 199 255);
--md-sys-color-primary-fixed: rgb(42 73 122);
--md-sys-color-on-primary-fixed: rgb(255 255 255);
--md-sys-color-primary-fixed-dim: rgb(14 50 98);
--md-sys-color-on-primary-fixed-variant: rgb(255 255 255);
--md-sys-color-secondary-fixed: rgb(65 73 91);
--md-sys-color-on-secondary-fixed: rgb(255 255 255);
--md-sys-color-secondary-fixed-dim: rgb(42 51 68);
--md-sys-color-on-secondary-fixed-variant: rgb(255 255 255);
--md-sys-color-tertiary-fixed: rgb(89 64 94);
--md-sys-color-on-tertiary-fixed: rgb(255 255 255);
--md-sys-color-tertiary-fixed-dim: rgb(65 42 71);
--md-sys-color-on-tertiary-fixed-variant: rgb(255 255 255);
--md-sys-color-surface-dim: rgb(184 184 191);
--md-sys-color-surface-bright: rgb(249 249 255);
--md-sys-color-surface-container-lowest: rgb(255 255 255);
--md-sys-color-surface-container-low: rgb(240 240 247);
--md-sys-color-surface-container: rgb(226 226 233);
--md-sys-color-surface-container-high: rgb(211 212 219);
--md-sys-color-surface-container-highest: rgb(197 198 205);
}

View File

@@ -0,0 +1,51 @@
.light-medium-contrast {
--md-sys-color-primary: rgb(19 54 101);
--md-sys-color-surface-tint: rgb(65 95 145);
--md-sys-color-on-primary: rgb(255 255 255);
--md-sys-color-primary-container: rgb(80 109 160);
--md-sys-color-on-primary-container: rgb(255 255 255);
--md-sys-color-secondary: rgb(46 54 71);
--md-sys-color-on-secondary: rgb(255 255 255);
--md-sys-color-secondary-container: rgb(100 109 128);
--md-sys-color-on-secondary-container: rgb(255 255 255);
--md-sys-color-tertiary: rgb(69 46 74);
--md-sys-color-on-tertiary: rgb(255 255 255);
--md-sys-color-tertiary-container: rgb(127 100 132);
--md-sys-color-on-tertiary-container: rgb(255 255 255);
--md-sys-color-error: rgb(116 0 6);
--md-sys-color-on-error: rgb(255 255 255);
--md-sys-color-error-container: rgb(207 44 39);
--md-sys-color-on-error-container: rgb(255 255 255);
--md-sys-color-background: rgb(249 249 255);
--md-sys-color-on-background: rgb(25 28 32);
--md-sys-color-surface: rgb(249 249 255);
--md-sys-color-on-surface: rgb(15 17 22);
--md-sys-color-surface-variant: rgb(224 226 236);
--md-sys-color-on-surface-variant: rgb(51 54 62);
--md-sys-color-outline: rgb(79 82 90);
--md-sys-color-outline-variant: rgb(106 109 117);
--md-sys-color-shadow: rgb(0 0 0);
--md-sys-color-scrim: rgb(0 0 0);
--md-sys-color-inverse-surface: rgb(46 48 54);
--md-sys-color-inverse-on-surface: rgb(240 240 247);
--md-sys-color-inverse-primary: rgb(170 199 255);
--md-sys-color-primary-fixed: rgb(80 109 160);
--md-sys-color-on-primary-fixed: rgb(255 255 255);
--md-sys-color-primary-fixed-dim: rgb(55 85 134);
--md-sys-color-on-primary-fixed-variant: rgb(255 255 255);
--md-sys-color-secondary-fixed: rgb(100 109 128);
--md-sys-color-on-secondary-fixed: rgb(255 255 255);
--md-sys-color-secondary-fixed-dim: rgb(76 85 103);
--md-sys-color-on-secondary-fixed-variant: rgb(255 255 255);
--md-sys-color-tertiary-fixed: rgb(127 100 132);
--md-sys-color-on-tertiary-fixed: rgb(255 255 255);
--md-sys-color-tertiary-fixed-dim: rgb(101 76 107);
--md-sys-color-on-tertiary-fixed-variant: rgb(255 255 255);
--md-sys-color-surface-dim: rgb(197 198 205);
--md-sys-color-surface-bright: rgb(249 249 255);
--md-sys-color-surface-container-lowest: rgb(255 255 255);
--md-sys-color-surface-container-low: rgb(243 243 250);
--md-sys-color-surface-container: rgb(231 232 238);
--md-sys-color-surface-container-high: rgb(220 220 227);
--md-sys-color-surface-container-highest: rgb(209 209 216);
}

View File

@@ -0,0 +1,53 @@
/* Light Theme Tokens */
body.light,
.light {
--md-sys-color-primary: rgb(65 95 145);
--md-sys-color-surface-tint: rgb(65 95 145);
--md-sys-color-on-primary: rgb(255 255 255);
--md-sys-color-primary-container: rgb(214 227 255);
--md-sys-color-on-primary-container: rgb(40 71 119);
--md-sys-color-secondary: rgb(86 95 113);
--md-sys-color-on-secondary: rgb(255 255 255);
--md-sys-color-secondary-container: rgb(218 226 249);
--md-sys-color-on-secondary-container: rgb(62 71 89);
--md-sys-color-tertiary: rgb(112 85 117);
--md-sys-color-on-tertiary: rgb(255 255 255);
--md-sys-color-tertiary-container: rgb(250 216 253);
--md-sys-color-on-tertiary-container: rgb(87 62 92);
--md-sys-color-error: rgb(186 26 26);
--md-sys-color-on-error: rgb(255 255 255);
--md-sys-color-error-container: rgb(255 218 214);
--md-sys-color-on-error-container: rgb(147 0 10);
--md-sys-color-background: rgb(249 249 255);
--md-sys-color-on-background: rgb(25 28 32);
--md-sys-color-surface: rgb(249 249 255);
--md-sys-color-on-surface: rgb(25 28 32);
--md-sys-color-surface-variant: rgb(224 226 236);
--md-sys-color-on-surface-variant: rgb(68 71 78);
--md-sys-color-outline: rgb(116 119 127);
--md-sys-color-outline-variant: rgb(196 198 208);
--md-sys-color-shadow: rgb(0 0 0);
--md-sys-color-scrim: rgb(0 0 0);
--md-sys-color-inverse-surface: rgb(46 48 54);
--md-sys-color-inverse-on-surface: rgb(240 240 247);
--md-sys-color-inverse-primary: rgb(170 199 255);
--md-sys-color-primary-fixed: rgb(214 227 255);
--md-sys-color-on-primary-fixed: rgb(0 27 62);
--md-sys-color-primary-fixed-dim: rgb(170 199 255);
--md-sys-color-on-primary-fixed-variant: rgb(40 71 119);
--md-sys-color-secondary-fixed: rgb(218 226 249);
--md-sys-color-on-secondary-fixed: rgb(19 28 43);
--md-sys-color-secondary-fixed-dim: rgb(190 198 220);
--md-sys-color-on-secondary-fixed-variant: rgb(62 71 89);
--md-sys-color-tertiary-fixed: rgb(250 216 253);
--md-sys-color-on-tertiary-fixed: rgb(40 19 46);
--md-sys-color-tertiary-fixed-dim: rgb(221 188 224);
--md-sys-color-on-tertiary-fixed-variant: rgb(87 62 92);
--md-sys-color-surface-dim: rgb(217 217 224);
--md-sys-color-surface-bright: rgb(249 249 255);
--md-sys-color-surface-container-lowest: rgb(255 255 255);
--md-sys-color-surface-container-low: rgb(243 243 250);
--md-sys-color-surface-container: rgb(237 237 244);
--md-sys-color-surface-container-high: rgb(231 232 238);
--md-sys-color-surface-container-highest: rgb(226 226 233);
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,232 +4,416 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Comet VPN</title>
<title>Stellarisei VPN</title>
<!-- Telegram Web App SDK -->
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="css/style.css?v=2">
<!-- Icons -->
<script src="https://unpkg.com/lucide@latest"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<!-- Styles -->
<link rel="stylesheet" href="css/dark.css">
<link rel="stylesheet" href="css/light.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<body class="dark">
<div id="stars-container"></div>
<div class="glow-overlay"></div>
<div id="toast-container"></div>
<div class="app-layout">
<!-- Sidebar (Desktop) -->
<aside class="sidebar glass">
<div class="logo">
<i data-lucide="rocket" class="logo-icon"></i>
<span>CometBot</span>
<div class="app-shell">
<!-- Desktop Rail (Full Features) -->
<nav class="nav-rail">
<div class="logo-area">
<i data-lucide="rocket" style="width:32px; height:32px;"></i>
</div>
<nav class="nav-menu">
<button class="nav-item active" data-page="dashboard" onclick="router('dashboard')">
<i data-lucide="layout-dashboard"></i> <span>Dashboard</span>
</button>
<button class="nav-item" data-page="shop" onclick="router('shop')">
<i data-lucide="shopping-bag"></i> <span>Shop</span>
</button>
<button class="nav-item" data-page="subscription" onclick="router('subscription')">
<i data-lucide="key"></i> <span>Config</span>
</button>
<button class="nav-item" data-page="profile" onclick="router('profile')">
<i data-lucide="user"></i> <span>Profile</span>
</button>
</nav>
</aside>
<!-- Main Content -->
<main class="content-area">
<header class="mobile-header glass">
<div class="logo-mini">
<i data-lucide="rocket"></i> Comet
<div class="nav-destinations">
<button class="rail-item active nav-item" data-page="dashboard" onclick="router('dashboard')">
<i data-lucide="layout-grid"></i>
<span data-t="nav_home">Home</span>
</button>
<button class="rail-item nav-item" data-page="shop" onclick="router('shop')">
<i data-lucide="shopping-bag"></i>
<span data-t="nav_shop">Shop</span>
</button>
<button class="rail-item nav-item" data-page="subscription" onclick="router('subscription')">
<i data-lucide="qr-code"></i>
<span data-t="nav_config">Config</span>
</button>
<button class="rail-item nav-item" data-page="promo" onclick="openPromoModal()">
<i data-lucide="ticket"></i>
<span data-t="nav_promo">Promo</span>
</button>
<button class="rail-item nav-item" data-page="profile" onclick="router('profile')">
<i data-lucide="user"></i>
<span data-t="nav_profile">Profile</span>
</button>
<button class="rail-item nav-item hidden" id="nav-admin" data-page="admin" onclick="router('admin')">
<i data-lucide="shield-check"></i>
<span data-t="nav_admin">Admin</span>
</button>
</div>
<button class="rail-fab" onclick="openSupport()">
<i data-lucide="message-square"></i>
</button>
</nav>
<!-- Main Content Pane -->
<main class="main-pane">
<header class="mobile-header">
<div style="display:flex; align-items:center; gap:12px;">
<i data-lucide="rocket" style="color:var(--md-sys-color-primary)"></i>
<span style="font-weight:600; font-size:18px;" id="header-title">Stellarisei</span>
</div>
<div class="user-chip" id="header-user">
<div class="avatar-xs" id="header-avatar">U</div>
<div class="user-chip" id="header-user" onclick="router('profile')">
<div class="avatar-xs" id="header-avatar"
style="width:32px;height:32px;border-radius:50%;background:var(--md-sys-color-primary-container);display:flex;align-items:center;justify-content:center;">
U</div>
</div>
</header>
<div id="app-view" class="view-container">
<!-- View Display -->
<div id="app-view" class="view-content">
<div class="loading-spinner"></div>
</div>
<!-- Bottom Nav (Mobile) -->
<nav class="bottom-nav glass">
<button class="nav-item active" data-page="dashboard" onclick="router('dashboard')">
<i data-lucide="layout-dashboard"></i>
</button>
<button class="nav-item" data-page="shop" onclick="router('shop')">
<i data-lucide="shopping-bag"></i>
</button>
<button class="nav-item center-fab" data-page="subscription" onclick="router('subscription')">
<div class="fab-bg"><i data-lucide="power"></i></div>
</button>
<button class="nav-item" data-page="profile" onclick="router('profile')">
<i data-lucide="user"></i>
</button>
<button class="nav-item" onclick="openHelp()">
<i data-lucide="help-circle"></i>
</button>
</nav>
<div style="height:120px;"></div> <!-- Bottom spacer for mobile -->
</main>
<!-- Mobile Bottom Bar (5 Items) -->
<nav class="bottom-bar">
<button class="bar-item nav-item active" data-page="dashboard" onclick="router('dashboard')">
<div class="bar-pill"><i data-lucide="layout-grid"></i></div>
<span style="font-size:10px; margin-top:4px;" data-t="nav_home">Home</span>
</button>
<button class="bar-item nav-item" data-page="shop" onclick="router('shop')">
<div class="bar-pill"><i data-lucide="shopping-bag"></i></div>
<span style="font-size:10px; margin-top:4px;" data-t="nav_shop">Shop</span>
</button>
<!-- Center Action: Config -->
<button class="bar-item nav-item" data-page="subscription" onclick="router('subscription')">
<div class="bar-pill"><i data-lucide="qr-code"></i></div>
<span style="font-size:10px; margin-top:4px;" data-t="nav_config">Config</span>
</button>
<button class="bar-item nav-item" data-page="promo" onclick="openPromoModal()">
<div class="bar-pill"><i data-lucide="ticket"></i></div>
<span style="font-size:10px; margin-top:4px;" data-t="nav_promo">Promo</span>
</button>
<!-- Profile (hidden for admins) -->
<button class="bar-item nav-item" id="mobile-profile-btn" data-page="profile" onclick="router('profile')">
<div class="bar-pill"><i data-lucide="user"></i></div>
<span style="font-size:10px; margin-top:4px;" data-t="nav_profile">Profile</span>
</button>
<!-- Admin (hidden by default, shown for admins) -->
<button class="bar-item nav-item hidden" id="mobile-admin-btn" data-page="admin" onclick="router('admin')">
<div class="bar-pill"><i data-lucide="shield-check"></i></div>
<span style="font-size:10px; margin-top:4px;" data-t="nav_admin">Admin</span>
</button>
</nav>
</div>
<!-- VIEW TEMPLATES -->
<!-- Modals -->
<div id="modal-overlay" class="modal-overlay hidden" onclick="closeModal(event)">
<div class="modal-dialog">
<div class="modal-header"
style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
<h2 id="modal-title" style="font:var(--type-headline-small);">Title</h2>
<button class="close-btn" onclick="closeModal()"
style="background:none; border:none; color:var(--md-sys-color-on-surface-variant); cursor:pointer;"><i
data-lucide="x"></i></button>
</div>
<div id="modal-body"></div>
</div>
</div>
<!-- TEMPLATES -->
<!-- Dashboard -->
<template id="view-dashboard">
<div class="view-header">
<h1>Overview</h1>
<p class="subtitle">Welcome back, <span id="user-name">User</span></p>
</div>
<header class="page-header-large">
<div class="subtitle" data-t="dash_welcome">Welcome back</div>
<h1><span id="user-name">User</span></h1>
</header>
<div class="status-card glass">
<div class="status-ring">
<svg viewBox="0 0 36 36" class="circular-chart">
<path class="circle-bg"
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
<path class="circle" id="data-ring" stroke-dasharray="0, 100"
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
</svg>
<div class="ring-content">
<span class="value" id="dash-data-left">0</span>
<span class="unit">GB Used</span>
<div class="status-overview">
<div class="ring-card">
<div class="ring-container">
<svg viewBox="0 0 36 36" class="circular-chart">
<path class="circle-bg"
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
<path class="circle" id="data-ring" stroke-dasharray="0, 100"
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
</svg>
<div
style="position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center;">
<i data-lucide="database" style="color:var(--md-sys-color-primary)"></i>
</div>
</div>
<div>
<div style="font:var(--type-title-large); color:var(--md-sys-color-on-surface);"><span
id="dash-data-left">0</span> GB</div>
<div style="font:var(--type-body-medium); color:var(--md-sys-color-outline);" data-t="dash_used">
Used Traffic</div>
</div>
</div>
<div class="status-details">
<div class="detail-item">
<span class="label">Status</span>
<span class="val status-badge" id="dash-status">Checking...</span>
<div class="ring-card">
<div class="ring-container">
<svg viewBox="0 0 36 36" class="circular-chart">
<path class="circle-bg"
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
<path class="circle" id="exp-ring" stroke-dasharray="0, 100"
d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
</svg>
<div
style="position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center;">
<i data-lucide="clock" style="color:var(--md-sys-color-primary)"></i>
</div>
</div>
<div class="detail-item">
<span class="label">Plan Details</span>
<span class="val" id="dash-limit">0 GB Total</span>
</div>
<div class="detail-item">
<span class="label">Expires</span>
<span class="val" id="dash-expire">-</span>
<div>
<div style="font:var(--type-title-large); color:var(--md-sys-color-on-surface);"><span
id="dash-days-left">0</span> Days</div>
<div style="font:var(--type-body-medium); color:var(--md-sys-color-outline);" data-t="dash_expiry">
Until Expiry</div>
</div>
</div>
</div>
<div class="quick-actions">
<button class="action-card glass" onclick="router('shop')">
<div class="icon-circle pop"><i data-lucide="zap"></i></div>
<span>Extend Plan</span>
</button>
<button class="action-card glass" onclick="router('subscription')">
<div class="icon-circle"><i data-lucide="qr-code"></i></div>
<span>Connect</span>
</button>
</div>
</template>
<!-- Shop -->
<template id="view-shop">
<div class="view-header">
<h1>Shop</h1>
<p class="subtitle">Choose your plan</p>
</div>
<div class="plans-list" id="plans-container">
<!-- Injected -->
</div>
</template>
<!-- Subscription -->
<template id="view-subscription">
<div class="view-header">
<h1>Connection</h1>
<p class="subtitle">Setup your VPN</p>
</div>
<div class="card glass center-content">
<div id="qrcode-container"></div>
<p class="helper-text">Scan availability QR Code</p>
<div class="copy-box" onclick="copyConfig()">
<div class="truncate-text" id="config-link">Loading...</div>
<i data-lucide="copy"></i>
<div class="m3-card">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:12px;">
<span class="subtitle" data-t="dash_status">Subscription Status</span>
<span class="status-badge" id="dash-status"
style="background:var(--md-sys-color-secondary-container); color:var(--md-sys-color-on-secondary-container); padding:4px 12px; border-radius:8px; font-weight:600; font-size:12px;">Checking...</span>
</div>
<div class="sub-actions">
<button class="btn-secondary" onclick="copyConfig()">Copy Link</button>
<button class="btn-primary"
onclick="window.Telegram.WebApp.openLink('https://apps.apple.com/us/app/v2box-v2ray-client/id6446814690')">
Download App
<div class="actions-grid">
<button class="action-btn-large" onclick="router('shop')">
<i data-lucide="zap"></i>
<span data-t="btn_extend">Extend</span>
</button>
<button class="action-btn-large" onclick="router('subscription')">
<i data-lucide="qr-code"></i>
<span data-t="btn_connect">Connect</span>
</button>
</div>
</div>
</template>
<div class="accordion glass">
<div class="acc-item">
<div class="acc-head" onclick="toggleAcc(this)">
<span>How to connect on iOS?</span>
<i data-lucide="chevron-down"></i>
</div>
<div class="acc-body">
<ol>
<li>Install <b>V2Box</b> from AppStore.</li>
<li>Copy the link above.</li>
<li>Open V2Box, it will detect the link.</li>
<li>Tap "Import" and Connect.</li>
</ol>
<template id="view-shop">
<header class="page-header-large">
<h1 data-t="shop_title">Select Plan</h1>
<div class="subtitle" data-t="shop_subtitle">Upgrade your experience</div>
</header>
<div id="plans-container" style="margin-bottom: 60px;"></div>
</template>
<template id="view-subscription">
<header class="page-header-large">
<h1 data-t="sub_title">Connect</h1>
<div class="subtitle" data-t="sub_subtitle">Setup your device</div>
</header>
<div class="m3-card" style="display:flex; flex-direction:column; align-items:center; text-align:center;">
<div id="qrcode-container" style="background:white; padding:12px; border-radius:16px;"></div>
<div style="margin-top:24px; width:100%;">
<div class="subtitle" style="margin-bottom:8px;" data-t="sub_link_label">Subscription Link</div>
<div style="background:var(--md-sys-color-surface-container); padding:12px; border-radius:12px; display:flex; align-items:center; gap:12px; cursor:pointer;"
onclick="copyConfig()">
<div id="config-link"
style="flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:13px; font-family:monospace; color:var(--md-sys-color-on-surface);">
...</div>
<i data-lucide="copy" style="color:var(--md-sys-color-primary);"></i>
</div>
</div>
</div>
<div id="device-apps-container"></div>
<div class="accordion m3-card" style="margin-top:16px;">
<div class="acc-item">
<div class="acc-head" onclick="toggleAcc(this)">
<span>How to connect on Android?</span>
<span data-t="sub_instructions">Detailed Instructions</span>
<i data-lucide="chevron-down"></i>
</div>
<div class="acc-body">
<ol>
<li>Install <b>v2rayNG</b> or <b>Hiddify</b>.</li>
<li>Copy the config link.</li>
<li>Open app -> Import from Clipboard.</li>
<li>Connect (V button).</li>
</ol>
<p data-t="sub_instr_1">1. Download app for your device.</p>
<p data-t="sub_instr_2">2. Copy the link above.</p>
<p data-t="sub_instr_3">3. Import from Clipboard.</p>
<p data-t="sub_instr_4">4. Connect.</p>
</div>
</div>
</div>
</template>
<!-- Profile -->
<template id="view-profile">
<div class="view-header">
<h1>Profile</h1>
<div style="text-align:center; margin:40px 0;">
<div class="avatar-xs" id="profile-avatar"
style="width:80px; height:80px; margin:0 auto 16px; border-radius:50%; background:var(--md-sys-color-primary-container); color:var(--md-sys-color-on-primary-container); font-size:32px; display:flex; align-items:center; justify-content:center;">
U</div>
<h2 id="profile-name" style="font:var(--type-headline-medium);">User</h2>
<div id="profile-id" class="subtitle">ID: 000000</div>
</div>
<div class="card glass profile-main">
<div class="big-avatar" id="profile-avatar">U</div>
<h2 id="profile-name">User</h2>
<p id="profile-id" class="id-badge">ID: 000000</p>
<div class="m3-card">
<div class="shop-item">
<span data-t="prof_joined">Joined</span>
<span id="stat-reg-date" style="font-weight:600;">...</span>
</div>
<div class="shop-item">
<span data-t="prof_spent">Total Spent</span>
<span style="font-weight:600;"><span id="stat-spent">0</span> ⭐️</span>
</div>
<div class="shop-item">
<span data-t="prof_purchases">Purchases</span>
<span id="stat-payments" style="font-weight:600;">0</span>
</div>
</div>
<div class="section-title">Promo Code</div>
<div class="card glass promo-card">
<input type="text" id="promo-input" placeholder="ENTER CODE" class="glass-input">
<button class="btn-small" onclick="checkPromo()">Apply</button>
</div>
<div id="promo-result"></div>
<div class="list-menu glass">
<button class="list-item" onclick="window.Telegram.WebApp.openLink('https://t.me/hoshimach1')">
<!-- Restored Support/About Buttons -->
<h3 style="margin-bottom:12px; padding-left:4px;" data-t="prof_app_info">App Info</h3>
<div class="m3-card" style="padding:12px;">
<button class="list-item" onclick="openSupport()" style="width:100%;">
<i data-lucide="message-square"></i>
<span>Support</span>
<span data-t="btn_support">Support</span>
<i data-lucide="chevron-right" class="arrow"></i>
</button>
<button class="list-item" onclick="alert('v1.0.0 Comet')">
<button class="list-item" onclick="openAbout()" style="width:100%;">
<i data-lucide="info"></i>
<span>About</span>
<span data-t="btn_about">About</span>
<i data-lucide="chevron-right" class="arrow"></i>
</button>
</div>
</template>
<script src="js/background.js"></script>
<!-- Admin templates -->
<template id="view-admin">
<header class="page-header-large">
<h1 data-t="adm_title">Admin</h1>
</header>
<div class="admin-tabs" style="display:flex; gap:8px; overflow-x:auto; margin-bottom:24px; padding-bottom:4px;">
<button class="btn-tonal tab-item" onclick="adminTab('stats')" data-t="adm_stats">Stats</button>
<button class="btn-tonal tab-item" onclick="adminTab('users')" data-t="adm_users">Users</button>
<button class="btn-tonal tab-item" onclick="adminTab('promos')" data-t="adm_promos">Promos</button>
<button class="btn-tonal tab-item" onclick="adminTab('broadcast')" data-t="adm_broadcast">Broadcast</button>
</div>
<div id="admin-content"></div>
</template>
<template id="admin-stats">
<div class="m3-card">
<h3 data-t="adm_bot_stats">Bot Stats</h3>
<div
style="margin-top:12px; display:flex; justify-content:space-between; padding:8px 0; border-bottom:1px solid var(--md-sys-color-outline-variant);">
<span data-t="adm_total_users">Total Users:</span> <b id="adm-total-users">0</b>
</div>
<div
style="display:flex; justify-content:space-between; padding:8px 0; border-bottom:1px solid var(--md-sys-color-outline-variant);">
<span data-t="adm_active_subs">Active Subs:</span> <b id="adm-active-subs">0</b>
</div>
<div style="display:flex; justify-content:space-between; padding:8px 0;">
<span data-t="adm_revenue">Revenue:</span> <b id="adm-revenue">0</b>
</div>
</div>
<div class="m3-card">
<h3 data-t="adm_server">Server</h3>
<div
style="margin-top:12px; display:flex; justify-content:space-between; padding:8px 0; border-bottom:1px solid var(--md-sys-color-outline-variant);">
<span>CPU:</span> <b id="adm-cpu">0%</b>
</div>
<div
style="display:flex; justify-content:space-between; padding:8px 0; border-bottom:1px solid var(--md-sys-color-outline-variant);">
<span>RAM:</span> <b id="adm-ram">0/0</b>
</div>
<div style="display:flex; justify-content:space-between; padding:8px 0;">
<span data-t="adm_marzban">Marzban Users:</span> <b id="adm-active-marz">0</b>
</div>
</div>
</template>
<template id="admin-users">
<div class="search-box">
<input type="text" id="admin-user-search" placeholder="Search..." data-tp="ph_search" class="glass-input">
<button class="btn-primary" style="width:auto; padding:0 24px;" onclick="adminSearchUsers()"
data-t="btn_search">Search</button>
</div>
<div id="admin-users-list" class="admin-list"></div>
</template>
<template id="admin-user-detail">
<button class="btn-secondary" onclick="adminTab('users')"
style="margin-bottom:16px; width:auto; padding:0 20px;"><i data-lucide="arrow-left"></i> <span
data-t="btn_back">Back</span></button>
<div class="m3-card" style="margin-bottom:50px;">
<div style="display:flex; align-items:center; gap:16px; margin-bottom:20px;">
<div class="avatar-xs" id="adm-user-avatar"
style="width:48px;height:48px;border-radius:50%;background:var(--md-sys-color-primary-container);display:flex;align-items:center;justify-content:center;">
U</div>
<div>
<div style="font-weight:700; font-size:18px;" id="adm-user-name">User</div>
<div style="font-size:12px;opacity:0.6;" id="adm-user-id">ID: ...</div>
</div>
</div>
<div style="display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-bottom:24px;">
<div class="list-item" style="flex-direction:column; align-items:flex-start; gap:4px;">
<span style="font-size:11px; opacity:0.6" data-t="adm_status">Status</span>
<b id="adm-user-status">...</b>
</div>
<div class="list-item" style="flex-direction:column; align-items:flex-start; gap:4px;">
<span style="font-size:11px; opacity:0.6" data-t="adm_expiry">Expiry</span>
<b id="adm-user-expire">...</b>
</div>
<div class="list-item"
style="grid-column: span 2; flex-direction:column; align-items:flex-start; gap:4px;">
<span style="font-size:11px; opacity:0.6" data-t="adm_traffic">Traffic</span>
<b id="adm-user-traffic">...</b>
</div>
</div>
<div style="display:grid; grid-template-columns:1fr 1fr; gap:8px;">
<button class="btn-secondary" onclick="adminUserAction('add_days')" data-t="adm_btn_days">+Days</button>
<button class="btn-secondary" onclick="adminUserAction('set_expiry')" data-t="adm_btn_exp">Set
Exp</button>
<button class="btn-secondary" onclick="adminUserAction('set_limit')" data-t="adm_btn_limit">Set
GB</button>
<button class="btn-secondary" onclick="adminUserAction('set_plan')" data-t="adm_btn_plan">Plan</button>
<button class="btn-secondary" onclick="adminUserAction('toggle_status')"
data-t="adm_btn_toggle">Toggle</button>
<button class="btn-secondary" onclick="adminUserAction('reset_traffic')"
data-t="adm_btn_reset">Reset</button>
<button class="btn-error" style="grid-column:span 2" onclick="adminUserAction('delete_sub')"
data-t="adm_btn_delete">Delete
Sub</button>
</div>
</div>
</template>
<template id="admin-promos">
<button class="btn-primary" style="margin-bottom:16px;" onclick="openCreatePromoModal()"
data-t="adm_create_promo">+ Create Promo</button>
<div id="admin-promos-list" class="admin-list"></div>
</template>
<template id="admin-broadcast">
<div class="m3-card">
<h3 data-t="adm_broadcast">Broadcast</h3>
<p class="subtitle" style="margin-bottom:16px;" data-t="adm_broadcast_msg">Send message to all users</p>
<textarea id="broadcast-msg" class="glass-input"
style="height:120px; border-radius:12px; margin-bottom:16px;" placeholder="Message..."
data-tp="ph_message"></textarea>
<button class="btn-primary" onclick="sendBroadcast()" data-t="adm_send">Send</button>
</div>
</template>
<!-- JS -->
<script src="js/app.js"></script>
</body>

File diff suppressed because it is too large Load Diff