diff --git a/site-web/console/cockpit.js b/site-web/console/cockpit.js new file mode 100644 index 0000000..1645956 --- /dev/null +++ b/site-web/console/cockpit.js @@ -0,0 +1,297 @@ +// FORGIUM CLOUD COCKPIT CONTROLLER +const FORGIUM_API_BASE = 'https://panel.forgium.fr/api/client'; +const DEFAULT_KEY = 'ptlc_8mAbydxokymforgium_144010543bd11119f4fe424b643e49d6d2e17c8271224b81'; + +class ForgiumCockpit { + constructor() { + this.apiKey = localStorage.getItem('forgium_cockpit_key') || DEFAULT_KEY; + this.servers = []; + this.currentServer = null; + this.socket = null; + this.pollInterval = null; + + this.initDOMElements(); + this.bindEvents(); + this.checkAuthAndStart(); + } + + initDOMElements() { + this.fleetView = document.getElementById('fleet-view'); + this.serverView = document.getElementById('server-view'); + this.serversGrid = document.getElementById('servers-grid'); + this.configModal = document.getElementById('config-modal'); + this.apiKeyInput = document.getElementById('api-key-input'); + + // Detail View Elements + this.backBtn = document.getElementById('back-to-fleet'); + this.serverTitle = document.getElementById('server-detail-title'); + this.serverDesc = document.getElementById('server-detail-desc'); + this.serverBadge = document.getElementById('server-detail-badge'); + this.terminalOutput = document.getElementById('terminal-output'); + this.terminalInput = document.getElementById('terminal-input'); + + // Stat counters + this.countTotal = document.getElementById('stat-total-servers'); + this.countRunning = document.getElementById('stat-running-servers'); + + // Detail Gauges + this.gaugeCpu = document.getElementById('gauge-cpu'); + this.gaugeCpuFill = document.getElementById('gauge-cpu-fill'); + this.gaugeRam = document.getElementById('gauge-ram'); + this.gaugeRamFill = document.getElementById('gauge-ram-fill'); + this.gaugeDisk = document.getElementById('gauge-disk'); + this.gaugeDiskFill = document.getElementById('gauge-disk-fill'); + + // Power Buttons + this.btnStart = document.getElementById('btn-power-start'); + this.btnRestart = document.getElementById('btn-power-restart'); + this.btnStop = document.getElementById('btn-power-stop'); + } + + bindEvents() { + document.getElementById('save-key-btn').addEventListener('click', () => { + const key = this.apiKeyInput.value.trim(); + if (key) { + this.apiKey = key; + localStorage.setItem('forgium_cockpit_key', key); + this.configModal.classList.add('hidden'); + this.loadFleet(); + } + }); + + document.getElementById('btn-settings').addEventListener('click', () => { + this.apiKeyInput.value = this.apiKey; + this.configModal.classList.remove('hidden'); + }); + + this.backBtn.addEventListener('click', () => { + this.showFleetView(); + }); + + this.terminalInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + const cmd = this.terminalInput.value.trim(); + if (cmd) { + this.sendCommand(cmd); + this.terminalInput.value = ''; + } + } + }); + + this.btnStart.addEventListener('click', () => this.sendPowerSignal('start')); + this.btnRestart.addEventListener('click', () => this.sendPowerSignal('restart')); + this.btnStop.addEventListener('click', () => this.sendPowerSignal('stop')); + } + + checkAuthAndStart() { + if (!this.apiKey) { + this.configModal.classList.remove('hidden'); + } else { + this.loadFleet(); + } + } + + async apiRequest(endpoint, method = 'GET', body = null) { + const headers = { + 'Authorization': `Bearer ${this.apiKey}`, + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }; + + const res = await fetch(`${FORGIUM_API_BASE}${endpoint}`, { + method, + headers, + body: body ? JSON.stringify(body) : null + }); + + if (!res.ok) { + if (res.status === 401 || res.status === 403) { + this.configModal.classList.remove('hidden'); + } + throw new Error(`Erreur API: ${res.status}`); + } + + return res.json(); + } + + async loadFleet() { + try { + const data = await this.apiRequest(''); + this.servers = data.data.map(item => item.attributes); + this.renderFleet(); + } catch (err) { + console.error('Erreur chargement flotte:', err); + } + } + + async renderFleet() { + this.serversGrid.innerHTML = ''; + this.countTotal.textContent = this.servers.length; + let onlineCount = 0; + + for (const s of this.servers) { + const card = document.createElement('div'); + card.className = 'server-card'; + + // Récupérer ressources en temps réel + let resState = 'offline'; + let cpu = '0.0%'; + let ram = '0 Mo'; + try { + const stats = await this.apiRequest(`/servers/${s.identifier}/resources`); + resState = stats.attributes.current_state; + if (resState === 'running' || resState === 'starting') { + onlineCount++; + } + cpu = (stats.attributes.resources.cpu_absolute || 0).toFixed(1) + '%'; + const ramMb = (stats.attributes.resources.memory_bytes / 1024 / 1024).toFixed(0); + ram = `${ramMb} Mo / ${s.limits.memory} Mo`; + } catch (e) { + console.log('Stats offline:', s.name); + } + + const isOnline = resState === 'running' || resState === 'starting'; + const badgeClass = isOnline ? 'status-running' : 'status-offline'; + const badgeText = isOnline ? 'EN LIGNE' : 'ARRÊTÉ'; + + const defaultAlloc = s.relationships?.allocations?.data[0]?.attributes; + const alias = defaultAlloc ? `node.forgium.fr:${defaultAlloc.port}` : 'Réseau Dédié'; + + card.innerHTML = ` +
+
+

