diff --git a/README.md b/README.md
index 9b777ca..7c1d60d 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
Game server management dashboard for the ZeroHost platform. Provides user-facing server lifecycle management (creation, renewal, suspension, deletion) backed by a Pyrodactyl/Pterodactyl panel API.
+
+
---
## Table of Contents
diff --git a/config/cap.js b/config/cap.js
index b885250..1bfb964 100644
--- a/config/cap.js
+++ b/config/cap.js
@@ -1,14 +1,6 @@
const CAP_SECRET = process.env.CAP_SECRET;
const CAP_ENDPOINT = process.env.CAP_ENDPOINT;
-if (!CAP_SECRET) {
- console.error('Missing CAP_SECRET environment variable');
-}
-
-if (!CAP_ENDPOINT) {
- console.error('Missing CAP_ENDPOINT environment variable');
-}
-
async function fetchWithTimeout(url, options = {}, timeout = 10000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout);
diff --git a/config/db.js b/config/db.js
index 4967a60..4e97881 100644
--- a/config/db.js
+++ b/config/db.js
@@ -17,18 +17,50 @@ const pool = mariadb.createPool({
acquireTimeout: 10000,
idleTimeout: 30000,
insertIdAsNumber: true,
+ pingTimeout: 5000,
});
+export async function closePool() {
+ try {
+ await pool.end();
+ } catch (err) {
+ console.error('Error closing pool:', err.message);
+ }
+}
+
+export async function getPoolStatus() {
+ try {
+ const active = pool.activeConnections();
+ const total = pool.totalConnections();
+ const idle = pool.idleConnections();
+ return { active, total, idle };
+ } catch {
+ return { active: -1, total: -1, idle: -1 };
+ }
+}
+
export async function query(sql, params = []) {
let lastErr;
for (let attempt = 1; attempt <= 3; attempt++) {
let conn;
+ let queryTimeout;
try {
conn = await pool.getConnection();
- const rows = await conn.query(sql, params);
+ const timeoutPromise = new Promise((_, reject) => {
+ queryTimeout = setTimeout(() => reject(new Error('Query timeout after 30000ms')), 30000);
+ });
+ let rows = await Promise.race([
+ conn.query(sql, params),
+ timeoutPromise,
+ ]);
+ clearTimeout(queryTimeout);
+ if (Array.isArray(rows) && rows.length > 10000) {
+ console.warn(`Large result set detected: ${rows.length} rows for query: ${sql.slice(0, 100)}`);
+ }
return rows;
} catch (err) {
lastErr = err;
+ clearTimeout(queryTimeout);
if (err.code === 'ECONNRESET' || err.code === 'PROTOCOL_CONNECTION_LOST' || err.message?.includes('timeout')) {
console.error(`DB query attempt ${attempt}/3 failed:`, err.message);
if (attempt < 3) await new Promise(r => setTimeout(r, 100 * attempt));
diff --git a/config/migrate.js b/config/migrate.js
index bde37db..ab3d0c0 100644
--- a/config/migrate.js
+++ b/config/migrate.js
@@ -56,6 +56,8 @@ const tables = {
{ name: 'id', def: 'INT AUTO_INCREMENT PRIMARY KEY' },
{ name: 'ptero_nest_id', def: 'INT NOT NULL UNIQUE' },
{ name: 'name', def: 'VARCHAR(255) NOT NULL' },
+ { name: 'logo', def: 'VARCHAR(255) DEFAULT NULL' },
+ { name: 'description', def: 'TEXT DEFAULT NULL' },
{ name: 'created_at', def: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP' },
],
},
@@ -64,6 +66,7 @@ const tables = {
{ name: 'id', def: 'INT AUTO_INCREMENT PRIMARY KEY' },
{ name: 'ptero_nest_id', def: 'INT NOT NULL' },
{ name: 'ptero_egg_id', def: 'INT NOT NULL' },
+ { name: 'logo', def: 'VARCHAR(255) DEFAULT NULL' },
{ name: 'cpu_limit', def: 'INT DEFAULT NULL' },
{ name: 'memory_limit', def: 'INT DEFAULT NULL' },
{ name: 'disk_limit', def: 'INT DEFAULT NULL' },
@@ -106,7 +109,14 @@ const constraints = [
{ table: 'egg_resources', sql: 'ALTER TABLE egg_resources ADD UNIQUE INDEX idx_egg_resources_nest_egg (ptero_nest_id, ptero_egg_id)', name: 'idx_egg_resources_nest_egg' },
{ table: 'notifications', sql: 'ALTER TABLE notifications ADD INDEX idx_notif_user (user_id)', name: 'idx_notif_user' },
{ table: 'notifications', sql: 'ALTER TABLE notifications ADD INDEX idx_notif_user_read (user_id, is_read)', name: 'idx_notif_user_read' },
+ { table: 'notifications', sql: 'ALTER TABLE notifications ADD INDEX idx_notif_created (created_at)', name: 'idx_notif_created' },
{ table: 'notifications', sql: 'ALTER TABLE notifications ADD CONSTRAINT fk_notif_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE', name: 'fk_notif_user' },
+ { table: 'users', sql: 'ALTER TABLE users ADD INDEX idx_email (email)', name: 'idx_user_email' },
+ { table: 'users', sql: 'ALTER TABLE users ADD INDEX idx_username (username)', name: 'idx_user_username' },
+ { table: 'activity_log', sql: 'ALTER TABLE activity_log ADD INDEX idx_action (action)', name: 'idx_activity_action' },
+ { table: 'activity_log', sql: 'ALTER TABLE activity_log ADD CONSTRAINT fk_activity_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE', name: 'fk_activity_user' },
+ { table: 'server_meta', sql: 'ALTER TABLE server_meta ADD INDEX idx_ptero_server (ptero_server_id)', name: 'idx_ptero_server' },
+ { table: 'server_meta', sql: 'ALTER TABLE server_meta ADD CONSTRAINT fk_server_meta_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE', name: 'fk_server_meta_user' },
];
export async function migrate() {
diff --git a/config/pyrodactyl.js b/config/pyrodactyl.js
index e06647c..cbe1cba 100644
--- a/config/pyrodactyl.js
+++ b/config/pyrodactyl.js
@@ -1,6 +1,6 @@
export const PTERO_URL = process.env.PTERO_URL || 'https://panel.zero-host.org';
export const PTERO_API_KEY = process.env.PTERO_API_KEY || '';
-export const PANEL_DB_NAME = process.env.PANEL_DB_NAME || 'panel';
+export const PANEL_DB_NAME = (process.env.PANEL_DB_NAME || 'panel').replace(/[^a-zA-Z0-9_]/g, '');
export const SERVER_LIMITS = {
memory: 512,
diff --git a/middleware/auth.js b/middleware/auth.js
index e09e59d..2006c9a 100644
--- a/middleware/auth.js
+++ b/middleware/auth.js
@@ -3,12 +3,6 @@ import { query } from '../config/db.js';
const JWT_SECRET = process.env.JWT_SECRET;
-if (!JWT_SECRET) {
- console.error('Missing JWT_SECRET environment variable');
-} else if (/[\$\(\)]/.test(JWT_SECRET)) {
- console.error('JWT_SECRET contains unresolved shell expansion characters ($(), backticks). Generate a proper random key (e.g. openssl rand -hex 32) and hardcode it in .env');
-}
-
export async function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
diff --git a/package.json b/package.json
index 704316b..cd1844f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "zerohost-dashboard",
- "version": "1.0.2",
+ "version": "1.0.3",
"private": true,
"type": "module",
"scripts": {
diff --git a/public/css/style.css b/public/css/style.css
index cc73afc..b5fbb34 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -1,2343 +1,2707 @@
-*, *::before, *::after {
- box-sizing: border-box;
- margin: 0;
- padding: 0;
-}
-
-:root {
- --bg-primary: #000;
- --bg-secondary: #1c1917;
- --bg-card: #1c1917;
- --bg-card-hover: #292524;
- --bg-sidebar: #0d0d0d;
- --border: rgba(238, 129, 50, 0.15);
- --border-hover: rgba(238, 129, 50, 0.3);
- --accent-1: #ee8132;
- --accent-2: #ee8132;
- --accent-3: #ee8132;
- --accent-cyan: #06b6d4;
- --accent-green: #059669;
- --accent-red: #ef4444;
- --accent-orange: #f59e0b;
- --text-primary: #f5f5f4;
- --text-secondary: #a8a29e;
- --text-muted: #78716c;
- --glow-primary: rgba(238, 129, 50, 0.15);
- --radius-sm: 8px;
- --radius-md: 14px;
- --radius-lg: 20px;
- --radius-xl: 28px;
- --transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
- --sidebar-w: 260px;
-}
-
-html { height: 100%; }
-
-body, body * {
- font-family: 'Schibsted Grotesk', system-ui, sans-serif;
- font-weight: 400;
-}
-
-body {
- height: 100%;
- background: var(--bg-primary);
- color: var(--text-primary);
- line-height: 1.6;
- overflow-x: hidden;
-}
-
-::-webkit-scrollbar { width: 6px; }
-::-webkit-scrollbar-track { background: var(--bg-primary); }
-::-webkit-scrollbar-thumb { background: var(--accent-1); border-radius: 99px; }
-
-a { color: var(--accent-3); text-decoration: none; }
-a:hover { color: var(--accent-1); }
-
-/* ===== AUTH PAGES ===== */
-.auth-page {
- min-height: 100vh;
- display: flex;
- align-items: center;
- justify-content: center;
- background: #000;
- position: relative;
- padding: 24px;
-}
-
-.auth-page::before {
- content: '';
- position: fixed;
- inset: 0;
- pointer-events: none;
- z-index: 0;
- background-image:
- linear-gradient(rgba(238, 129, 50, 0.05) 1px, transparent 1px),
- linear-gradient(90deg, rgba(238, 129, 50, 0.05) 1px, transparent 1px);
- background-size: 24px 24px;
- mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, black 30%, transparent 80%);
- -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, black 30%, transparent 80%);
-}
-
-.auth-page::after {
- content: '';
- position: fixed;
- inset: 0;
- z-index: 0;
- pointer-events: none;
- background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.08'/%3E%3C/svg%3E");
- background-repeat: repeat;
- background-size: 512px 512px;
-}
-
-.auth-card {
- position: relative;
- z-index: 1;
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius-xl);
- padding: 48px 40px;
- width: 100%;
- max-width: 440px;
- box-shadow: 0 24px 80px rgba(0,0,0,0.5);
-}
-
-.auth-logo {
- display: flex;
- align-items: center;
- gap: 10px;
- margin-bottom: 8px;
-}
-
-.auth-logo img {
- width: 36px;
- height: 36px;
- border-radius: var(--radius-sm);
-}
-
-.auth-logo-text {
- font-size: 1.4rem;
- font-weight: 800;
- letter-spacing: -0.02em;
-}
-
-.auth-logo-accent { color: var(--accent-3); }
-
-.auth-title {
- font-size: 1.6rem;
- font-weight: 700;
- margin-bottom: 6px;
-}
-
-.auth-subtitle {
- font-size: 0.9rem;
- color: var(--text-secondary);
- margin-bottom: 32px;
-}
-
-.form-group {
- margin-bottom: 20px;
-}
-
-.form-group label {
- display: block;
- font-size: 0.82rem;
- font-weight: 600;
- color: var(--text-secondary);
- margin-bottom: 6px;
- text-transform: uppercase;
- letter-spacing: 0.05em;
-}
-
-.form-group input, .form-group select, .form-group textarea {
- width: 100%;
- padding: 12px 16px;
- background: var(--bg-secondary);
- border: 1px solid var(--border);
- border-radius: var(--radius-sm);
- color: var(--text-primary);
- font-family: 'Schibsted Grotesk', sans-serif;
- font-size: 0.95rem;
- transition: all var(--transition);
- outline: none;
-}
-
-.form-group input:focus, .form-group select:focus, .form-group textarea:focus {
- border-color: var(--accent-1);
- box-shadow: 0 0 0 3px rgba(238, 129, 50, 0.15);
-}
-
-.form-group input::placeholder {
- color: var(--text-muted);
-}
-
-.form-group select {
- cursor: pointer;
- appearance: none;
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
- background-repeat: no-repeat;
- background-position: right 12px center;
-}
-
-/* Custom Select */
-.custom-select {
- position: relative;
- width: 100%;
-}
-
-.custom-select-trigger {
- width: 100%;
- padding: 12px 16px;
- background: var(--bg-secondary);
- border: 1px solid var(--border);
- border-radius: var(--radius-sm);
- color: var(--text-primary);
- font-family: 'Schibsted Grotesk', sans-serif;
- font-size: 0.95rem;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 8px;
- transition: all var(--transition);
- outline: none;
- text-align: left;
-}
-
-.custom-select-trigger:hover {
- border-color: var(--border-hover);
-}
-
-.custom-select-trigger:focus,
-.custom-select.open .custom-select-trigger {
- border-color: var(--accent-1);
- box-shadow: 0 0 0 3px rgba(238, 129, 50, 0.15);
-}
-
-.custom-select-arrow {
- flex-shrink: 0;
- color: var(--text-secondary);
- transition: transform var(--transition);
-}
-
-.custom-select.open .custom-select-arrow {
- transform: rotate(180deg);
-}
-
-.custom-select-dropdown {
- position: absolute;
- top: calc(100% + 4px);
- left: 0;
- right: 0;
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius-sm);
- box-shadow: 0 12px 40px rgba(0,0,0,0.5);
- z-index: 50;
- max-height: 280px;
- overflow-y: auto;
- display: none;
-}
-
-.custom-select-dropdown.open {
- display: block;
-}
-
-.custom-select-option {
- padding: 10px 16px 10px 20px;
- font-size: 0.9rem;
- color: var(--text-secondary);
- cursor: pointer;
- transition: all var(--transition);
- border-left: 2px solid transparent;
-}
-
-.custom-select-option:hover {
- background: rgba(238, 129, 50, 0.08);
- color: var(--text-primary);
- border-left-color: var(--accent-1);
-}
-
-.custom-select-option:first-child {
- border-radius: var(--radius-sm) var(--radius-sm) 0 0;
-}
-
-.custom-select-option:last-child {
- border-radius: 0 0 var(--radius-sm) var(--radius-sm);
-}
-
-.custom-select-category {
- padding: 14px 16px 6px;
- font-size: 0.65rem;
- font-weight: 800;
- text-transform: uppercase;
- letter-spacing: 0.12em;
- color: var(--accent-1);
- cursor: default;
- border-top: 1px solid rgba(238, 129, 50, 0.12);
- margin-top: 6px;
-}
-
-.custom-select-category:first-child {
- border-top: none;
- margin-top: 0;
- padding-top: 12px;
-}
-
-.form-row {
- display: flex;
- gap: 12px;
-}
-
-cap-widget {
- display: block !important;
- width: 100% !important;
- min-width: 100% !important;
- --cap-background: #1c1917;
- --cap-color: #f5f5f4;
- --cap-border-color: rgba(238, 129, 50, 0.15);
- --cap-checkbox-border-radius: 8px;
- --cap-border-radius: 8px;
- --cap-checkbox-background: transparent;
- --cap-spinner-color: #ee8132;
- --cap-spinner-background-color: rgba(238, 129, 50, 0.1);
- --cap-widget-width: 100%;
-}
-
-.form-row .form-group {
- flex: 1;
-}
-
-.btn {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- gap: 8px;
- font-family: 'Schibsted Grotesk', sans-serif;
- font-size: 0.9rem;
- font-weight: 600;
- border-radius: var(--radius-md);
- padding: 12px 22px;
- border: none;
- cursor: pointer;
- text-decoration: none;
- transition: all var(--transition);
- white-space: nowrap;
-}
-
-.btn-primary {
- background: linear-gradient(180deg, #433b32 0%, #29241f 100%);
- color: #fff;
- border: 1px solid rgba(255, 237, 217, 0.2);
-}
-
-.btn-primary:hover {
- color: #fff;
- transform: translateY(-2px);
- border-color: rgba(238, 129, 50, 0.4);
-}
-
-.btn-primary:disabled {
- opacity: 0.6;
- cursor: not-allowed;
- transform: none;
-}
-
-.btn-ghost {
- background: transparent;
- color: var(--text-secondary);
- border: 1px solid var(--border);
-}
-
-.btn-ghost:hover {
- color: var(--text-primary);
- border-color: var(--border-hover);
- background: rgba(238, 129, 50, 0.06);
-}
-
-.btn-danger {
- background: linear-gradient(135deg, var(--accent-red), #dc2626);
- color: #fff;
- box-shadow: 0 4px 20px rgba(239, 68, 68, 0.3);
-}
-
-.btn-danger:hover {
- color: #fff;
- transform: translateY(-2px);
- box-shadow: 0 8px 32px rgba(239, 68, 68, 0.45);
-}
-
-.btn-warning {
- background: linear-gradient(135deg, #f59e0b, #d97706);
- color: #fff;
- box-shadow: 0 4px 20px rgba(245, 158, 11, 0.3);
-}
-
-.btn-warning:hover {
- color: #fff;
- transform: translateY(-2px);
- box-shadow: 0 8px 32px rgba(245, 158, 11, 0.45);
-}
-
-.btn-warning:disabled {
- opacity: 0.6;
- cursor: not-allowed;
- transform: none;
-}
-
-.btn-sm {
- padding: 8px 16px;
- font-size: 0.82rem;
-}
-
-.btn-full {
- width: 100%;
- justify-content: center;
-}
-
-.auth-footer {
- text-align: center;
- margin-top: 24px;
- font-size: 0.88rem;
- color: var(--text-secondary);
-}
-
-.auth-footer a {
- color: var(--accent-3);
- font-weight: 600;
-}
-
-.auth-error {
- background: rgba(239, 68, 68, 0.1);
- border: 1px solid rgba(239, 68, 68, 0.3);
- color: var(--accent-red);
- padding: 12px 16px;
- border-radius: var(--radius-sm);
- font-size: 0.85rem;
- margin-bottom: 20px;
- display: none;
-}
-
-.auth-error.show {
- display: block;
-}
-
-/* ===== DASHBOARD LAYOUT ===== */
-.dashboard-layout {
- display: flex;
- min-height: 100vh;
- position: relative;
-}
-
-.dashboard-layout::before {
- content: '';
- position: fixed;
- inset: 0;
- z-index: 0;
- pointer-events: none;
- background-image:
- linear-gradient(rgba(238, 129, 50, 0.05) 1px, transparent 1px),
- linear-gradient(90deg, rgba(238, 129, 50, 0.05) 1px, transparent 1px);
- background-size: 24px 24px;
- mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, black 30%, transparent 80%);
- -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, black 30%, transparent 80%);
-}
-
-.dashboard-layout::after {
- content: '';
- position: fixed;
- inset: 0;
- z-index: 0;
- pointer-events: none;
- background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.08'/%3E%3C/svg%3E");
- background-repeat: repeat;
- background-size: 512px 512px;
-}
-
-.sidebar {
- width: var(--sidebar-w);
- background: var(--bg-sidebar);
- border-right: 1px solid var(--border);
- display: flex;
- flex-direction: column;
- position: fixed;
- top: 0;
- left: 0;
- bottom: 0;
- z-index: 100;
- overflow: hidden;
- transition: transform var(--transition);
-}
-
-.sidebar.resizing {
- transition: none;
-}
-
-.sidebar-resizer {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- width: 5px;
- cursor: col-resize;
- z-index: 10;
- background: transparent;
- transition: background 0.15s;
-}
-
-.sidebar-resizer:hover,
-.sidebar.resizing .sidebar-resizer {
- background: var(--accent-1);
-}
-
-.sidebar.collapsed {
- width: 60px;
-}
-
-.sidebar.collapsed .sidebar-resizer {
- display: none;
-}
-
-.sidebar.collapsed .sidebar-logo {
- justify-content: center;
-}
-
-.sidebar.collapsed .sidebar-logo-text {
- display: none;
-}
-
-.sidebar.collapsed .sidebar-header {
- padding: 16px 12px;
- justify-content: center;
-}
-
-.sidebar.collapsed .sidebar-nav {
- padding: 12px 8px;
-}
-
-.sidebar.collapsed .nav-section-label {
- display: none;
-}
-
-.sidebar.collapsed .nav-item {
- font-size: 0;
- justify-content: center;
- padding: 10px 0;
- gap: 0;
-}
-
-.sidebar.collapsed .nav-indicator {
- left: 8px;
- width: calc(100% - 16px);
-}
-
-.sidebar.collapsed .nav-item svg {
- font-size: 1rem;
- width: 20px;
- height: 20px;
-}
-
-.sidebar.collapsed .sidebar-footer {
- border-top: none;
- padding: 8px 0 0;
- text-align: center;
-}
-
-.sidebar.collapsed #logout-btn {
- display: none !important;
-}
-
-.sidebar.collapsed .user-info {
- display: block !important;
- padding: 0 !important;
- text-align: center !important;
-}
-
-.sidebar.collapsed #sidebar-user-info > div:first-child {
- gap: 0;
- display: inline-flex !important;
- overflow: visible !important;
-}
-
-.sidebar.collapsed #sidebar-user-info > div:first-child > div:last-child {
- display: none;
-}
-
-.sidebar.collapsed .user-name,
-.sidebar.collapsed .user-email {
- display: none;
-}
-
-.sidebar.collapsed .sidebar-footer > div:not(.user-info) {
- display: none;
-}
-
-.sidebar.collapsed ~ .main-content {
- margin-left: 60px;
-}
-
-.sidebar-tooltip {
- position: fixed;
- z-index: 10000;
- background: var(--bg-card);
- color: var(--text-primary);
- padding: 6px 12px;
- border-radius: var(--radius-sm);
- font-size: 0.85rem;
- white-space: nowrap;
- pointer-events: none;
- opacity: 0;
- transform: translateY(-50%);
- transition: opacity 0.15s ease;
- box-shadow: 0 4px 12px rgba(0,0,0,0.4);
- border: 1px solid var(--border);
-}
-
-.sidebar-tooltip.visible {
- opacity: 1;
-}
-
-.main-content {
- position: relative;
- z-index: 1;
-}
-
-.sidebar-header {
- padding: 20px 20px 16px;
- border-bottom: 1px solid var(--border);
- display: flex;
- align-items: center;
- gap: 10px;
-}
-
-.sidebar-logo {
- display: flex;
- align-items: center;
- gap: 10px;
- text-decoration: none;
-}
-
-.sidebar-logo img {
- width: 28px;
- height: 28px;
- border-radius: 6px;
-}
-
-.sidebar-logo-text {
- font-size: 1.1rem;
- font-weight: 800;
- color: var(--text-primary);
- letter-spacing: -0.02em;
-}
-
-.sidebar-nav {
- flex: 1;
- padding: 8px 12px;
- overflow-y: auto;
- position: relative;
-}
-
-.nav-indicator {
- position: absolute;
- left: 12px;
- width: calc(100% - 24px);
- background: rgba(238, 129, 50, 0.12);
- border-radius: var(--radius-sm);
- border-left: 3px solid var(--accent-1);
- transition: top var(--transition), height var(--transition), opacity var(--transition);
- pointer-events: none;
- z-index: 1;
-}
-
-.nav-section-label {
- font-size: 0.7rem;
- font-weight: 700;
- text-transform: uppercase;
- letter-spacing: 0.1em;
- color: var(--text-muted);
- padding: 16px 12px 6px;
-}
-
-.nav-item {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 10px 12px;
- border-radius: var(--radius-sm);
- color: var(--text-secondary);
- font-size: 0.9rem;
- font-weight: 500;
- cursor: pointer;
- transition: color var(--transition);
- text-decoration: none;
- margin-bottom: 2px;
- position: relative;
- z-index: 2;
-}
-
-.nav-item:hover {
- color: var(--text-primary);
- background: rgba(238, 129, 50, 0.08);
-}
-
-.nav-item.active {
- color: var(--accent-1);
-}
-
-.nav-item svg {
- width: 18px;
- height: 18px;
- flex-shrink: 0;
-}
-
-.sidebar-footer {
- padding: 12px 12px 4px;
- border-top: 1px solid var(--border);
-}
-
-.user-info {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 8px 12px;
-}
-
-.user-info > div:first-child {
- min-width: 0;
- overflow: hidden;
-}
-.user-info > div:first-child > div:last-child {
- min-width: 0;
- overflow: hidden;
-}
-
-.user-avatar {
- width: 32px;
- height: 32px;
- border-radius: 50%;
- background: var(--accent-1);
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 0.8rem;
- font-weight: 700;
- color: #fff;
- flex-shrink: 0;
-}
-
-.user-name {
- font-size: 0.85rem;
- font-weight: 600;
- color: var(--text-primary);
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.user-email {
- font-size: 0.75rem;
- color: var(--text-muted);
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.main-content {
- flex: 1;
- margin-left: var(--sidebar-w);
- padding: 32px;
- min-height: 100vh;
-}
-
-/* ===== DASHBOARD PAGES ===== */
-.page {
- display: none;
-}
-
-.page.active {
- display: block;
-}
-
-.page-header {
- margin-bottom: 32px;
-}
-
-.page-title {
- font-size: 1.8rem;
- font-weight: 800;
- letter-spacing: -0.02em;
- margin-bottom: 6px;
-}
-
-.page-subtitle {
- font-size: 0.92rem;
- color: var(--text-secondary);
-}
-
-/* ===== CARDS ===== */
-.card {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius-lg);
- padding: 24px;
- transition: all var(--transition);
-}
-
-.card:hover {
- border-color: var(--border-hover);
-}
-
-.card-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 16px;
-}
-
-.card-title {
- font-size: 1rem;
- font-weight: 700;
-}
-
-.stat-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
- gap: 20px;
- margin-bottom: 32px;
-}
-
-.stat-card {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius-lg);
- padding: 24px;
- position: relative;
- overflow: hidden;
-}
-
-.stat-card::before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- width: 4px;
- height: 100%;
- background: linear-gradient(180deg, var(--accent-1), var(--accent-2));
- border-radius: 0 2px 2px 0;
-}
-
-.stat-icon {
- width: 40px;
- height: 40px;
- border-radius: var(--radius-sm);
- display: flex;
- align-items: center;
- justify-content: center;
- margin-bottom: 16px;
- color: var(--accent-1);
- background: rgba(238, 129, 50, 0.12);
-}
-
-.stat-value {
- font-size: 2rem;
- font-weight: 800;
- letter-spacing: -0.03em;
- margin-bottom: 4px;
-}
-
-.stat-label {
- font-size: 0.82rem;
- color: var(--text-secondary);
- font-weight: 500;
-}
-
-.chart-container {
- position: relative;
- max-width: 320px;
- margin: 0 auto;
-}
-
-.chart-container canvas {
- width: 100% !important;
- height: auto !important;
-}
-
-/* ===== SERVER LIST ===== */
-.server-grid {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
- gap: 16px;
-}
-
-.server-card {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius-lg);
- padding: 20px;
- transition: all var(--transition);
- cursor: pointer;
-}
-
-.server-card:hover {
- border-color: var(--border-hover);
- background: var(--bg-card-hover);
- transform: translateY(-2px);
- box-shadow: 0 8px 32px rgba(0,0,0,0.3);
-}
-
-.server-card-top {
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- margin-bottom: 12px;
-}
-
-.server-card-name {
- font-size: 1rem;
- font-weight: 700;
- color: var(--text-primary);
-}
-
-.server-card-status {
- font-size: 0.7rem;
- font-weight: 600;
- padding: 3px 10px;
- border-radius: 99px;
- text-transform: uppercase;
- letter-spacing: 0.05em;
-}
-
-.power-state-dot {
- display: inline-flex;
- align-items: center;
- gap: 4px;
- font-size: 0.7rem;
- font-weight: 500;
- margin-left: 6px;
-}
-.power-state-dot::before {
- content: '';
- width: 7px;
- height: 7px;
- border-radius: 50%;
- flex-shrink: 0;
-}
-.power-state-dot.running::before { background: #10b981; }
-.power-state-dot.offline::before { background: #6b7280; }
-.power-state-dot.starting::before { background: #f59e0b; animation: pulse-dot 1s ease-in-out infinite; }
-.power-state-dot.stopping::before { background: #f59e0b; animation: pulse-dot 1s ease-in-out infinite; }
-
-@keyframes pulse-dot {
- 0%, 100% { opacity: 1; }
- 50% { opacity: 0.4; }
-}
-
-.status-active {
- background: rgba(16, 185, 129, 0.12);
- color: var(--accent-green);
-}
-
-.status-suspended {
- background: rgba(239, 68, 68, 0.12);
- color: var(--accent-red);
-}
-
-.status-expired {
- background: rgba(245, 158, 11, 0.12);
- color: var(--accent-orange);
-}
-
-.status-installing {
- background: rgba(245, 158, 11, 0.12);
- color: var(--accent-orange);
-}
-
-.status-offline {
- background: rgba(107, 114, 128, 0.12);
- color: #9ca3af;
-}
-
-.server-card-details {
- display: flex;
- flex-wrap: wrap;
- gap: 6px;
-}
-
-.server-detail-tag {
- font-size: 0.75rem;
- color: var(--text-secondary);
- background: rgba(255,255,255,0.04);
- border: 1px solid rgba(255,255,255,0.06);
- padding: 4px 10px;
- border-radius: 4px;
- font-family: 'JetBrains Mono', monospace;
-}
-
-.server-card-actions {
- margin-top: 12px;
- padding-top: 12px;
- border-top: 1px solid var(--border);
- display: flex;
- gap: 8px;
-}
-
-/* ===== TABLE ===== */
-.table-container {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius-lg);
- overflow: hidden;
-}
-
-table {
- width: 100%;
- border-collapse: collapse;
-}
-
-thead th {
- text-align: left;
- padding: 14px 20px;
- font-size: 0.78rem;
- font-weight: 700;
- text-transform: uppercase;
- letter-spacing: 0.06em;
- color: var(--text-muted);
- background: rgba(255,255,255,0.02);
- border-bottom: 1px solid var(--border);
-}
-
-tbody td {
- padding: 14px 20px;
- font-size: 0.9rem;
- border-bottom: 1px solid rgba(255,255,255,0.04);
-}
-
-tbody tr:last-child td {
- border-bottom: none;
-}
-
-tbody tr:hover {
- background: rgba(238, 129, 50, 0.04);
-}
-
-/* ===== TABS ===== */
-.tabs {
- position: relative;
- display: flex;
- gap: 4px;
- margin-bottom: 24px;
- border-bottom: 1px solid var(--border);
- padding-bottom: 0;
-}
-
-.tab {
- background: none;
- border: none;
- color: var(--text-secondary);
- padding: 10px 20px;
- font-size: 0.9rem;
- font-weight: 500;
- cursor: pointer;
- margin-bottom: -1px;
- transition: color 0.15s;
-}
-
-.tab:hover {
- color: var(--text-primary);
-}
-
-.tab.active {
- color: var(--accent-1);
-}
-
-.tab-indicator {
- position: absolute;
- bottom: -1px;
- left: 0;
- height: 2px;
- background: var(--accent-1);
- transition: left 0.2s ease, width 0.2s ease;
- pointer-events: none;
-}
-
-.tab-content {
- animation: fadeIn 0.15s ease;
-}
-
-@keyframes fadeIn {
- from { opacity: 0; }
- to { opacity: 1; }
-}
-
-/* ===== ACTION CARDS ===== */
-.action-card {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius-xl);
- padding: 20px;
- margin-bottom: 16px;
-}
-
-.action-card-header {
- display: flex;
- align-items: flex-start;
- gap: 14px;
- margin-bottom: 16px;
-}
-
-.action-card-header svg {
- flex-shrink: 0;
- margin-top: 2px;
- color: var(--accent-1);
-}
-
-.action-card-title {
- font-size: 1rem;
- font-weight: 600;
- color: var(--text-primary);
- margin: 0 0 4px 0;
-}
-
-.action-card-desc {
- font-size: 0.85rem;
- color: var(--text-muted);
- line-height: 1.5;
- margin: 0;
-}
-
-/* ===== MODAL ===== */
-.modal-overlay {
- position: fixed;
- inset: 0;
- background: rgba(0,0,0,0.7);
- backdrop-filter: blur(4px);
- z-index: 200;
- display: none;
- align-items: center;
- justify-content: center;
- padding: 24px;
-}
-
-.modal-overlay.open {
- display: flex;
-}
-
-.modal {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius-xl);
- padding: 32px;
- width: 100%;
- max-width: 520px;
- max-height: 90vh;
- overflow-y: auto;
- box-shadow: 0 32px 80px rgba(0,0,0,0.6);
-}
-
-.modal-title {
- font-size: 1.3rem;
- font-weight: 700;
- margin-bottom: 24px;
-}
-
-.modal-actions {
- display: flex;
- gap: 12px;
- margin-top: 28px;
-}
-
-/* ===== EMPTY STATE ===== */
-.empty-state {
- text-align: center;
- padding: 60px 24px;
- background: var(--bg-card);
- border: 1px dashed var(--border);
- border-radius: var(--radius-lg);
-}
-
-.empty-state-icon {
- width: 56px;
- height: 56px;
- border-radius: 50%;
- background: rgba(238, 129, 50, 0.1);
- display: flex;
- align-items: center;
- justify-content: center;
- margin: 0 auto 16px;
- color: var(--accent-1);
-}
-
-.empty-state-title {
- font-size: 1.1rem;
- font-weight: 700;
- margin-bottom: 8px;
-}
-
-.empty-state-desc {
- font-size: 0.88rem;
- color: var(--text-secondary);
- margin-bottom: 20px;
-}
-
-/* ===== SPINNER ===== */
-.spinner {
- display: inline-block;
- width: 20px;
- height: 20px;
- border: 2px solid rgba(255,255,255,0.2);
- border-top-color: #fff;
- border-radius: 50%;
- animation: spin 0.6s linear infinite;
-}
-
-@keyframes spin {
- to { transform: rotate(360deg); }
-}
-
-/* ===== TOAST ===== */
-.toast-container {
- position: fixed;
- bottom: 24px;
- right: 24px;
- z-index: 300;
- display: flex;
- flex-direction: column;
- gap: 8px;
-}
-
-.toast {
- padding: 14px 20px;
- border-radius: var(--radius-sm);
- font-size: 0.88rem;
- font-weight: 500;
- animation: slideUp 0.3s ease;
- box-shadow: 0 8px 32px rgba(0,0,0,0.4);
-}
-
-.toast-success {
- background: rgba(16, 185, 129, 0.15);
- border: 1px solid rgba(16, 185, 129, 0.3);
- color: var(--accent-green);
-}
-
-.toast-error {
- background: rgba(239, 68, 68, 0.15);
- border: 1px solid rgba(239, 68, 68, 0.3);
- color: var(--accent-red);
-}
-
-@keyframes slideUp {
- from { opacity: 0; transform: translateY(20px); }
- to { opacity: 1; transform: translateY(0); }
-}
-
-/* ===== NOTIFICATION PANEL ===== */
-.notif-panel {
- position: fixed;
- top: 0;
- left: var(--sidebar-w);
- width: 380px;
- height: 100vh;
- background: var(--bg-card);
- border-right: 1px solid var(--border);
- z-index: 99;
- transform: translateX(-100%);
- transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
- display: flex;
- flex-direction: column;
- overflow: hidden;
-}
-
-.notif-panel.open {
- transform: translateX(0);
-}
-
-.sidebar.collapsed ~ .notif-panel {
- left: 60px;
-}
-
-.notif-backdrop {
- position: fixed;
- inset: 0;
- z-index: 50;
- background: rgba(0, 0, 0, 0.5);
- backdrop-filter: blur(4px);
- -webkit-backdrop-filter: blur(4px);
- opacity: 0;
- pointer-events: none;
- transition: opacity 0.3s ease;
-}
-
-.notif-backdrop.open {
- opacity: 1;
- pointer-events: auto;
-}
-
-.notif-panel-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 20px 20px 16px;
- border-bottom: 1px solid var(--border);
- flex-shrink: 0;
-}
-
-.notif-panel-header h3 {
- font-size: 1.1rem;
- font-weight: 700;
- color: var(--text-primary);
-}
-
-.notif-mark-all {
- background: none;
- border: none;
- color: var(--accent-3);
- font-size: 0.82rem;
- font-weight: 600;
- cursor: pointer;
- padding: 4px 8px;
- border-radius: var(--radius-sm);
- transition: all var(--transition);
-}
-
-.notif-mark-all:hover {
- background: rgba(238, 129, 50, 0.1);
-}
-
-.notif-panel-list {
- flex: 1;
- overflow-y: auto;
- padding: 8px 0;
-}
-
-.notif-empty {
- text-align: center;
- color: var(--text-muted);
- padding: 48px 20px;
- font-size: 0.9rem;
-}
-
-.notif-item {
- display: flex;
- align-items: flex-start;
- gap: 12px;
- padding: 14px 20px;
- cursor: pointer;
- transition: background var(--transition);
- position: relative;
-}
-
-.notif-item:hover {
- background: rgba(238, 129, 50, 0.04);
-}
-
-.notif-unread {
- background: rgba(238, 129, 50, 0.06);
-}
-
-.notif-unread:hover {
- background: rgba(238, 129, 50, 0.1);
-}
-
-.notif-item-icon {
- width: 36px;
- height: 36px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
-}
-
-.notif-icon {
- width: 18px;
- height: 18px;
-}
-
-.notif-item-icon.notif-success {
- background: rgba(16, 185, 129, 0.15);
- color: var(--accent-green);
-}
-
-.notif-item-icon.notif-error {
- background: rgba(239, 68, 68, 0.15);
- color: var(--accent-red);
-}
-
-.notif-item-icon.notif-warning {
- background: rgba(245, 158, 11, 0.15);
- color: var(--accent-orange);
-}
-
-.notif-item-icon.notif-info {
- background: rgba(6, 182, 212, 0.15);
- color: var(--accent-cyan);
-}
-
-.notif-item-body {
- flex: 1;
- min-width: 0;
-}
-
-.notif-item-title {
- font-size: 0.88rem;
- font-weight: 600;
- color: var(--text-primary);
- margin-bottom: 2px;
-}
-
-.notif-item-msg {
- font-size: 0.82rem;
- color: var(--text-secondary);
- line-height: 1.4;
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- overflow: hidden;
-}
-
-.notif-item-time {
- font-size: 0.75rem;
- color: var(--text-muted);
- margin-top: 4px;
-}
-
-.notif-dot {
- width: 8px;
- height: 8px;
- border-radius: 50%;
- background: var(--accent-3);
- flex-shrink: 0;
- margin-top: 6px;
-}
-
-.notif-badge {
- position: absolute;
- top: -6px;
- right: -6px;
- min-width: 18px;
- height: 18px;
- border-radius: 99px;
- background: var(--accent-red);
- color: #fff;
- font-size: 0.65rem;
- font-weight: 700;
- display: none;
- align-items: center;
- justify-content: center;
- padding: 0 5px;
- line-height: 1;
-}
-
-/* ===== RESPONSIVE ===== */
-.hamburger-toggle {
- display: none;
- background: none;
- border: none;
- color: var(--text-secondary);
- cursor: pointer;
- padding: 8px;
-}
-
-@media (max-width: 768px) {
- .sidebar {
- transform: translateX(-100%);
- }
-
- .sidebar.open {
- transform: translateX(0);
- }
-
- .sidebar-resizer {
- display: none;
- }
-
- .main-content {
- margin-left: 0;
- padding: 20px;
- }
-
- .hamburger-toggle {
- display: flex;
- align-items: center;
- justify-content: center;
- position: fixed;
- top: 16px;
- left: 16px;
- z-index: 101;
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius-sm);
- padding: 10px;
- }
-
- .stat-grid {
- grid-template-columns: 1fr;
- }
-
- .server-grid {
- grid-template-columns: 1fr;
- }
-
- .form-row {
- flex-direction: column;
- gap: 0;
- }
-
- .auth-card {
- padding: 32px 24px;
- }
-
- .table-container {
- overflow-x: auto;
- }
-}
-
-/* ===== PYRODACTYL PAGE ===== */
-.ptero-grid {
- max-width: 640px;
-}
-
-.ptero-card-icon {
- width: 56px;
- height: 56px;
- border-radius: var(--radius-md);
- display: flex;
- align-items: center;
- justify-content: center;
- margin-bottom: 20px;
- color: var(--accent-cyan);
- background: rgba(34, 211, 238, 0.1);
-}
-
-.ptero-card-title {
- font-size: 1.4rem;
- font-weight: 700;
- margin-bottom: 12px;
-}
-
-.ptero-card-desc {
- color: var(--text-secondary);
- line-height: 1.7;
- margin-bottom: 24px;
-}
-
-.ptero-info {
- display: flex;
- flex-direction: column;
- gap: 14px;
-}
-
-.ptero-info-item {
- display: flex;
- align-items: flex-start;
- gap: 12px;
- font-size: 0.9rem;
- color: var(--text-secondary);
- line-height: 1.5;
-}
-
-.ptero-info-item svg {
- flex-shrink: 0;
- margin-top: 2px;
- color: var(--accent-cyan);
-}
-
-.ptero-info-item span {
- flex: 1;
-}
-
-.ptero-info-item strong {
- color: var(--text-primary);
-}
-
-/* ===== ACCOUNT PAGE ===== */
-.account-grid {
- display: flex;
- flex-direction: column;
- gap: 24px;
- max-width: 640px;
-}
-
-.account-grid .card-title {
- font-size: 1.1rem;
- font-weight: 700;
-}
-
-.account-menu-card {
- transition: background var(--transition);
-}
-
-.account-menu-card:hover {
- background: var(--bg-secondary);
-}
-
-.account-menu-item {
- display: flex;
- align-items: center;
- gap: 16px;
-}
-
-.account-menu-icon {
- width: 44px;
- height: 44px;
- border-radius: var(--radius-sm);
- background: var(--bg-secondary);
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- color: var(--accent-3);
-}
-
-.account-menu-text {
- flex: 1;
- min-width: 0;
-}
-
-.account-menu-title {
- font-weight: 600;
- font-size: 0.95rem;
- color: var(--text-primary);
-}
-
-.account-menu-desc {
- font-size: 0.8rem;
- color: var(--text-muted);
- margin-top: 2px;
-}
-
-/* ===== AVATAR UPLOAD ===== */
-.avatar-upload {
- display: flex;
- gap: 20px;
- align-items: flex-start;
- flex-wrap: wrap;
-}
-
-.avatar-upload-preview {
- width: 100px;
- height: 100px;
- border-radius: 50%;
- background: var(--accent-1);
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 2.5rem;
- font-weight: 700;
- color: #fff;
- flex-shrink: 0;
- overflow: hidden;
- position: relative;
-}
-
-.avatar-upload-preview img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- border-radius: 50%;
-}
-
-.avatar-upload-placeholder {
- position: absolute;
- inset: 0;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.avatar-upload-info {
- flex: 1;
- min-width: 200px;
-}
-
-/* ===== SERVER EXPIRY ===== */
-.server-card-expiry {
- display: flex;
- gap: 6px;
- font-size: 0.75rem;
- color: var(--text-secondary);
- padding: 8px 0 4px;
- flex-wrap: wrap;
-}
-
-.server-card-expiry.expired {
- color: var(--accent-red);
-}
-
-.server-card-expiry.expiring {
- color: var(--accent-orange);
-}
-
-/* ===== SERVER DETAIL PAGE ===== */
-.server-detail-grid {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 20px;
- align-items: start;
-}
-
-
-
-.detail-list {
- display: flex;
- flex-direction: column;
- gap: 12px;
-}
-
-.detail-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding-bottom: 8px;
- border-bottom: 1px solid rgba(255,255,255,0.04);
-}
-
-.detail-item:last-child {
- border-bottom: none;
- padding-bottom: 0;
-}
-
-.detail-label {
- font-size: 0.82rem;
- color: var(--text-muted);
- text-transform: uppercase;
- letter-spacing: 0.05em;
- font-weight: 600;
-}
-
-.detail-value {
- font-size: 0.9rem;
- color: var(--text-primary);
- font-weight: 500;
-}
-
-@media (max-width: 768px) {
- .server-detail-grid {
- grid-template-columns: 1fr;
- }
-}
-
-/* ===== RGPD / COOKIE CONSENT ===== */
-.cookie-banner {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- z-index: 9999;
- background: var(--bg-card);
- border-top: 1px solid var(--border);
- padding: 20px 24px;
- box-shadow: 0 -8px 40px rgba(0,0,0,0.5);
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 20px;
- flex-wrap: wrap;
- animation: slideUpFromBelow 0.4s ease;
-}
-
-@keyframes slideUpFromBelow {
- from { transform: translateY(100%); opacity: 0; }
- to { transform: translateY(0); opacity: 1; }
-}
-
-.cookie-banner-text {
- flex: 1;
- min-width: 280px;
-}
-
-.cookie-banner-text p {
- font-size: 0.88rem;
- color: var(--text-secondary);
- line-height: 1.6;
- margin-bottom: 4px;
-}
-
-.cookie-banner-text a {
- color: var(--accent-3);
- text-decoration: underline;
-}
-
-.cookie-banner-actions {
- display: flex;
- gap: 10px;
- flex-shrink: 0;
- flex-wrap: wrap;
-}
-
-/* ===== RGPD CONSENT CHECKBOX ===== */
-.consent-group {
- margin-bottom: 20px;
- display: flex;
- align-items: flex-start;
- gap: 10px;
-}
-
-.custom-checkbox {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- position: relative;
- cursor: pointer;
- flex-shrink: 0;
- margin-top: 2px;
-}
-
-.custom-checkbox input[type="checkbox"] {
- position: absolute;
- opacity: 0;
- width: 0;
- height: 0;
- pointer-events: none;
-}
-
-.custom-checkbox .checkmark {
- width: 20px;
- height: 20px;
- border: 2px solid var(--text-secondary);
- border-radius: 4px;
- background: transparent;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: all 0.2s ease;
- flex-shrink: 0;
- box-sizing: border-box;
-}
-
-.custom-checkbox input[type="checkbox"]:checked ~ .checkmark {
- background: var(--accent-1);
- border-color: var(--accent-1);
-}
-
-.custom-checkbox input[type="checkbox"]:checked ~ .checkmark::after {
- content: '';
- display: block;
- width: 5px;
- height: 9px;
- border: solid #fff;
- border-width: 0 2px 2px 0;
- transform: rotate(45deg);
- margin-top: -1px;
-}
-
-.custom-checkbox input[type="checkbox"]:focus-visible ~ .checkmark {
- outline: 2px solid var(--accent-1);
- outline-offset: 2px;
-}
-
-.consent-group label {
- font-size: 0.85rem;
- color: var(--text-secondary);
- line-height: 1.5;
- cursor: pointer;
-}
-
-.consent-group label a {
- color: var(--accent-3);
- text-decoration: underline;
-}
-
-.cap-modal-overlay {
- position: fixed;
- inset: 0;
- z-index: 9999;
- display: flex;
- align-items: center;
- justify-content: center;
- backdrop-filter: blur(8px);
- -webkit-backdrop-filter: blur(8px);
- background: rgba(0, 0, 0, 0.5);
- animation: capFadeIn 0.2s ease;
-}
-
-.cap-modal-overlay.cap-modal-fadeout {
- animation: capFadeOut 0.3s ease forwards;
-}
-
-.cap-modal {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: 12px;
- padding: 24px;
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
- animation: capScaleIn 0.2s ease;
-}
-
-.cap-modal cap-widget {
- display: block !important;
- width: 300px !important;
- min-width: unset !important;
-}
-
-.cap-modal-overlay.cap-modal-fadeout .cap-modal {
- animation: capScaleOut 0.3s ease forwards;
-}
-
-@keyframes capFadeIn {
- from { opacity: 0; }
- to { opacity: 1; }
-}
-
-@keyframes capFadeOut {
- from { opacity: 1; }
- to { opacity: 0; }
-}
-
-@keyframes capScaleIn {
- from { transform: scale(0.9); opacity: 0; }
- to { transform: scale(1); opacity: 1; }
-}
-
-@keyframes capScaleOut {
- from { transform: scale(1); opacity: 1; }
- to { transform: scale(0.9); opacity: 0; }
-}
-
-/* ===== ACTIVITY TIMELINE ===== */
-.activity-list {
- display: flex;
- flex-direction: column;
- gap: 0;
-}
-
-.activity-item {
- display: flex;
- align-items: flex-start;
- gap: 14px;
- padding: 14px 0;
- border-bottom: 1px solid rgba(255,255,255,0.04);
- position: relative;
-}
-
-.activity-item:last-child {
- border-bottom: none;
-}
-
-.activity-icon {
- width: 32px;
- height: 32px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
- font-size: 0.8rem;
- position: relative;
- z-index: 1;
-}
-
-.activity-icon-server_created { background: rgba(16,185,129,0.15); color: var(--accent-green); }
-.activity-icon-server_renewed { background: rgba(6,182,212,0.15); color: var(--accent-cyan); }
-.activity-icon-server_renamed { background: rgba(238,129,50,0.15); color: var(--accent-1); }
-.activity-icon-server_reinstalled { background: rgba(245,158,11,0.15); color: var(--accent-orange); }
-.activity-icon-server_deleted { background: rgba(239,68,68,0.15); color: var(--accent-red); }
-.activity-icon-account_registered { background: rgba(168,85,247,0.15); color: #a855f7; }
-.activity-icon-password_changed { background: rgba(59,130,246,0.15); color: #3b82f6; }
-.activity-icon-email_changed { background: rgba(59,130,246,0.15); color: #3b82f6; }
-.activity-icon-account_deleted { background: rgba(239,68,68,0.15); color: var(--accent-red); }
-.activity-icon-api_key_updated { background: rgba(236,201,75,0.15); color: #ecc94b; }
-.activity-icon-avatar_updated { background: rgba(168,85,247,0.15); color: #a855f7; }
-.activity-icon-admin_suspend { background: rgba(245,158,11,0.15); color: var(--accent-orange); }
-.activity-icon-admin_unsuspend { background: rgba(16,185,129,0.15); color: var(--accent-green); }
-.activity-icon-admin_renew_now { background: rgba(6,182,212,0.15); color: var(--accent-cyan); }
-
-.activity-content {
- flex: 1;
- min-width: 0;
-}
-
-.activity-action {
- font-size: 0.88rem;
- font-weight: 600;
- color: var(--text-primary);
-}
-
-.activity-details {
- font-size: 0.78rem;
- color: var(--text-muted);
- margin-top: 2px;
-}
-
-.activity-time {
- font-size: 0.72rem;
- color: var(--text-muted);
- white-space: nowrap;
- padding-top: 2px;
-}
-
-.activity-empty {
- text-align: center;
- padding: 32px 16px;
- color: var(--text-muted);
- font-size: 0.88rem;
-}
-
-/* ===== LOG PAGE ===== */
-.log-pagination {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 12px;
- padding: 16px 0;
-}
-
-.log-pagination-info {
- font-size: 0.82rem;
- color: var(--text-muted);
-}
-
-.log-pagination .btn-ghost[disabled] {
- opacity: 0.4;
- cursor: not-allowed;
- pointer-events: none;
-}
-
-/* ===== SEARCH & FILTERS ===== */
-.servers-toolbar {
- display: flex;
- gap: 12px;
- margin-bottom: 20px;
- flex-wrap: wrap;
- align-items: center;
-}
-
-.search-wrapper {
- position: relative;
- flex: 1;
- min-width: 200px;
-}
-
-.search-wrapper svg {
- position: absolute;
- left: 14px;
- top: 50%;
- transform: translateY(-50%);
- color: var(--text-muted);
- pointer-events: none;
-}
-
-.search-wrapper input {
- width: 100%;
- padding: 10px 14px 10px 40px;
- background: var(--bg-secondary);
- border: 1px solid var(--border);
- border-radius: var(--radius-sm);
- color: var(--text-primary);
- font-family: 'Schibsted Grotesk', sans-serif;
- font-size: 0.9rem;
- outline: none;
- transition: all var(--transition);
-}
-
-.search-wrapper input:focus {
- border-color: var(--accent-1);
- box-shadow: 0 0 0 3px rgba(238,129,50,0.15);
-}
-
-.search-wrapper input::placeholder {
- color: var(--text-muted);
-}
-
-.filter-group {
- display: flex;
- gap: 8px;
- flex-wrap: wrap;
-}
-
-.filter-btn {
- padding: 8px 16px;
- background: var(--bg-secondary);
- border: 1px solid var(--border);
- border-radius: 99px;
- color: var(--text-secondary);
- font-family: 'Schibsted Grotesk', sans-serif;
- font-size: 0.8rem;
- font-weight: 500;
- cursor: pointer;
- transition: all var(--transition);
- white-space: nowrap;
-}
-
-.filter-btn:hover {
- border-color: var(--border-hover);
- color: var(--text-primary);
-}
-
-.filter-btn.active {
- background: rgba(238,129,50,0.12);
- border-color: var(--accent-1);
- color: var(--accent-1);
-}
-
-
-
-/* Resource gauges for server detail */
-.resource-gauges {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 20px;
- margin-top: 8px;
-}
-
-.resource-gauge {
- text-align: center;
- padding: 20px 12px;
- background: rgba(255,255,255,0.02);
- border: 1px solid var(--border);
- border-radius: var(--radius-md);
- position: relative;
- overflow: hidden;
-}
-
-.resource-gauge-value {
- font-size: 1.8rem;
- font-weight: 800;
- letter-spacing: -0.03em;
- margin-bottom: 4px;
-}
-
-.resource-gauge-label {
- font-size: 0.75rem;
- color: var(--text-muted);
- text-transform: uppercase;
- letter-spacing: 0.06em;
- font-weight: 600;
-}
-
-.resource-gauge-bar {
- margin-top: 12px;
- height: 4px;
- background: rgba(255,255,255,0.06);
- border-radius: 99px;
- overflow: hidden;
-}
-
-.resource-gauge-fill {
- height: 100%;
- border-radius: 99px;
- transition: width 1s cubic-bezier(0.4, 0, 0.2, 1);
-}
-
-.resource-gauge-sub {
- font-size: 0.68rem;
- color: var(--text-muted);
- margin-top: 6px;
- font-family: 'JetBrains Mono', monospace;
-}
-
-/* Account API key section */
-.api-key-input-group {
- display: flex;
- gap: 8px;
- align-items: stretch;
-}
-
-.api-key-input-group input {
- flex: 1;
-}
-
-@media (max-width: 768px) {
- .servers-toolbar {
- flex-direction: column;
- align-items: stretch;
- }
- .filter-group {
- justify-content: center;
- }
- .resource-gauges {
- grid-template-columns: 1fr;
- }
-}
-
-/* ===== ADMIN PANEL ===== */
-.admin-layout {
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- position: relative;
-}
-
-.admin-navbar {
- display: flex;
- align-items: center;
- padding: 0 24px;
- height: 60px;
- background: var(--bg-sidebar);
- border-bottom: 1px solid var(--border);
- position: sticky;
- top: 0;
- z-index: 100;
- gap: 16px;
-}
-
-.admin-navbar-left {
- display: flex;
- align-items: center;
- gap: 10px;
- flex-shrink: 0;
-}
-
-.admin-navbar-logo {
- width: 28px;
- height: 28px;
- border-radius: 6px;
-}
-
-.admin-navbar-brand {
- font-size: 1rem;
- font-weight: 800;
- letter-spacing: -0.02em;
- display: flex;
- align-items: center;
- gap: 6px;
-}
-
-.admin-badge {
- font-size: 0.6rem;
- font-weight: 700;
- text-transform: uppercase;
- letter-spacing: 0.08em;
- color: var(--accent-1);
- border: 1px solid var(--accent-1);
- padding: 2px 6px;
- border-radius: 4px;
-}
-
-.admin-navbar-center {
- display: flex;
- align-items: center;
- gap: 4px;
- flex: 1;
- justify-content: center;
-}
-
-.admin-nav-link {
- display: flex;
- align-items: center;
- gap: 8px;
- padding: 8px 16px;
- border-radius: var(--radius-sm);
- color: var(--text-secondary);
- font-size: 0.88rem;
- font-weight: 500;
- text-decoration: none;
- transition: all var(--transition);
-}
-
-.admin-nav-link:hover {
- color: var(--text-primary);
- background: rgba(238, 129, 50, 0.08);
-}
-
-.admin-nav-link.active {
- color: var(--accent-1);
- background: rgba(238, 129, 50, 0.12);
-}
-
-.admin-navbar-right {
- display: flex;
- align-items: center;
- gap: 12px;
- flex-shrink: 0;
-}
-
-.admin-navbar-user {
- font-size: 0.85rem;
- font-weight: 600;
- color: var(--text-primary);
-}
-
-.admin-content {
- flex: 1;
- padding: 32px;
- position: relative;
- z-index: 1;
-}
-
-.admin-page {
- display: none;
-}
-
-.admin-page.active {
- display: block;
-}
-
-.admin-layout::before {
- content: '';
- position: fixed;
- inset: 0;
- z-index: 0;
- pointer-events: none;
- background-image:
- linear-gradient(rgba(238, 129, 50, 0.05) 1px, transparent 1px),
- linear-gradient(90deg, rgba(238, 129, 50, 0.05) 1px, transparent 1px);
- background-size: 24px 24px;
- mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, black 30%, transparent 80%);
- -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, black 30%, transparent 80%);
-}
-
-.admin-notice {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 12px 16px;
- margin-bottom: 24px;
- background: rgba(238, 129, 50, 0.08);
- border: 1px solid rgba(238, 129, 50, 0.2);
- border-radius: var(--radius-sm);
- font-size: 0.85rem;
- color: var(--text-secondary);
- line-height: 1.5;
-}
-
-.admin-notice svg {
- flex-shrink: 0;
- color: var(--accent-1);
- opacity: 0.8;
-}
-
-.admin-content > * {
- position: relative;
- z-index: 1;
-}
-
-@media (max-width: 768px) {
- .admin-navbar {
- padding: 0 12px;
- gap: 8px;
- }
- .admin-navbar-brand {
- font-size: 0.85rem;
- }
- .admin-navbar-user {
- display: none;
- }
- .admin-content {
- padding: 20px;
- }
-}
-
-.date-tooltip {
- cursor: help;
-}
-#admin-date-tooltip {
- position: fixed;
- z-index: 10000;
- background: var(--bg-card);
- color: var(--text-primary);
- padding: 6px 12px;
- border-radius: var(--radius-sm);
- font-size: 0.85rem;
- white-space: nowrap;
- pointer-events: none;
- opacity: 0;
- box-shadow: 0 4px 12px rgba(0,0,0,0.4);
- border: 1px solid var(--border);
- transition: opacity 0.15s ease;
-}
-#admin-date-tooltip.visible {
- opacity: 1;
-}
-
-
+*, *::before, *::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+:root {
+ --bg-primary: #000;
+ --bg-secondary: #1c1917;
+ --bg-tertiary: #292524;
+ --bg-card: #1c1917;
+ --bg-card-hover: #292524;
+ --bg-sidebar: #0d0d0d;
+ --border: rgba(238, 129, 50, 0.15);
+ --border-hover: rgba(238, 129, 50, 0.3);
+ --accent-1: #ee8132;
+ --accent-2: #ee8132;
+ --accent-3: #ee8132;
+ --accent-cyan: #06b6d4;
+ --accent-green: #059669;
+ --accent-red: #ef4444;
+ --accent-orange: #f59e0b;
+ --text-primary: #f5f5f4;
+ --text-secondary: #a8a29e;
+ --text-muted: #78716c;
+ --glow-primary: rgba(238, 129, 50, 0.15);
+ --radius-sm: 8px;
+ --radius-md: 14px;
+ --radius-lg: 20px;
+ --radius-xl: 28px;
+ --transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
+ --sidebar-w: 260px;
+}
+
+html { height: 100%; }
+
+body, body * {
+ font-family: 'Schibsted Grotesk', system-ui, sans-serif;
+ font-weight: 400;
+}
+
+body {
+ height: 100%;
+ background: var(--bg-primary);
+ color: var(--text-primary);
+ line-height: 1.6;
+ overflow-x: hidden;
+}
+
+::-webkit-scrollbar { width: 6px; }
+::-webkit-scrollbar-track { background: var(--bg-primary); }
+::-webkit-scrollbar-thumb { background: var(--accent-1); border-radius: 99px; }
+
+a { color: var(--accent-3); text-decoration: none; }
+a:hover { color: var(--accent-1); }
+
+/* ===== AUTH PAGES ===== */
+.auth-page {
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: #000;
+ position: relative;
+ padding: 24px;
+}
+
+.auth-page::before {
+ content: '';
+ position: fixed;
+ inset: 0;
+ pointer-events: none;
+ z-index: 0;
+ background-image:
+ linear-gradient(rgba(238, 129, 50, 0.05) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(238, 129, 50, 0.05) 1px, transparent 1px);
+ background-size: 24px 24px;
+ mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, black 30%, transparent 80%);
+ -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, black 30%, transparent 80%);
+}
+
+.auth-page::after {
+ content: '';
+ position: fixed;
+ inset: 0;
+ z-index: 0;
+ pointer-events: none;
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.08'/%3E%3C/svg%3E");
+ background-repeat: repeat;
+ background-size: 512px 512px;
+}
+
+.auth-card {
+ position: relative;
+ z-index: 1;
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-xl);
+ padding: 48px 40px;
+ width: 100%;
+ max-width: 440px;
+ box-shadow: 0 24px 80px rgba(0,0,0,0.5);
+}
+
+.auth-logo {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 8px;
+}
+
+.auth-logo img {
+ width: 36px;
+ height: 36px;
+ border-radius: var(--radius-sm);
+}
+
+.auth-logo-text {
+ font-size: 1.4rem;
+ font-weight: 800;
+ letter-spacing: -0.02em;
+}
+
+.auth-logo-accent { color: var(--accent-3); }
+
+.auth-title {
+ font-size: 1.6rem;
+ font-weight: 700;
+ margin-bottom: 6px;
+}
+
+.auth-subtitle {
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+ margin-bottom: 32px;
+}
+
+.form-group {
+ margin-bottom: 20px;
+}
+
+.form-group label {
+ display: block;
+ font-size: 0.82rem;
+ font-weight: 600;
+ color: var(--text-secondary);
+ margin-bottom: 6px;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+}
+
+.form-group input, .form-group select, .form-group textarea {
+ width: 100%;
+ padding: 12px 16px;
+ background: var(--bg-secondary);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ color: var(--text-primary);
+ font-family: 'Schibsted Grotesk', sans-serif;
+ font-size: 0.95rem;
+ transition: all var(--transition);
+ outline: none;
+}
+
+.form-group input:focus, .form-group select:focus, .form-group textarea:focus {
+ border-color: var(--accent-1);
+ box-shadow: 0 0 0 3px rgba(238, 129, 50, 0.15);
+}
+
+.form-group input::placeholder {
+ color: var(--text-muted);
+}
+
+.form-group select {
+ cursor: pointer;
+ appearance: none;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position: right 12px center;
+}
+
+/* Custom Select */
+.custom-select {
+ position: relative;
+ width: 100%;
+}
+
+.custom-select-trigger {
+ width: 100%;
+ padding: 12px 16px;
+ background: var(--bg-secondary);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ color: var(--text-primary);
+ font-family: 'Schibsted Grotesk', sans-serif;
+ font-size: 0.95rem;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ transition: all var(--transition);
+ outline: none;
+ text-align: left;
+}
+
+.custom-select-trigger:hover {
+ border-color: var(--border-hover);
+}
+
+.custom-select-trigger:focus,
+.custom-select.open .custom-select-trigger {
+ border-color: var(--accent-1);
+ box-shadow: 0 0 0 3px rgba(238, 129, 50, 0.15);
+}
+
+.custom-select-arrow {
+ flex-shrink: 0;
+ color: var(--text-secondary);
+ transition: transform var(--transition);
+}
+
+.custom-select.open .custom-select-arrow {
+ transform: rotate(180deg);
+}
+
+.custom-select-dropdown {
+ position: absolute;
+ top: calc(100% + 4px);
+ left: 0;
+ right: 0;
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ box-shadow: 0 12px 40px rgba(0,0,0,0.5);
+ z-index: 50;
+ max-height: 200px;
+ overflow-y: auto;
+ display: none;
+}
+
+.custom-select.open .custom-select-dropdown {
+ display: block;
+}
+
+.custom-select-option {
+ padding: 10px 16px 10px 20px;
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+ cursor: pointer;
+ transition: all var(--transition);
+ border-left: 2px solid transparent;
+}
+
+.custom-select-option:hover {
+ background: rgba(238, 129, 50, 0.08);
+ color: var(--text-primary);
+ border-left-color: var(--accent-1);
+}
+
+.custom-select-option:first-child {
+ border-radius: var(--radius-sm) var(--radius-sm) 0 0;
+}
+
+.custom-select-option:last-child {
+ border-radius: 0 0 var(--radius-sm) var(--radius-sm);
+}
+
+.custom-select-category {
+ padding: 14px 16px 6px;
+ font-size: 0.65rem;
+ font-weight: 800;
+ text-transform: uppercase;
+ letter-spacing: 0.12em;
+ color: var(--accent-1);
+ cursor: default;
+ border-top: 1px solid rgba(238, 129, 50, 0.12);
+ margin-top: 6px;
+}
+
+.custom-select-category:first-child {
+ border-top: none;
+ margin-top: 0;
+ padding-top: 12px;
+}
+
+.form-row {
+ display: flex;
+ gap: 12px;
+}
+
+
+
+cap-widget {
+ display: block !important;
+ width: 100% !important;
+ min-width: 100% !important;
+ --cap-background: #1c1917;
+ --cap-color: #f5f5f4;
+ --cap-border-color: rgba(238, 129, 50, 0.15);
+ --cap-checkbox-border-radius: 8px;
+ --cap-border-radius: 8px;
+ --cap-checkbox-background: transparent;
+ --cap-spinner-color: #ee8132;
+ --cap-spinner-background-color: rgba(238, 129, 50, 0.1);
+ --cap-widget-width: 100%;
+}
+
+.form-row .form-group {
+ flex: 1;
+}
+
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ font-family: 'Schibsted Grotesk', sans-serif;
+ font-size: 0.9rem;
+ font-weight: 600;
+ border-radius: var(--radius-md);
+ padding: 12px 22px;
+ border: none;
+ cursor: pointer;
+ text-decoration: none;
+ transition: all var(--transition);
+ white-space: nowrap;
+}
+
+.btn-primary {
+ background: linear-gradient(180deg, #433b32 0%, #29241f 100%);
+ color: #fff;
+ border: 1px solid rgba(255, 237, 217, 0.2);
+}
+
+.btn-primary:hover {
+ color: #fff;
+ transform: translateY(-2px);
+ border-color: rgba(238, 129, 50, 0.4);
+}
+
+.btn-primary:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ transform: none;
+}
+
+.btn-ghost {
+ background: transparent;
+ color: var(--text-secondary);
+ border: 1px solid var(--border);
+}
+
+.btn-ghost:hover {
+ color: var(--text-primary);
+ border-color: var(--border-hover);
+ background: rgba(238, 129, 50, 0.06);
+}
+
+.btn-danger {
+ background: linear-gradient(135deg, var(--accent-red), #dc2626);
+ color: #fff;
+ box-shadow: 0 4px 20px rgba(239, 68, 68, 0.3);
+}
+
+.btn-danger:hover {
+ color: #fff;
+ transform: translateY(-2px);
+ box-shadow: 0 8px 32px rgba(239, 68, 68, 0.45);
+}
+
+.btn-warning {
+ background: linear-gradient(135deg, #f59e0b, #d97706);
+ color: #fff;
+ box-shadow: 0 4px 20px rgba(245, 158, 11, 0.3);
+}
+
+.btn-warning:hover {
+ color: #fff;
+ transform: translateY(-2px);
+ box-shadow: 0 8px 32px rgba(245, 158, 11, 0.45);
+}
+
+.btn-warning:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ transform: none;
+}
+
+.btn-sm {
+ padding: 8px 16px;
+ font-size: 0.82rem;
+}
+
+.btn-full {
+ width: 100%;
+ justify-content: center;
+}
+
+.auth-footer {
+ text-align: center;
+ margin-top: 24px;
+ font-size: 0.88rem;
+ color: var(--text-secondary);
+}
+
+.auth-footer a {
+ color: var(--accent-3);
+ font-weight: 600;
+}
+
+.auth-error {
+ background: rgba(239, 68, 68, 0.1);
+ border: 1px solid rgba(239, 68, 68, 0.3);
+ color: var(--accent-red);
+ padding: 12px 16px;
+ border-radius: var(--radius-sm);
+ font-size: 0.85rem;
+ margin-bottom: 20px;
+ display: none;
+}
+
+.auth-error.show {
+ display: block;
+}
+
+/* ===== DASHBOARD LAYOUT ===== */
+.dashboard-layout {
+ display: flex;
+ min-height: 100vh;
+ position: relative;
+}
+
+.dashboard-layout::before {
+ content: '';
+ position: fixed;
+ inset: 0;
+ z-index: 0;
+ pointer-events: none;
+ background-image:
+ linear-gradient(rgba(238, 129, 50, 0.05) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(238, 129, 50, 0.05) 1px, transparent 1px);
+ background-size: 24px 24px;
+ mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, black 30%, transparent 80%);
+ -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, black 30%, transparent 80%);
+}
+
+.dashboard-layout::after {
+ content: '';
+ position: fixed;
+ inset: 0;
+ z-index: 0;
+ pointer-events: none;
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.08'/%3E%3C/svg%3E");
+ background-repeat: repeat;
+ background-size: 512px 512px;
+}
+
+.sidebar {
+ width: var(--sidebar-w);
+ background: var(--bg-sidebar);
+ border-right: 1px solid var(--border);
+ display: flex;
+ flex-direction: column;
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ z-index: 100;
+ overflow: hidden;
+ transition: transform var(--transition);
+}
+
+.sidebar.resizing {
+ transition: none;
+}
+
+.sidebar-resizer {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ width: 5px;
+ cursor: col-resize;
+ z-index: 10;
+ background: transparent;
+ transition: background 0.15s;
+}
+
+.sidebar-resizer:hover,
+.sidebar.resizing .sidebar-resizer {
+ background: var(--accent-1);
+}
+
+.sidebar.collapsed {
+ width: 60px;
+}
+
+.sidebar.collapsed .sidebar-resizer {
+ display: none;
+}
+
+.sidebar.collapsed .sidebar-logo {
+ justify-content: center;
+}
+
+.sidebar.collapsed .sidebar-logo-text {
+ display: none;
+}
+
+.sidebar.collapsed .sidebar-header {
+ padding: 16px 12px;
+ justify-content: center;
+}
+
+.sidebar.collapsed .sidebar-nav {
+ padding: 12px 8px;
+}
+
+.sidebar.collapsed .nav-section-label {
+ display: none;
+}
+
+.sidebar.collapsed .nav-item {
+ font-size: 0;
+ justify-content: center;
+ padding: 10px 0;
+ gap: 0;
+}
+
+.sidebar.collapsed .nav-indicator {
+ left: 8px;
+ width: calc(100% - 16px);
+}
+
+.sidebar.collapsed .nav-item svg {
+ font-size: 1rem;
+ width: 20px;
+ height: 20px;
+}
+
+.sidebar.collapsed .sidebar-footer {
+ border-top: none;
+ padding: 8px 0 0;
+ text-align: center;
+}
+
+.sidebar.collapsed #logout-btn {
+ display: none !important;
+}
+
+.sidebar.collapsed .user-info {
+ display: block !important;
+ padding: 0 !important;
+ text-align: center !important;
+}
+
+.sidebar.collapsed #sidebar-user-info > div:first-child {
+ gap: 0;
+ display: inline-flex !important;
+ overflow: visible !important;
+}
+
+.sidebar.collapsed #sidebar-user-info > div:first-child > div:last-child {
+ display: none;
+}
+
+.sidebar.collapsed .user-name,
+.sidebar.collapsed .user-email {
+ display: none;
+}
+
+.sidebar.collapsed .sidebar-footer > div:not(.user-info) {
+ display: none;
+}
+
+.sidebar.collapsed ~ .main-content {
+ margin-left: 60px;
+}
+
+.sidebar-tooltip {
+ position: fixed;
+ z-index: 10000;
+ background: var(--bg-card);
+ color: var(--text-primary);
+ padding: 6px 12px;
+ border-radius: var(--radius-sm);
+ font-size: 0.85rem;
+ white-space: nowrap;
+ pointer-events: none;
+ opacity: 0;
+ transform: translateY(-50%);
+ transition: opacity 0.15s ease;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.4);
+ border: 1px solid var(--border);
+}
+
+.sidebar-tooltip.visible {
+ opacity: 1;
+}
+
+.main-content {
+ position: relative;
+ z-index: 1;
+}
+
+.sidebar-header {
+ padding: 20px 20px 16px;
+ border-bottom: 1px solid var(--border);
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.sidebar-logo {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ text-decoration: none;
+}
+
+.sidebar-logo img {
+ width: 28px;
+ height: 28px;
+ border-radius: 6px;
+}
+
+.sidebar-logo-text {
+ font-size: 1.1rem;
+ font-weight: 800;
+ color: var(--text-primary);
+ letter-spacing: -0.02em;
+}
+
+.sidebar-nav {
+ flex: 1;
+ padding: 8px 12px;
+ overflow-y: auto;
+ position: relative;
+}
+
+.nav-indicator {
+ position: absolute;
+ left: 12px;
+ width: calc(100% - 24px);
+ background: rgba(238, 129, 50, 0.12);
+ border-radius: var(--radius-sm);
+ border-left: 3px solid var(--accent-1);
+ transition: top var(--transition), height var(--transition), opacity var(--transition);
+ pointer-events: none;
+ z-index: 1;
+}
+
+.nav-section-label {
+ font-size: 0.7rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ color: var(--text-muted);
+ padding: 16px 12px 6px;
+}
+
+.nav-item {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 10px 12px;
+ border-radius: var(--radius-sm);
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+ font-weight: 500;
+ cursor: pointer;
+ transition: color var(--transition);
+ text-decoration: none;
+ margin-bottom: 2px;
+ position: relative;
+ z-index: 2;
+}
+
+.nav-item:hover {
+ color: var(--text-primary);
+ background: rgba(238, 129, 50, 0.08);
+}
+
+.nav-item.active {
+ color: var(--accent-1);
+}
+
+.nav-item svg {
+ width: 18px;
+ height: 18px;
+ flex-shrink: 0;
+}
+
+.sidebar-footer {
+ padding: 12px 12px 4px;
+ border-top: 1px solid var(--border);
+}
+
+.user-info {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 8px 12px;
+}
+
+.user-info > div:first-child {
+ min-width: 0;
+ overflow: hidden;
+}
+.user-info > div:first-child > div:last-child {
+ min-width: 0;
+ overflow: hidden;
+}
+
+.user-avatar {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ background: var(--accent-1);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.8rem;
+ font-weight: 700;
+ color: #fff;
+ flex-shrink: 0;
+}
+
+.user-name {
+ font-size: 0.85rem;
+ font-weight: 600;
+ color: var(--text-primary);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.user-email {
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.main-content {
+ flex: 1;
+ margin-left: var(--sidebar-w);
+ padding: 32px;
+ min-height: 100vh;
+}
+
+/* ===== DASHBOARD PAGES ===== */
+.page {
+ display: none;
+}
+
+.page.active {
+ display: block;
+}
+
+.page-header {
+ margin-bottom: 32px;
+}
+
+.page-title {
+ font-size: 1.8rem;
+ font-weight: 800;
+ letter-spacing: -0.02em;
+ margin-bottom: 6px;
+}
+
+.page-subtitle {
+ font-size: 0.92rem;
+ color: var(--text-secondary);
+}
+
+/* ===== CARDS ===== */
+.card {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ padding: 24px;
+ transition: all var(--transition);
+}
+
+.card:hover {
+ border-color: var(--border-hover);
+}
+
+.card-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 16px;
+}
+
+.card-title {
+ font-size: 1rem;
+ font-weight: 700;
+}
+
+.stat-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+ gap: 20px;
+ margin-bottom: 32px;
+}
+
+.stat-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ padding: 24px;
+ position: relative;
+ overflow: hidden;
+}
+
+.stat-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 4px;
+ height: 100%;
+ background: linear-gradient(180deg, var(--accent-1), var(--accent-2));
+ border-radius: 0 2px 2px 0;
+}
+
+.stat-icon {
+ width: 40px;
+ height: 40px;
+ border-radius: var(--radius-sm);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 16px;
+ color: var(--accent-1);
+ background: rgba(238, 129, 50, 0.12);
+}
+
+.stat-value {
+ font-size: 2rem;
+ font-weight: 800;
+ letter-spacing: -0.03em;
+ margin-bottom: 4px;
+}
+
+.stat-label {
+ font-size: 0.82rem;
+ color: var(--text-secondary);
+ font-weight: 500;
+}
+
+.chart-container {
+ position: relative;
+ max-width: 320px;
+ margin: 0 auto;
+}
+
+.chart-container canvas {
+ width: 100% !important;
+ height: auto !important;
+}
+
+/* ===== SERVER LIST ===== */
+.server-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
+ gap: 16px;
+}
+
+.server-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ padding: 20px;
+ transition: all var(--transition);
+ cursor: pointer;
+}
+
+.server-card:hover {
+ border-color: var(--border-hover);
+ background: var(--bg-card-hover);
+ transform: translateY(-2px);
+ box-shadow: 0 8px 32px rgba(0,0,0,0.3);
+}
+
+.server-card-top {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ margin-bottom: 12px;
+}
+
+.server-card-name {
+ font-size: 1rem;
+ font-weight: 700;
+ color: var(--text-primary);
+}
+
+.server-card-status {
+ font-size: 0.7rem;
+ font-weight: 600;
+ padding: 3px 10px;
+ border-radius: 99px;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+}
+
+.power-state-dot {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ font-size: 0.7rem;
+ font-weight: 500;
+ margin-left: 6px;
+}
+.power-state-dot::before {
+ content: '';
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ flex-shrink: 0;
+}
+.power-state-dot.running::before { background: #10b981; }
+.power-state-dot.offline::before { background: #6b7280; }
+.power-state-dot.starting::before { background: #f59e0b; animation: pulse-dot 1s ease-in-out infinite; }
+.power-state-dot.stopping::before { background: #f59e0b; animation: pulse-dot 1s ease-in-out infinite; }
+
+@keyframes pulse-dot {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.4; }
+}
+
+.status-active {
+ background: rgba(16, 185, 129, 0.12);
+ color: var(--accent-green);
+}
+
+.status-suspended {
+ background: rgba(239, 68, 68, 0.12);
+ color: var(--accent-red);
+}
+
+.status-expired {
+ background: rgba(245, 158, 11, 0.12);
+ color: var(--accent-orange);
+}
+
+.status-installing {
+ background: rgba(245, 158, 11, 0.12);
+ color: var(--accent-orange);
+}
+
+.status-offline {
+ background: rgba(107, 114, 128, 0.12);
+ color: #9ca3af;
+}
+
+.server-card-details {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+}
+
+.server-detail-tag {
+ font-size: 0.75rem;
+ color: var(--text-secondary);
+ background: rgba(255,255,255,0.04);
+ border: 1px solid rgba(255,255,255,0.06);
+ padding: 4px 10px;
+ border-radius: 4px;
+ font-family: 'JetBrains Mono', monospace;
+}
+
+.server-card-actions {
+ margin-top: 12px;
+ padding-top: 12px;
+ border-top: 1px solid var(--border);
+ display: flex;
+ gap: 8px;
+}
+
+/* ===== TABLE ===== */
+.table-container {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ overflow: hidden;
+}
+
+table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+thead th {
+ text-align: left;
+ padding: 14px 20px;
+ font-size: 0.78rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--text-muted);
+ background: rgba(255,255,255,0.02);
+ border-bottom: 1px solid var(--border);
+}
+
+tbody td {
+ padding: 14px 20px;
+ font-size: 0.9rem;
+ border-bottom: 1px solid rgba(255,255,255,0.04);
+}
+
+tbody tr:last-child td {
+ border-bottom: none;
+}
+
+tbody tr:hover {
+ background: rgba(238, 129, 50, 0.04);
+}
+
+/* ===== TABS ===== */
+.tabs {
+ position: relative;
+ display: flex;
+ gap: 4px;
+ margin-bottom: 24px;
+ border-bottom: 1px solid var(--border);
+ padding-bottom: 0;
+}
+
+.tab {
+ background: none;
+ border: none;
+ color: var(--text-secondary);
+ padding: 10px 20px;
+ font-size: 0.9rem;
+ font-weight: 500;
+ cursor: pointer;
+ margin-bottom: -1px;
+ transition: color 0.15s;
+}
+
+.tab:hover {
+ color: var(--text-primary);
+}
+
+.tab.active {
+ color: var(--accent-1);
+}
+
+.tab-indicator {
+ position: absolute;
+ bottom: -1px;
+ left: 0;
+ height: 2px;
+ background: var(--accent-1);
+ transition: left 0.2s ease, width 0.2s ease;
+ pointer-events: none;
+}
+
+.tab-content {
+ animation: fadeIn 0.15s ease;
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+/* ===== ACTION CARDS ===== */
+.action-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-xl);
+ padding: 20px;
+ margin-bottom: 16px;
+}
+
+.action-card-header {
+ display: flex;
+ align-items: flex-start;
+ gap: 14px;
+ margin-bottom: 16px;
+}
+
+.action-card-header svg {
+ flex-shrink: 0;
+ margin-top: 2px;
+ color: var(--accent-1);
+}
+
+.action-card-title {
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--text-primary);
+ margin: 0 0 4px 0;
+}
+
+.action-card-desc {
+ font-size: 0.85rem;
+ color: var(--text-muted);
+ line-height: 1.5;
+ margin: 0;
+}
+
+/* ===== MODAL ===== */
+.modal-overlay {
+ position: fixed;
+ inset: 0;
+ background: rgba(0,0,0,0.7);
+ backdrop-filter: blur(4px);
+ z-index: 200;
+ display: none;
+ align-items: center;
+ justify-content: center;
+ padding: 24px;
+}
+
+.modal-overlay.open {
+ display: flex;
+}
+
+.modal {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-xl);
+ padding: 32px;
+ width: 100%;
+ max-width: 520px;
+ max-height: 90vh;
+ overflow-y: auto;
+ box-shadow: 0 32px 80px rgba(0,0,0,0.6);
+}
+
+.modal-title {
+ font-size: 1.3rem;
+ font-weight: 700;
+ margin-bottom: 24px;
+}
+
+.modal-actions {
+ display: flex;
+ gap: 12px;
+ margin-top: 28px;
+}
+
+/* ===== EMPTY STATE ===== */
+.empty-state {
+ text-align: center;
+ padding: 60px 24px;
+ background: var(--bg-card);
+ border: 1px dashed var(--border);
+ border-radius: var(--radius-lg);
+}
+
+.empty-state-icon {
+ width: 56px;
+ height: 56px;
+ border-radius: 50%;
+ background: rgba(238, 129, 50, 0.1);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin: 0 auto 16px;
+ color: var(--accent-1);
+}
+
+.empty-state-title {
+ font-size: 1.1rem;
+ font-weight: 700;
+ margin-bottom: 8px;
+}
+
+.empty-state-desc {
+ font-size: 0.88rem;
+ color: var(--text-secondary);
+ margin-bottom: 20px;
+}
+
+/* ===== SPINNER ===== */
+.spinner {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ border: 2px solid rgba(255,255,255,0.2);
+ border-top-color: #fff;
+ border-radius: 50%;
+ animation: spin 0.6s linear infinite;
+}
+
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+
+/* ===== TOAST ===== */
+.toast-container {
+ position: fixed;
+ bottom: 24px;
+ right: 24px;
+ z-index: 300;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.toast {
+ padding: 14px 20px;
+ border-radius: var(--radius-sm);
+ font-size: 0.88rem;
+ font-weight: 500;
+ animation: slideUp 0.3s ease;
+ box-shadow: 0 8px 32px rgba(0,0,0,0.4);
+}
+
+.toast-success {
+ background: rgba(16, 185, 129, 0.15);
+ border: 1px solid rgba(16, 185, 129, 0.3);
+ color: var(--accent-green);
+}
+
+.toast-error {
+ background: rgba(239, 68, 68, 0.15);
+ border: 1px solid rgba(239, 68, 68, 0.3);
+ color: var(--accent-red);
+}
+
+@keyframes slideUp {
+ from { opacity: 0; transform: translateY(20px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+/* ===== NOTIFICATION PANEL ===== */
+.notif-panel {
+ position: fixed;
+ top: 0;
+ left: var(--sidebar-w);
+ width: 380px;
+ height: 100vh;
+ background: var(--bg-card);
+ border-right: 1px solid var(--border);
+ z-index: 99;
+ transform: translateX(-100%);
+ transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.notif-panel.open {
+ transform: translateX(0);
+}
+
+.sidebar.collapsed ~ .notif-panel {
+ left: 60px;
+}
+
+.notif-backdrop {
+ position: fixed;
+ inset: 0;
+ z-index: 50;
+ background: rgba(0, 0, 0, 0.5);
+ backdrop-filter: blur(4px);
+ -webkit-backdrop-filter: blur(4px);
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.3s ease;
+}
+
+.notif-backdrop.open {
+ opacity: 1;
+ pointer-events: auto;
+}
+
+.notif-panel-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 20px 20px 16px;
+ border-bottom: 1px solid var(--border);
+ flex-shrink: 0;
+}
+
+.notif-panel-header h3 {
+ font-size: 1.1rem;
+ font-weight: 700;
+ color: var(--text-primary);
+}
+
+.notif-mark-all {
+ background: none;
+ border: none;
+ color: var(--accent-3);
+ font-size: 0.82rem;
+ font-weight: 600;
+ cursor: pointer;
+ padding: 4px 8px;
+ border-radius: var(--radius-sm);
+ transition: all var(--transition);
+}
+
+.notif-mark-all:hover {
+ background: rgba(238, 129, 50, 0.1);
+}
+
+.notif-panel-list {
+ flex: 1;
+ overflow-y: auto;
+ padding: 8px 0;
+}
+
+.notif-empty {
+ text-align: center;
+ color: var(--text-muted);
+ padding: 48px 20px;
+ font-size: 0.9rem;
+}
+
+.notif-item {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+ padding: 14px 20px;
+ cursor: pointer;
+ transition: background var(--transition);
+ position: relative;
+}
+
+.notif-item:hover {
+ background: rgba(238, 129, 50, 0.04);
+}
+
+.notif-unread {
+ background: rgba(238, 129, 50, 0.06);
+}
+
+.notif-unread:hover {
+ background: rgba(238, 129, 50, 0.1);
+}
+
+.notif-item-icon {
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.notif-icon {
+ width: 18px;
+ height: 18px;
+}
+
+.notif-item-icon.notif-success {
+ background: rgba(16, 185, 129, 0.15);
+ color: var(--accent-green);
+}
+
+.notif-item-icon.notif-error {
+ background: rgba(239, 68, 68, 0.15);
+ color: var(--accent-red);
+}
+
+.notif-item-icon.notif-warning {
+ background: rgba(245, 158, 11, 0.15);
+ color: var(--accent-orange);
+}
+
+.notif-item-icon.notif-info {
+ background: rgba(6, 182, 212, 0.15);
+ color: var(--accent-cyan);
+}
+
+.notif-item-body {
+ flex: 1;
+ min-width: 0;
+}
+
+.notif-item-title {
+ font-size: 0.88rem;
+ font-weight: 600;
+ color: var(--text-primary);
+ margin-bottom: 2px;
+}
+
+.notif-item-msg {
+ font-size: 0.82rem;
+ color: var(--text-secondary);
+ line-height: 1.4;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+
+.notif-item-time {
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ margin-top: 4px;
+}
+
+.notif-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--accent-3);
+ flex-shrink: 0;
+ margin-top: 6px;
+}
+
+.notif-view-modal {
+ position: fixed;
+ inset: 0;
+ z-index: 200;
+ background: rgba(0, 0, 0, 0.6);
+ backdrop-filter: blur(4px);
+ -webkit-backdrop-filter: blur(4px);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.2s ease;
+}
+
+.notif-view-modal.open {
+ opacity: 1;
+ pointer-events: auto;
+}
+
+.notif-view-modal-content {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ padding: 0;
+ max-width: 500px;
+ width: 90%;
+ max-height: 80vh;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
+ animation: notifModalIn 0.2s ease;
+}
+
+@keyframes notifModalIn {
+ from { transform: scale(0.95); opacity: 0; }
+ to { transform: scale(1); opacity: 1; }
+}
+
+.notif-view-modal-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 20px 24px 0;
+}
+
+.notif-view-modal-header h3 {
+ font-size: 1.05rem;
+ font-weight: 700;
+ color: var(--text-primary);
+ margin: 0;
+ flex: 1;
+}
+
+.notif-view-modal-close {
+ background: none;
+ border: none;
+ color: var(--text-muted);
+ cursor: pointer;
+ padding: 4px;
+ border-radius: var(--radius-sm);
+ transition: all var(--transition);
+ flex-shrink: 0;
+ margin-left: 12px;
+}
+
+.notif-view-modal-close:hover {
+ color: var(--text-primary);
+ background: rgba(255, 255, 255, 0.06);
+}
+
+.notif-view-modal-body {
+ padding: 16px 24px 24px;
+ overflow-y: auto;
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+ line-height: 1.6;
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
+.notif-badge {
+ position: absolute;
+ top: -6px;
+ right: -6px;
+ min-width: 18px;
+ height: 18px;
+ border-radius: 99px;
+ background: var(--accent-red);
+ color: #fff;
+ font-size: 0.65rem;
+ font-weight: 700;
+ display: none;
+ align-items: center;
+ justify-content: center;
+ padding: 0 5px;
+ line-height: 1;
+}
+
+/* ===== RESPONSIVE ===== */
+.hamburger-toggle {
+ display: none;
+ background: none;
+ border: none;
+ color: var(--text-secondary);
+ cursor: pointer;
+ padding: 8px;
+}
+
+@media (max-width: 768px) {
+ .sidebar {
+ transform: translateX(-100%);
+ }
+
+ .sidebar.open {
+ transform: translateX(0);
+ }
+
+ .sidebar-resizer {
+ display: none;
+ }
+
+ .main-content {
+ margin-left: 0;
+ padding: 20px;
+ }
+
+ .hamburger-toggle {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: fixed;
+ top: 16px;
+ left: 16px;
+ z-index: 101;
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ padding: 10px;
+ }
+
+ .stat-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .server-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .form-row {
+ flex-direction: column;
+ gap: 0;
+ }
+
+ .auth-card {
+ padding: 32px 24px;
+ }
+
+ .table-container {
+ overflow-x: auto;
+ }
+}
+
+/* ===== PYRODACTYL PAGE ===== */
+.ptero-grid {
+ max-width: 640px;
+}
+
+.ptero-card-icon {
+ width: 56px;
+ height: 56px;
+ border-radius: var(--radius-md);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 20px;
+ color: var(--accent-cyan);
+ background: rgba(34, 211, 238, 0.1);
+}
+
+.ptero-card-title {
+ font-size: 1.4rem;
+ font-weight: 700;
+ margin-bottom: 12px;
+}
+
+.ptero-card-desc {
+ color: var(--text-secondary);
+ line-height: 1.7;
+ margin-bottom: 24px;
+}
+
+.ptero-info {
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+}
+
+.ptero-info-item {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+ line-height: 1.5;
+}
+
+.ptero-info-item svg {
+ flex-shrink: 0;
+ margin-top: 2px;
+ color: var(--accent-cyan);
+}
+
+.ptero-info-item span {
+ flex: 1;
+}
+
+.ptero-info-item strong {
+ color: var(--text-primary);
+}
+
+/* ===== ACCOUNT PAGE ===== */
+.account-grid {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+ max-width: 640px;
+}
+
+.account-grid .card-title {
+ font-size: 1.1rem;
+ font-weight: 700;
+}
+
+.account-menu-card {
+ transition: background var(--transition);
+}
+
+.account-menu-card:hover {
+ background: var(--bg-secondary);
+}
+
+.account-menu-item {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+}
+
+.account-menu-icon {
+ width: 44px;
+ height: 44px;
+ border-radius: var(--radius-sm);
+ background: var(--bg-secondary);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ color: var(--accent-3);
+}
+
+.account-menu-text {
+ flex: 1;
+ min-width: 0;
+}
+
+.account-menu-title {
+ font-weight: 600;
+ font-size: 0.95rem;
+ color: var(--text-primary);
+}
+
+.account-menu-desc {
+ font-size: 0.8rem;
+ color: var(--text-muted);
+ margin-top: 2px;
+}
+
+/* ===== AVATAR UPLOAD ===== */
+.avatar-upload {
+ display: flex;
+ gap: 20px;
+ align-items: flex-start;
+ flex-wrap: wrap;
+}
+
+.avatar-upload-preview {
+ width: 100px;
+ height: 100px;
+ border-radius: 50%;
+ background: var(--accent-1);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 2.5rem;
+ font-weight: 700;
+ color: #fff;
+ flex-shrink: 0;
+ overflow: hidden;
+ position: relative;
+}
+
+.avatar-upload-preview img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ border-radius: 50%;
+}
+
+.avatar-upload-placeholder {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.avatar-upload-info {
+ flex: 1;
+ min-width: 200px;
+}
+
+/* ===== SERVER EXPIRY ===== */
+.server-card-expiry {
+ display: flex;
+ gap: 6px;
+ font-size: 0.75rem;
+ color: var(--text-secondary);
+ padding: 8px 0 4px;
+ flex-wrap: wrap;
+}
+
+.server-card-expiry.expired {
+ color: var(--accent-red);
+}
+
+.server-card-expiry.expiring {
+ color: var(--accent-orange);
+}
+
+/* ===== SERVER DETAIL PAGE ===== */
+.server-detail-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 20px;
+ align-items: start;
+}
+
+
+
+.detail-list {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.detail-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 8px;
+ border-bottom: 1px solid rgba(255,255,255,0.04);
+}
+
+.detail-item:last-child {
+ border-bottom: none;
+ padding-bottom: 0;
+}
+
+.detail-label {
+ font-size: 0.82rem;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ font-weight: 600;
+}
+
+.detail-value {
+ font-size: 0.9rem;
+ color: var(--text-primary);
+ font-weight: 500;
+}
+
+@media (max-width: 768px) {
+ .server-detail-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
+/* ===== RGPD / COOKIE CONSENT ===== */
+.cookie-banner {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: 9999;
+ background: var(--bg-card);
+ border-top: 1px solid var(--border);
+ padding: 20px 24px;
+ box-shadow: 0 -8px 40px rgba(0,0,0,0.5);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 20px;
+ flex-wrap: wrap;
+ animation: slideUpFromBelow 0.4s ease;
+}
+
+@keyframes slideUpFromBelow {
+ from { transform: translateY(100%); opacity: 0; }
+ to { transform: translateY(0); opacity: 1; }
+}
+
+.cookie-banner-text {
+ flex: 1;
+ min-width: 280px;
+}
+
+.cookie-banner-text p {
+ font-size: 0.88rem;
+ color: var(--text-secondary);
+ line-height: 1.6;
+ margin-bottom: 4px;
+}
+
+.cookie-banner-text a {
+ color: var(--accent-3);
+ text-decoration: underline;
+}
+
+.cookie-banner-actions {
+ display: flex;
+ gap: 10px;
+ flex-shrink: 0;
+ flex-wrap: wrap;
+}
+
+/* ===== RGPD CONSENT CHECKBOX ===== */
+.consent-group {
+ margin-bottom: 20px;
+ display: flex;
+ align-items: flex-start;
+ gap: 10px;
+}
+
+.custom-checkbox {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ cursor: pointer;
+ flex-shrink: 0;
+ margin-top: 2px;
+}
+
+.custom-checkbox input[type="checkbox"] {
+ position: absolute;
+ opacity: 0;
+ width: 0;
+ height: 0;
+ pointer-events: none;
+}
+
+.custom-checkbox .checkmark {
+ width: 20px;
+ height: 20px;
+ border: 2px solid var(--text-secondary);
+ border-radius: 4px;
+ background: transparent;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s ease;
+ flex-shrink: 0;
+ box-sizing: border-box;
+}
+
+.custom-checkbox input[type="checkbox"]:checked ~ .checkmark {
+ background: var(--accent-1);
+ border-color: var(--accent-1);
+}
+
+.custom-checkbox input[type="checkbox"]:checked ~ .checkmark::after {
+ content: '';
+ display: block;
+ width: 5px;
+ height: 9px;
+ border: solid #fff;
+ border-width: 0 2px 2px 0;
+ transform: rotate(45deg);
+ margin-top: -1px;
+}
+
+.custom-checkbox input[type="checkbox"]:focus-visible ~ .checkmark {
+ outline: 2px solid var(--accent-1);
+ outline-offset: 2px;
+}
+
+.consent-group label {
+ font-size: 0.85rem;
+ color: var(--text-secondary);
+ line-height: 1.5;
+ cursor: pointer;
+}
+
+.consent-group label a {
+ color: var(--accent-3);
+ text-decoration: underline;
+}
+
+.cap-modal-overlay {
+ position: fixed;
+ inset: 0;
+ z-index: 9999;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+ background: rgba(0, 0, 0, 0.5);
+ animation: capFadeIn 0.2s ease;
+}
+
+.cap-modal-overlay.cap-modal-fadeout {
+ animation: capFadeOut 0.3s ease forwards;
+}
+
+.cap-modal {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ padding: 24px;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
+ animation: capScaleIn 0.2s ease;
+}
+
+.cap-modal cap-widget {
+ display: block !important;
+ width: 300px !important;
+ min-width: unset !important;
+}
+
+.cap-modal-overlay.cap-modal-fadeout .cap-modal {
+ animation: capScaleOut 0.3s ease forwards;
+}
+
+@keyframes capFadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+@keyframes capFadeOut {
+ from { opacity: 1; }
+ to { opacity: 0; }
+}
+
+@keyframes capScaleIn {
+ from { transform: scale(0.9); opacity: 0; }
+ to { transform: scale(1); opacity: 1; }
+}
+
+@keyframes capScaleOut {
+ from { transform: scale(1); opacity: 1; }
+ to { transform: scale(0.9); opacity: 0; }
+}
+
+/* ===== ACTIVITY TIMELINE ===== */
+.activity-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+}
+
+.activity-item {
+ display: flex;
+ align-items: flex-start;
+ gap: 14px;
+ padding: 14px 0;
+ border-bottom: 1px solid rgba(255,255,255,0.04);
+ position: relative;
+}
+
+.activity-item:last-child {
+ border-bottom: none;
+}
+
+.activity-icon {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ font-size: 0.8rem;
+ position: relative;
+ z-index: 1;
+}
+
+.activity-icon-server_created { background: rgba(16,185,129,0.15); color: var(--accent-green); }
+.activity-icon-server_renewed { background: rgba(6,182,212,0.15); color: var(--accent-cyan); }
+.activity-icon-server_renamed { background: rgba(238,129,50,0.15); color: var(--accent-1); }
+.activity-icon-server_reinstalled { background: rgba(245,158,11,0.15); color: var(--accent-orange); }
+.activity-icon-server_deleted { background: rgba(239,68,68,0.15); color: var(--accent-red); }
+.activity-icon-account_registered { background: rgba(168,85,247,0.15); color: #a855f7; }
+.activity-icon-password_changed { background: rgba(59,130,246,0.15); color: #3b82f6; }
+.activity-icon-email_changed { background: rgba(59,130,246,0.15); color: #3b82f6; }
+.activity-icon-account_deleted { background: rgba(239,68,68,0.15); color: var(--accent-red); }
+.activity-icon-api_key_updated { background: rgba(236,201,75,0.15); color: #ecc94b; }
+.activity-icon-avatar_updated { background: rgba(168,85,247,0.15); color: #a855f7; }
+.activity-icon-admin_suspend { background: rgba(245,158,11,0.15); color: var(--accent-orange); }
+.activity-icon-admin_unsuspend { background: rgba(16,185,129,0.15); color: var(--accent-green); }
+.activity-icon-admin_renew_now { background: rgba(6,182,212,0.15); color: var(--accent-cyan); }
+
+.activity-content {
+ flex: 1;
+ min-width: 0;
+}
+
+.activity-action {
+ font-size: 0.88rem;
+ font-weight: 600;
+ color: var(--text-primary);
+}
+
+.activity-details {
+ font-size: 0.78rem;
+ color: var(--text-muted);
+ margin-top: 2px;
+}
+
+.activity-time {
+ font-size: 0.72rem;
+ color: var(--text-muted);
+ white-space: nowrap;
+ padding-top: 2px;
+}
+
+.activity-empty {
+ text-align: center;
+ padding: 32px 16px;
+ color: var(--text-muted);
+ font-size: 0.88rem;
+}
+
+/* ===== LOG PAGE ===== */
+.log-pagination {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ padding: 16px 0;
+}
+
+.log-pagination-info {
+ font-size: 0.82rem;
+ color: var(--text-muted);
+}
+
+.log-pagination .btn-ghost[disabled] {
+ opacity: 0.4;
+ cursor: not-allowed;
+ pointer-events: none;
+}
+
+/* ===== SEARCH & FILTERS ===== */
+.servers-toolbar {
+ display: flex;
+ gap: 12px;
+ margin-bottom: 20px;
+ flex-wrap: wrap;
+ align-items: center;
+}
+
+.search-wrapper {
+ position: relative;
+ flex: 1;
+ min-width: 200px;
+}
+
+.search-wrapper svg {
+ position: absolute;
+ left: 14px;
+ top: 50%;
+ transform: translateY(-50%);
+ color: var(--text-muted);
+ pointer-events: none;
+}
+
+.search-wrapper input {
+ width: 100%;
+ padding: 10px 14px 10px 40px;
+ background: var(--bg-secondary);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ color: var(--text-primary);
+ font-family: 'Schibsted Grotesk', sans-serif;
+ font-size: 0.9rem;
+ outline: none;
+ transition: all var(--transition);
+}
+
+.search-wrapper input:focus {
+ border-color: var(--accent-1);
+ box-shadow: 0 0 0 3px rgba(238,129,50,0.15);
+}
+
+.search-wrapper input::placeholder {
+ color: var(--text-muted);
+}
+
+.filter-group {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+}
+
+.filter-btn {
+ padding: 8px 16px;
+ background: var(--bg-secondary);
+ border: 1px solid var(--border);
+ border-radius: 99px;
+ color: var(--text-secondary);
+ font-family: 'Schibsted Grotesk', sans-serif;
+ font-size: 0.8rem;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all var(--transition);
+ white-space: nowrap;
+}
+
+.filter-btn:hover {
+ border-color: var(--border-hover);
+ color: var(--text-primary);
+}
+
+.filter-btn.active {
+ background: rgba(238,129,50,0.12);
+ border-color: var(--accent-1);
+ color: var(--accent-1);
+}
+
+
+
+/* Resource gauges for server detail */
+.resource-gauges {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 20px;
+ margin-top: 8px;
+}
+
+.resource-gauge {
+ text-align: center;
+ padding: 20px 12px;
+ background: rgba(255,255,255,0.02);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ position: relative;
+ overflow: hidden;
+}
+
+.resource-gauge-value {
+ font-size: 1.8rem;
+ font-weight: 800;
+ letter-spacing: -0.03em;
+ margin-bottom: 4px;
+}
+
+.resource-gauge-label {
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ font-weight: 600;
+}
+
+.resource-gauge-bar {
+ margin-top: 12px;
+ height: 4px;
+ background: rgba(255,255,255,0.06);
+ border-radius: 99px;
+ overflow: hidden;
+}
+
+.resource-gauge-fill {
+ height: 100%;
+ border-radius: 99px;
+ transition: width 1s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.resource-gauge-sub {
+ font-size: 0.68rem;
+ color: var(--text-muted);
+ margin-top: 6px;
+ font-family: 'JetBrains Mono', monospace;
+}
+
+/* Account API key section */
+.api-key-input-group {
+ display: flex;
+ gap: 8px;
+ align-items: stretch;
+}
+
+.api-key-input-group input {
+ flex: 1;
+}
+
+@media (max-width: 768px) {
+ .servers-toolbar {
+ flex-direction: column;
+ align-items: stretch;
+ }
+ .filter-group {
+ justify-content: center;
+ }
+ .resource-gauges {
+ grid-template-columns: 1fr;
+ }
+}
+
+/* ===== ADMIN PANEL ===== */
+.admin-layout {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+}
+
+.admin-navbar {
+ display: flex;
+ align-items: center;
+ padding: 0 24px;
+ height: 60px;
+ background: var(--bg-sidebar);
+ border-bottom: 1px solid var(--border);
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ gap: 16px;
+}
+
+.admin-navbar-left {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-shrink: 0;
+}
+
+.admin-navbar-logo {
+ width: 28px;
+ height: 28px;
+ border-radius: 6px;
+}
+
+.admin-navbar-brand {
+ font-size: 1rem;
+ font-weight: 800;
+ letter-spacing: -0.02em;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.admin-badge {
+ font-size: 0.6rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--accent-1);
+ border: 1px solid var(--accent-1);
+ padding: 2px 6px;
+ border-radius: 4px;
+}
+
+.admin-navbar-center {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ flex: 1;
+ justify-content: center;
+}
+
+.admin-nav-link {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 8px 16px;
+ border-radius: var(--radius-sm);
+ color: var(--text-secondary);
+ font-size: 0.88rem;
+ font-weight: 500;
+ text-decoration: none;
+ transition: all var(--transition);
+}
+
+.admin-nav-link:hover {
+ color: var(--text-primary);
+ background: rgba(238, 129, 50, 0.08);
+}
+
+.admin-nav-link.active {
+ color: var(--accent-1);
+ background: rgba(238, 129, 50, 0.12);
+}
+
+.admin-navbar-right {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ flex-shrink: 0;
+}
+
+.admin-navbar-user {
+ font-size: 0.85rem;
+ font-weight: 600;
+ color: var(--text-primary);
+}
+
+.admin-content {
+ flex: 1;
+ padding: 32px;
+ position: relative;
+ z-index: 1;
+}
+
+.admin-page {
+ display: none;
+}
+
+.admin-page.active {
+ display: block;
+}
+
+.admin-layout::before {
+ content: '';
+ position: fixed;
+ inset: 0;
+ z-index: 0;
+ pointer-events: none;
+ background-image:
+ linear-gradient(rgba(238, 129, 50, 0.05) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(238, 129, 50, 0.05) 1px, transparent 1px);
+ background-size: 24px 24px;
+ mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, black 30%, transparent 80%);
+ -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, black 30%, transparent 80%);
+}
+
+.admin-notice {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 12px 16px;
+ margin-bottom: 24px;
+ background: rgba(238, 129, 50, 0.08);
+ border: 1px solid rgba(238, 129, 50, 0.2);
+ border-radius: var(--radius-sm);
+ font-size: 0.85rem;
+ color: var(--text-secondary);
+ line-height: 1.5;
+}
+
+.admin-notice svg {
+ flex-shrink: 0;
+ color: var(--accent-1);
+ opacity: 0.8;
+}
+
+.admin-content > * {
+ position: relative;
+ z-index: 1;
+}
+
+@media (max-width: 768px) {
+ .admin-navbar {
+ padding: 0 12px;
+ gap: 8px;
+ }
+ .admin-navbar-brand {
+ font-size: 0.85rem;
+ }
+ .admin-navbar-user {
+ display: none;
+ }
+ .admin-content {
+ padding: 20px;
+ }
+}
+
+/* โโโ Create Wizard โโโ */
+.wizard-progress {
+ display: flex;
+ gap: 4px;
+ max-width: 600px;
+ background: var(--bg-secondary);
+ border-radius: var(--radius-lg);
+ padding: 8px;
+}
+
+.wizard-step-indicator {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 1;
+ padding: 8px 12px;
+ border-radius: var(--radius-md);
+ background: transparent;
+ transition: var(--transition);
+ opacity: 0.5;
+}
+
+.wizard-step-indicator.active {
+ background: var(--accent-1);
+ opacity: 1;
+}
+
+.wizard-step-indicator.active .wizard-step-circle {
+ background: rgba(255,255,255,0.2);
+}
+
+.wizard-step-indicator.completed {
+ opacity: 0.8;
+}
+
+.wizard-step-indicator.completed .wizard-step-circle {
+ background: var(--accent-green);
+}
+
+.wizard-step-circle {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--bg-tertiary);
+ flex-shrink: 0;
+}
+
+.wizard-step-label {
+ font-size: 0.82rem;
+ font-weight: 600;
+ white-space: nowrap;
+}
+
+.wizard-content {
+ width: 100%;
+}
+
+.wizard-content-wrapper {
+ overflow: visible;
+ position: relative;
+}
+
+.wizard-content.slide-left {
+ animation: slideLeft 0.3s ease;
+}
+
+.wizard-content.slide-right {
+ animation: slideRight 0.3s ease;
+}
+
+@keyframes slideLeft {
+ from { transform: translateX(20px); opacity: 0; }
+ to { transform: translateX(0); opacity: 1; }
+}
+
+@keyframes slideRight {
+ from { transform: translateX(-20px); opacity: 0; }
+ to { transform: translateX(0); opacity: 1; }
+}
+
+.wizard-progress-track {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ background: var(--accent-1);
+ border-radius: calc(var(--radius-lg) - 4px);
+ transition: transform 0.3s var(--transition);
+}
+.wizard-step-title {
+ font-size: 1.25rem;
+ font-weight: 700;
+ margin-bottom: 4px;
+ color: var(--text-primary);
+}
+
+.wizard-step-desc {
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+ margin: 0 0 24px;
+}
+
+.wizard-actions {
+ display: flex;
+ gap: 12px;
+ margin-top: 28px;
+}
+
+#wizard-next-btn {
+ margin-left: auto;
+}
+
+.wizard-subsection {
+ border-top: 1px solid var(--border);
+ padding-top: 20px;
+}
+
+/* Nest Cards Grid */
+.nest-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
+ gap: 16px;
+}
+
+.nest-card {
+ background: var(--bg-secondary);
+ border: 2px solid var(--border);
+ border-radius: var(--radius-lg);
+ padding: 24px 20px;
+ cursor: pointer;
+ transition: var(--transition);
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
+}
+
+.nest-card:hover {
+ border-color: var(--accent-1);
+ background: var(--bg-tertiary);
+ transform: translateY(-2px);
+}
+
+.nest-card.selected {
+ border-color: var(--accent-1);
+ background: color-mix(in srgb, var(--accent-1) 10%, var(--bg-secondary));
+ box-shadow: 0 0 0 1px var(--accent-1);
+}
+
+.nest-card-logo {
+ width: 64px;
+ height: 64px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.nest-card-logo img {
+ max-width: 100%;
+ max-height: 100%;
+ object-fit: contain;
+ filter: grayscale(100%);
+}
+
+.nest-card-name {
+ font-weight: 700;
+ font-size: 1rem;
+ color: var(--text-primary);
+}
+
+.nest-card-desc {
+ font-size: 0.82rem;
+ color: var(--text-secondary);
+ line-height: 1.5;
+}
+
+/* Egg Cards Grid */
+.egg-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+ gap: 12px;
+}
+
+.egg-card {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ background: var(--bg-secondary);
+ border: 2px solid var(--border);
+ border-radius: var(--radius-md);
+ padding: 14px 16px;
+ cursor: pointer;
+ transition: var(--transition);
+}
+
+.egg-card:hover {
+ border-color: var(--accent-1);
+ background: var(--bg-tertiary);
+}
+
+.egg-card.selected {
+ border-color: var(--accent-1);
+ background: color-mix(in srgb, var(--accent-1) 10%, var(--bg-secondary));
+ box-shadow: 0 0 0 1px var(--accent-1);
+}
+
+.egg-card-logo {
+ width: 40px;
+ height: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.egg-card-logo img {
+ max-width: 100%;
+ max-height: 100%;
+ object-fit: contain;
+ filter: grayscale(100%);
+}
+
+.egg-card-info {
+ min-width: 0;
+}
+
+.egg-card-name {
+ font-weight: 600;
+ font-size: 0.92rem;
+ color: var(--text-primary);
+}
+
+.egg-card-desc {
+ font-size: 0.8rem;
+ color: var(--text-secondary);
+ line-height: 1.4;
+ margin-top: 2px;
+}
+
+/* Docker Image Cards */
+/* Summary Card */
+.summary-card {
+ background: var(--bg-secondary);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ padding: 20px 24px;
+ max-width: 480px;
+}
+
+.summary-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 0;
+ border-bottom: 1px solid var(--border);
+}
+
+.summary-row:last-child {
+ border-bottom: none;
+}
+
+.summary-label {
+ color: var(--text-secondary);
+ font-size: 0.85rem;
+ font-weight: 500;
+}
+
+.summary-value {
+ color: var(--text-primary);
+ font-weight: 600;
+ font-size: 0.92rem;
+ text-align: right;
+ max-width: 60%;
+ word-break: break-word;
+}
+
+.date-tooltip {
+ cursor: help;
+}
+#admin-date-tooltip {
+ position: fixed;
+ z-index: 10000;
+ background: var(--bg-card);
+ color: var(--text-primary);
+ padding: 6px 12px;
+ border-radius: var(--radius-sm);
+ font-size: 0.85rem;
+ white-space: nowrap;
+ pointer-events: none;
+ opacity: 0;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.4);
+ border: 1px solid var(--border);
+ transition: opacity 0.15s ease;
+}
+#admin-date-tooltip.visible {
+ opacity: 1;
+}
+
+
diff --git a/public/index.html b/public/index.html
index 495d3f5..f208569 100644
--- a/public/index.html
+++ b/public/index.html
@@ -4,7 +4,7 @@
ZeroHost Dashboard
-
+
diff --git a/public/js/admin.js b/public/js/admin.js
index 71f829a..41a54bb 100644
--- a/public/js/admin.js
+++ b/public/js/admin.js
@@ -1,5 +1,11 @@
function initIcons() { if (window.lucide) lucide.createIcons(); }
+function escapeHtml(str) {
+ if (typeof str !== 'string') return '';
+ const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
+ return str.replace(/[&<>"']/g, m => map[m]);
+}
+
const ADMIN_STORAGE_KEY = 'zh_admin_token';
const adminState = {
@@ -115,7 +121,7 @@ function renderAdminLogin() {
-

+
ZeroHost Admin
Admin Panel
@@ -192,7 +198,7 @@ function renderAdminLayout() {
@@ -1382,7 +1519,7 @@ async function renderAdminNestEggs(nestId) {
if (!tbody) return;
if (data.eggs.length === 0) {
- tbody.innerHTML = '
| No eggs found in this nest. |
';
+ tbody.innerHTML = '
| No eggs found in this nest. |
';
return;
}
@@ -1394,6 +1531,7 @@ async function renderAdminNestEggs(nestId) {
return ahtml`
| ${e.id} |
+ ${res?.logo ? ` ` : 'โ'} |
${e.name} |
${e.description || 'โ'} |
${resStr} |
@@ -1405,7 +1543,7 @@ async function renderAdminNestEggs(nestId) {
}).join('');
} catch (err) {
const tbody = $a('#admin-eggs-tbody');
- if (tbody) tbody.innerHTML = `
| Error: ${err.message} |
`;
+ if (tbody) tbody.innerHTML = `
| Error: ${err.message} |
`;
}
initIcons();
}
@@ -1425,12 +1563,19 @@ async function renderAdminEggSettings(nestId, eggId) {
Egg Resources
Loading...
-
+