${s.name}

+ AMD RYZEN 7 3700X • ${s.docker_image.split('/').pop().replace('yolks:', '')} +
+ + + ${badgeText} + +
+
+
+ CPU + ${cpu} +
+
+ RAM + ${ram} +
+
+ DISQUE + ${s.limits.disk} Mo +
+
+
+ ${alias} + +
+ `; + + card.addEventListener('click', () => this.openServerCockpit(s)); + this.serversGrid.appendChild(card); + } + + this.countRunning.textContent = onlineCount; + } + + openServerCockpit(server) { + this.currentServer = server; + this.fleetView.style.display = 'none'; + this.serverView.classList.add('active'); + + this.serverTitle.textContent = server.name; + this.serverDesc.textContent = server.description || `Instance Cloud haute performance sur AMD Ryzen 7 3700X`; + this.terminalOutput.textContent = `[Forgium Console Gateway] Connexion au nœud Wings sécurisé...\n[Forgium Console Gateway] Authentification TLS / WebSocket pour le serveur [${server.name}] (UUID: ${server.identifier})\n`; + + this.startTelemetryPoll(); + } + + showFleetView() { + this.stopTelemetryPoll(); + this.serverView.classList.remove('active'); + this.fleetView.style.display = 'block'; + this.currentServer = null; + this.loadFleet(); + } + + startTelemetryPoll() { + this.updateTelemetry(); + this.pollInterval = setInterval(() => this.updateTelemetry(), 3000); + } + + stopTelemetryPoll() { + if (this.pollInterval) { + clearInterval(this.pollInterval); + this.pollInterval = null; + } + } + + async updateTelemetry() { + if (!this.currentServer) return; + try { + const stats = await this.apiRequest(`/servers/${this.currentServer.identifier}/resources`); + const res = stats.attributes.resources; + const state = stats.attributes.current_state; + + const isOnline = state === 'running' || state === 'starting'; + this.serverBadge.className = `status-badge ${isOnline ? 'status-running' : 'status-offline'}`; + this.serverBadge.innerHTML = ` ${isOnline ? 'EN LIGNE' : 'ARRÊTÉ'}`; + + // CPU + const cpuPercent = (res.cpu_absolute || 0).toFixed(1); + this.gaugeCpu.textContent = `${cpuPercent}% / ${this.currentServer.limits.cpu}%`; + this.gaugeCpuFill.style.width = `${Math.min(100, (res.cpu_absolute / this.currentServer.limits.cpu) * 100)}%`; + + // RAM + const ramMb = (res.memory_bytes / 1024 / 1024).toFixed(1); + const ramMax = this.currentServer.limits.memory; + this.gaugeRam.textContent = `${ramMb} Mo / ${ramMax} Mo`; + this.gaugeRamFill.style.width = `${Math.min(100, (ramMb / ramMax) * 100)}%`; + + // DISK + const diskMb = (res.disk_bytes / 1024 / 1024).toFixed(1); + const diskMax = this.currentServer.limits.disk; + this.gaugeDisk.textContent = `${diskMb} Mo / ${diskMax} Mo`; + this.gaugeDiskFill.style.width = `${Math.min(100, (diskMb / diskMax) * 100)}%`; + + // Log stream imitation for testing + if (isOnline && Math.random() > 0.4) { + this.appendTerminal(`[Node ${this.currentServer.node}] Heartbeat sync: CPU=${cpuPercent}% | RAM=${ramMb}Mo | Uptime=${(res.uptime / 60).toFixed(0)}m`); + } + } catch (e) { + console.error('Erreur télémétrie serveur:', e); + } + } + + appendTerminal(text) { + this.terminalOutput.textContent += `${text}\n`; + this.terminalOutput.scrollTop = this.terminalOutput.scrollHeight; + } + + async sendPowerSignal(signal) { + if (!this.currentServer) return; + this.appendTerminal(`[Forgium Cockpit] Envoi du signal d'alimentation: ${signal.toUpperCase()}...`); + try { + await this.apiRequest(`/servers/${this.currentServer.identifier}/power`, 'POST', { signal }); + this.appendTerminal(`[Forgium Cockpit] Ordre ${signal.toUpperCase()} exécuté avec succès par Wings.`); + setTimeout(() => this.updateTelemetry(), 1000); + } catch (e) { + this.appendTerminal(`[Forgium Cockpit] ERREUR signal ${signal}: ${e.message}`); + } + } + + async sendCommand(command) { + if (!this.currentServer) return; + this.appendTerminal(`> ${command}`); + try { + await this.apiRequest(`/servers/${this.currentServer.identifier}/command`, 'POST', { command }); + } catch (e) { + this.appendTerminal(`[Forgium Cockpit] Erreur commande: ${e.message}`); + } + } +} + +document.addEventListener('DOMContentLoaded', () => { + window.cockpit = new ForgiumCockpit(); +}); diff --git a/site-web/console/index.html b/site-web/console/index.html new file mode 100644 index 0000000..a1efc42 --- /dev/null +++ b/site-web/console/index.html @@ -0,0 +1,160 @@ + + + + + + + Cockpit & Console | Forgium Cloud + + + + + + + + + + + +
+
+
+ + Forgium Logo + FORGIUM + COCKPIT + +
+ + WINGS CLUSTER ONLINE +
+
+ +
+
+
F
+ Mathias Z. (Admin) +
+ +
+
+
+ + +
+ + +
+
+
+

Centre de Contrôle Matériel

+

Supervision et pilotage direct sur architecture dédiée AMD Ryzen 7 3700X.

+
+
+
+
Serveurs Déployés
+
--
+
+
+
En Fonctionnement
+
--
+
+
+
+ + +
+ +
+
+ + +
+
+ +
+

Chargement...

+

+
+
+ + + +
+
+ +
+ +
+
+
+ TERMINAL SHELL + -- +
+
+ + + +
+
+
+
+ > + +
+
+ + +
+
+

+ Charge Processeur + -- +

+
+
+
+

AMD Ryzen 7 3700X High Performance Core

+
+ +
+

+ Mémoire Vive (RAM) + -- +

+
+
+
+

Allocation stricte isolée sans dépassement

+
+ +
+

+ Espace Disque SSD + -- +

+
+
+
+

NVMe Local haute vitesse

+
+
+
+
+ +
+ + + + + + + + diff --git a/site-web/console/style-console.css b/site-web/console/style-console.css new file mode 100644 index 0000000..3d2129e --- /dev/null +++ b/site-web/console/style-console.css @@ -0,0 +1,633 @@ +:root { + --bg-deep: #030712; + --bg-surface: #0a101f; + --bg-card: rgba(13, 22, 41, 0.75); + --bg-card-hover: rgba(18, 30, 56, 0.85); + --border: rgba(56, 189, 248, 0.15); + --border-hover: rgba(56, 189, 248, 0.4); + --accent-cyan: #38bdf8; + --accent-blue: #2563eb; + --accent-green: #10b981; + --accent-amber: #f59e0b; + --accent-rose: #f43f5e; + --text-main: #f8fafc; + --text-muted: #94a3b8; + --text-dim: #64748b; + --glow-cyan: 0 0 25px rgba(56, 189, 248, 0.25); + --glow-green: 0 0 20px rgba(16, 185, 129, 0.3); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, sans-serif; + background-color: var(--bg-deep); + background-image: + radial-gradient(circle at 15% 15%, rgba(56, 189, 248, 0.08) 0%, transparent 45%), + radial-gradient(circle at 85% 85%, rgba(37, 99, 235, 0.08) 0%, transparent 45%); + color: var(--text-main); + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* NAVBAR COCKPIT */ +.cockpit-header { + background: rgba(10, 16, 31, 0.8); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border-bottom: 1px solid var(--border); + position: sticky; + top: 0; + z-index: 100; +} + +.header-container { + max-width: 1400px; + margin: 0 auto; + padding: 0.8rem 1.5rem; + display: flex; + align-items: center; + justify-content: space-between; +} + +.brand-area { + display: flex; + align-items: center; + gap: 1.2rem; +} + +.brand-link { + display: flex; + align-items: center; + gap: 0.6rem; + text-decoration: none; + color: var(--text-main); + font-weight: 800; + font-size: 1.2rem; + letter-spacing: -0.5px; +} + +.brand-badge { + background: rgba(56, 189, 248, 0.12); + border: 1px solid var(--border); + color: var(--accent-cyan); + font-family: 'JetBrains Mono', monospace; + font-size: 0.7rem; + padding: 0.2rem 0.6rem; + border-radius: 6px; + font-weight: 700; +} + +.node-status { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.8rem; + color: var(--text-muted); + font-family: 'JetBrains Mono', monospace; +} + +.pulse-indicator { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--accent-green); + box-shadow: var(--glow-green); + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { transform: scale(0.95); opacity: 0.8; } + 50% { transform: scale(1.2); opacity: 1; } + 100% { transform: scale(0.95); opacity: 0.8; } +} + +.user-area { + display: flex; + align-items: center; + gap: 1rem; +} + +.user-chip { + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.08); + padding: 0.4rem 0.9rem; + border-radius: 100px; + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.85rem; + font-weight: 600; +} + +.user-avatar { + width: 24px; + height: 24px; + border-radius: 50%; + background: linear-gradient(135deg, var(--accent-cyan), var(--accent-blue)); + display: flex; + align-items: center; + justify-content: center; + font-size: 0.7rem; + font-weight: 800; +} + +/* LAYOUT PRINCIPAL */ +.cockpit-main { + max-width: 1400px; + margin: 0 auto; + padding: 2rem 1.5rem; + flex: 1; + width: 100%; +} + +/* VUE LISTE SERVEURS (FLEET OVERVIEW) */ +.fleet-header { + display: flex; + justify-content: space-between; + align-items: flex-end; + margin-bottom: 2rem; +} + +.fleet-title h1 { + font-size: 1.8rem; + font-weight: 800; + letter-spacing: -0.5px; + margin-bottom: 0.3rem; +} + +.fleet-title p { + color: var(--text-muted); + font-size: 0.95rem; +} + +.fleet-stats { + display: flex; + gap: 1.5rem; +} + +.stat-pill { + background: var(--bg-card); + border: 1px solid var(--border); + padding: 0.6rem 1.2rem; + border-radius: 12px; + backdrop-filter: blur(8px); +} + +.stat-label { + font-size: 0.75rem; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.stat-value { + font-size: 1.2rem; + font-weight: 700; + font-family: 'JetBrains Mono', monospace; + color: var(--accent-cyan); +} + +/* GRILLE DE CARTES SERVEURS */ +.servers-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)); + gap: 1.5rem; +} + +.server-card { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 18px; + padding: 1.6rem; + backdrop-filter: blur(14px); + transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); + cursor: pointer; + position: relative; + overflow: hidden; +} + +.server-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient(90deg, var(--accent-cyan), var(--accent-blue)); + opacity: 0; + transition: opacity 0.3s; +} + +.server-card:hover { + border-color: var(--border-hover); + transform: translateY(-4px); + box-shadow: var(--glow-cyan); +} + +.server-card:hover::before { + opacity: 1; +} + +.card-top { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 1.2rem; +} + +.server-info h3 { + font-size: 1.2rem; + font-weight: 700; + margin-bottom: 0.3rem; +} + +.server-type { + display: inline-flex; + align-items: center; + gap: 0.4rem; + font-size: 0.75rem; + color: var(--text-muted); + font-family: 'JetBrains Mono', monospace; +} + +.status-badge { + padding: 0.3rem 0.7rem; + border-radius: 100px; + font-size: 0.75rem; + font-weight: 700; + display: flex; + align-items: center; + gap: 0.4rem; +} + +.status-running { + background: rgba(16, 185, 129, 0.15); + border: 1px solid rgba(16, 185, 129, 0.3); + color: var(--accent-green); +} + +.status-offline { + background: rgba(100, 116, 139, 0.15); + border: 1px solid rgba(100, 116, 139, 0.3); + color: var(--text-dim); +} + +.metrics-row { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0.8rem; + background: rgba(0, 0, 0, 0.3); + padding: 0.9rem; + border-radius: 12px; + border: 1px solid rgba(255, 255, 255, 0.04); + margin-bottom: 1.2rem; +} + +.metric-item { + display: flex; + flex-direction: column; +} + +.metric-name { + font-size: 0.7rem; + color: var(--text-muted); + margin-bottom: 0.2rem; +} + +.metric-val { + font-size: 0.95rem; + font-weight: 700; + font-family: 'JetBrains Mono', monospace; + color: var(--text-main); +} + +.card-actions { + display: flex; + justify-content: space-between; + align-items: center; +} + +.conn-alias { + font-size: 0.8rem; + font-family: 'JetBrains Mono', monospace; + color: var(--accent-cyan); + background: rgba(56, 189, 248, 0.08); + padding: 0.2rem 0.5rem; + border-radius: 6px; +} + +.btn-manage { + background: linear-gradient(135deg, rgba(56, 189, 248, 0.2), rgba(37, 99, 235, 0.2)); + border: 1px solid var(--border); + color: var(--text-main); + padding: 0.4rem 0.9rem; + border-radius: 8px; + font-size: 0.85rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; +} + +.btn-manage:hover { + background: var(--accent-cyan); + color: #000; + box-shadow: var(--glow-cyan); +} + +/* VUE DÉTAIL SERVEUR (COCKPIT VIEW) */ +.server-view { + display: none; +} + +.server-view.active { + display: block; +} + +.server-navbar { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; + flex-wrap: wrap; + gap: 1rem; +} + +.back-btn { + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--border); + color: var(--text-main); + padding: 0.5rem 1rem; + border-radius: 8px; + font-size: 0.85rem; + cursor: pointer; + font-weight: 600; + display: flex; + align-items: center; + gap: 0.4rem; +} + +.back-btn:hover { + border-color: var(--accent-cyan); + color: var(--accent-cyan); +} + +.power-controls { + display: flex; + gap: 0.6rem; +} + +.btn-power { + padding: 0.5rem 1rem; + border-radius: 8px; + font-size: 0.85rem; + font-weight: 700; + cursor: pointer; + border: none; + display: flex; + align-items: center; + gap: 0.4rem; + transition: all 0.2s; +} + +.btn-start { + background: var(--accent-green); + color: #000; +} +.btn-start:hover { + box-shadow: var(--glow-green); + filter: brightness(1.1); +} + +.btn-restart { + background: var(--accent-amber); + color: #000; +} + +.btn-stop { + background: var(--accent-rose); + color: #fff; +} + +/* COCKPIT GRID : CONSOLE + FILE/METRICS TABS */ +.cockpit-grid { + display: grid; + grid-template-columns: 2fr 1fr; + gap: 1.5rem; +} + +/* CONSOLE CYBERPUNK */ +.terminal-window { + background: #02040a; + border: 1px solid rgba(56, 189, 248, 0.2); + border-radius: 16px; + overflow: hidden; + display: flex; + flex-direction: column; + height: 600px; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6); +} + +.terminal-header { + background: rgba(13, 22, 41, 0.95); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); + padding: 0.8rem 1.2rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.terminal-title { + font-family: 'JetBrains Mono', monospace; + font-size: 0.85rem; + color: var(--accent-cyan); + display: flex; + align-items: center; + gap: 0.6rem; +} + +.terminal-dots { + display: flex; + gap: 6px; +} + +.terminal-dots span { + width: 10px; + height: 10px; + border-radius: 50%; +} +.dot-red { background: #ef4444; } +.dot-yellow { background: #f59e0b; } +.dot-green { background: #10b981; } + +.terminal-output { + flex: 1; + padding: 1.2rem; + overflow-y: auto; + font-family: 'JetBrains Mono', monospace; + font-size: 0.85rem; + line-height: 1.6; + color: #cbd5e1; + white-space: pre-wrap; + word-break: break-word; +} + +.terminal-input-bar { + background: rgba(10, 16, 31, 0.9); + border-top: 1px solid rgba(255, 255, 255, 0.06); + padding: 0.8rem 1.2rem; + display: flex; + align-items: center; + gap: 0.8rem; +} + +.terminal-prompt { + color: var(--accent-cyan); + font-family: 'JetBrains Mono', monospace; + font-weight: 700; +} + +.terminal-input { + flex: 1; + background: transparent; + border: none; + color: var(--text-main); + font-family: 'JetBrains Mono', monospace; + font-size: 0.9rem; + outline: none; +} + +/* SIDE PANEL : METRIQUES EN DIRECT */ +.side-panel { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.gauge-box { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 16px; + padding: 1.5rem; + backdrop-filter: blur(14px); +} + +.gauge-box h4 { + font-size: 0.95rem; + font-weight: 700; + margin-bottom: 1rem; + display: flex; + justify-content: space-between; +} + +.progress-bar-bg { + background: rgba(255, 255, 255, 0.05); + height: 10px; + border-radius: 100px; + overflow: hidden; + margin-bottom: 0.5rem; +} + +.progress-bar-fill { + height: 100%; + border-radius: 100px; + background: linear-gradient(90deg, var(--accent-cyan), var(--accent-blue)); + transition: width 0.5s cubic-bezier(0.16, 1, 0.3, 1); +} + +/* MODAL CONFIG / TOKEN */ +.config-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.85); + backdrop-filter: blur(12px); + display: flex; + align-items: center; + justify-content: center; + z-index: 999; +} + +.config-modal.hidden { + display: none; +} + +.modal-content { + background: var(--bg-surface); + border: 1px solid var(--border); + border-radius: 20px; + padding: 2.5rem; + max-width: 480px; + width: 90%; + box-shadow: var(--glow-cyan); +} + +.modal-content h2 { + font-size: 1.5rem; + font-weight: 800; + margin-bottom: 0.8rem; +} + +.modal-content p { + color: var(--text-muted); + font-size: 0.9rem; + margin-bottom: 1.5rem; + line-height: 1.5; +} + +.input-field { + width: 100%; + background: rgba(0, 0, 0, 0.5); + border: 1px solid var(--border); + padding: 0.8rem 1rem; + border-radius: 10px; + color: var(--text-main); + font-family: 'JetBrains Mono', monospace; + font-size: 0.85rem; + margin-bottom: 1.2rem; + outline: none; +} + +.input-field:focus { + border-color: var(--accent-cyan); + box-shadow: 0 0 10px rgba(56, 189, 248, 0.2); +} + +.modal-btn { + width: 100%; + background: linear-gradient(135deg, var(--accent-cyan), var(--accent-blue)); + border: none; + color: #000; + font-weight: 800; + padding: 0.9rem; + border-radius: 10px; + cursor: pointer; + font-size: 1rem; + transition: all 0.2s; +} + +.modal-btn:hover { + filter: brightness(1.1); + box-shadow: var(--glow-cyan); +} + +@media (max-width: 900px) { + .cockpit-grid { + grid-template-columns: 1fr; + } + .fleet-header { + flex-direction: column; + align-items: flex-start; + gap: 1rem; + } + .terminal-window { + height: 450px; + } +}