diff --git a/login.html b/login.html index a0bc6d5..1bf88dc 100644 --- a/login.html +++ b/login.html @@ -4,1002 +4,132 @@ /> - +
- +
+ +
+ +
+
+
+ +
+
SKYNET
+
+ GLOBAL DEFENSE NETWORK  //  SECURE NODE ACCESS +
+ +
+ + +
+ + + +
+ + +
+
+ ⚠   AUTHENTIFICATION ÉCHOUÉE +
+ + +
+ + + +
+ +
+ + + +
+ + +
+ +
+ MENACE +
+
+
+
+
+
+
+
+
+ MODÉRÉE +
+ +
+
RÉSEAU : EN LIGNE
+
SÉCURITÉ : NIV.5
+
NŒUD : HELIOS
+
+ +
+ + // ACCÈS NON AUTORISÉ SERA TRACÉ ET ÉLIMINÉ   //   + SKYNET SURVEILLE TOUTES LES CONNEXIONS   //   + PROTOCOLE DE SÉCURITÉ DELTA ACTIF   //   TOUTE + INTRUSION SERA NEUTRALISÉE   //   + +
+
+ SYS-ID: 4A2F-9C01-DELTA  |  v4.0.2026  |  SKYNET CORP. +
+
+ +
- - - -
SKYNET
-
- GLOBAL DEFENSE NETWORK  //  SECURE NODE ACCESS -
- -
- - -
- - - -
- - -
-
- ⚠   AUTHENTIFICATION ÉCHOUÉE -
- - -
- - - -
- -
- - - -
- - -
- -
- MENACE -
-
-
-
-
-
-
-
-
- MODÉRÉE -
- -
-
RÉSEAU : EN LIGNE
-
SÉCURITÉ : NIV.5
-
NŒUD : HELIOS
-
- -
- - // ACCÈS NON AUTORISÉ SERA TRACÉ ET ÉLIMINÉ   //   - SKYNET SURVEILLE TOUTES LES CONNEXIONS   //   - PROTOCOLE DE SÉCURITÉ DELTA ACTIF   //   TOUTE - INTRUSION SERA NEUTRALISÉE   //   - -
-
- SYS-ID: 4A2F-9C01-DELTA  |  v4.0.2026  |  SKYNET CORP. -
+ - - + + diff --git a/logo-skynet.png b/logo-skynet.png deleted file mode 100644 index 2e45e8c..0000000 Binary files a/logo-skynet.png and /dev/null differ diff --git a/script.js b/script.js new file mode 100644 index 0000000..ab881f2 --- /dev/null +++ b/script.js @@ -0,0 +1,518 @@ +(function () { + function init() { + const cv = document.getElementById("skc"); + if (!cv) return; + const ctx = cv.getContext("2d"); + + /* ── CANVAS SIZING ── + Width = sky-login-card width + Height = sky-login-card height + Read after DOM paint via double requestAnimationFrame to get final dimensions. + ResizeObserver keeps it in sync if card height changes later. + */ + function resizeCanvas() { + const card = document.getElementById("sky-login-card"); + if (!card) return; + const w = Math.round(card.offsetWidth) || 440; + const h = Math.round(card.offsetHeight) || 600; + if (cv.width !== w) cv.width = w; + if (cv.height !== h) cv.height = h; + } + + /* Wait two frames so card.offsetHeight reflects final layout */ + requestAnimationFrame(function () { + requestAnimationFrame(function () { + resizeCanvas(); + }); + }); + window.addEventListener("resize", resizeCanvas); + + /* Track card height changes (font load, zoom, etc.) */ + if (typeof ResizeObserver !== "undefined") { + const ro = new ResizeObserver(resizeCanvas); + const card = document.getElementById("sky-login-card"); + if (card) ro.observe(card); + } + + /* Dynamic accessors — always reflect current canvas size */ + const dim = { + get W() { + return cv.width; + }, + get H() { + return cv.height; + }, + get CX() { + return cv.width / 2; + }, + get CY() { + return cv.height / 2; + }, + }; + + /* ── 3D GEOMETRY ── + Raw coordinates in mm, centred on AX / OZ. + Scale (SC) applied dynamically in proj() every frame. + X range : 64.491..151.341 = 86.85 mm centre AX = 107.916 + Z range : 50.926..94.147 = 43.22 mm centre OZ = 72.536 + D = half-depth of extrusion (mm) + */ + const AX = 107.916, + OZ = 72.536, + D = 9; + + /* Logo is rendered in the TOP THIRD of the canvas. + CY_LOGO = vertical centre of the logo rendering area. + Scale fits the logo into the top portion with margin. */ + const LOGO_ZONE = 0.42; /* logo uses top 42% of canvas height */ + + function getSC() { + const availH = dim.H * LOGO_ZONE; + const scX = dim.W / (86.85 + D * 2); + const scZ = availH / (43.22 + D * 2); + return Math.min(scX, scZ) * 0.86; + } + + /* Vertical centre of the logo zone */ + function getLogoY() { + return (dim.H * LOGO_ZONE) / 2; + } + + function sv(x, z) { + return [x - AX, z - OZ]; + } + + /* Diamond (rotated square) */ + const LO = [ + sv(106.916, 51.0), + sv(119.2, 61.8), + sv(107.8, 74.5), + sv(95.812, 62.842), + ]; + /* Right quadrilateral */ + const FD = [ + sv(121.188, 63.842), + sv(151.341, 94.147), + sv(109.186, 94.147), + sv(109.186, 77.044), + ]; + /* Left quadrilateral (mirror of FD) */ + const FG = [ + sv(93.748, 64.742), + sv(106.646, 77.044), + sv(106.646, 94.147), + sv(64.491, 94.147), + ]; + + /* Extrude a 2D polygon into a 3D prism. + Returns {verts, edges}. + Front faces: indices 0..n-1 (y = -D) + Back faces: indices n..2n-1 (y = +D) */ + function extrude(pts2d) { + const n = pts2d.length; + const verts = [ + ...pts2d.map(([x, z]) => [x, -D, z]), + ...pts2d.map(([x, z]) => [x, D, z]), + ]; + const edges = []; + for (let i = 0; i < n; i++) { + const j = (i + 1) % n; + edges.push([i, j, "f"]); /* front edge */ + edges.push([i + n, j + n, "b"]); /* back edge */ + edges.push([i, i + n, "s"]); /* side edge */ + } + return { verts, edges }; + } + + const shapes = [extrude(LO), extrude(FD), extrude(FG)]; + + /* ── PERSPECTIVE PROJECTION ── + SC applied here every frame so resizing is instant. + Logo is centred on getLogoY() (top zone), not dim.CY. */ + function proj([x, y, z]) { + const sc = getSC(); + const s = 1.0 / (1.0 + y / 500.0); + return [dim.CX + x * sc * s, getLogoY() + z * sc * s, s, y]; + } + + /* ── ROTATION HELPERS ── */ + function rx(p, a) { + const c = Math.cos(a), + s = Math.sin(a); + return p.map(([x, y, z]) => [x, y * c - z * s, y * s + z * c]); + } + function ry(p, a) { + const c = Math.cos(a), + s = Math.sin(a); + return p.map(([x, y, z]) => [x * c + z * s, y, -x * s + z * c]); + } + function rz(p, a) { + const c = Math.cos(a), + s = Math.sin(a); + return p.map(([x, y, z]) => [x * c - y * s, x * s + y * c, z]); + } + + /* ── FLUORESCENT TUBE EFFECT ── + 4-pass rendering: wide glow → mid glow → tight glow → core wire */ + function tube(x1, y1, x2, y2, bright, al) { + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.strokeStyle = "rgba(100,180,255,0.05)"; + ctx.lineWidth = 20; + ctx.globalAlpha = al * 0.55; + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.strokeStyle = bright + ? "rgba(60,190,255,0.20)" + : "rgba(15,80,200,0.08)"; + ctx.lineWidth = 8; + ctx.globalAlpha = al * 0.88; + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.strokeStyle = bright + ? "rgba(150,225,255,0.65)" + : "rgba(45,120,255,0.22)"; + ctx.lineWidth = 2.4; + ctx.globalAlpha = al; + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.strokeStyle = bright + ? "rgba(225,246,255,0.98)" + : "rgba(85,165,255,0.52)"; + ctx.lineWidth = 0.65; + ctx.globalAlpha = al; + ctx.stroke(); + } + + /* ── CRT ARTEFACT STATE ── */ + let t = 0, + glT = 0, + glL = [], + scanY = 0, + scanOn = false, + scanTmr = 0; + let flickAlpha = 1.0, + flickTarget = 1.0, + flickTimer = 0; + + function spawnGlitch() { + glL = []; + const n = (2 + Math.random() * 5) | 0; + for (let i = 0; i < n; i++) + glL.push({ + y: (Math.random() * dim.H) | 0, + h: (1 + Math.random() * 5) | 0, + dx: (Math.random() - 0.5) * 26, + a: 0.28 + Math.random() * 0.5, + }); + glT = (3 + Math.random() * 8) | 0; + } + + /* ── MAIN RENDER LOOP ── */ + function draw() { + const W = dim.W, + H = dim.H, + CX = dim.CX; + + ctx.clearRect(0, 0, W, H); + + /* Global flicker (all shapes at once) */ + if (--flickTimer <= 0) { + flickTarget = Math.random() < 0.06 ? 0.55 + Math.random() * 0.35 : 1.0; + flickTimer = (2 + Math.random() * 12) | 0; + } + flickAlpha += (flickTarget - flickAlpha) * 0.18; + + /* Slow tilted rotation */ + const ax = t * 0.0042 + 0.2, + ay = t * 0.0078; + //const ax = 0; + //ay = 0; + + const allEdges = []; + + shapes.forEach(function ({ verts, edges }) { + let pts = verts; + pts = rx(pts, ax); + pts = ry(pts, ay); + pts = rz(pts, 0.07); + const pr = pts.map(proj); + edges.forEach(function ([a, b, tp]) { + allEdges.push({ pr, a, b, tp, d: (pr[a][3] + pr[b][3]) * 0.5 }); + }); + }); + + /* Ambient glow centred on logo zone */ + const logoY = getLogoY(); + const g = ctx.createRadialGradient(CX, logoY, 5, CX, logoY, 100); + g.addColorStop(0, "rgba(0,65,195," + 0.14 * flickAlpha + ")"); + g.addColorStop(1, "rgba(0,0,0,0)"); + ctx.fillStyle = g; + ctx.globalAlpha = 1; + ctx.fillRect(0, 0, W, H); + + /* Painter's algorithm — back to front */ + allEdges.sort(function (u, v) { + return v.d - u.d; + }); + allEdges.forEach(function ({ pr, a, b, tp, d }) { + const [ax2, ay2] = pr[a], + [bx, by] = pr[b]; + const d01 = (d + D) / (2 * D); + const bright = tp === "f"; + const base = + tp === "f" + ? 0.52 + d01 * 0.46 + : tp === "b" + ? 0.07 + d01 * 0.06 + : 0.17 + d01 * 0.22; + tube(ax2, ay2, bx, by, bright, base * flickAlpha); + }); + + ctx.globalAlpha = 1; + + /* Glitch: horizontal band shifts + compression block artefacts */ + if (glT > 0) { + glT--; + glL.forEach(function (gl) { + try { + const sl = ctx.getImageData(0, gl.y, W, gl.h); + ctx.globalAlpha = gl.a; + ctx.putImageData(sl, gl.dx, gl.y); + } catch (e) {} + ctx.fillStyle = "rgba(0,150,255,0.04)"; + ctx.globalAlpha = 1; + ctx.fillRect(0, gl.y, W, gl.h); + }); + if (Math.random() < 0.4) { + ctx.globalAlpha = 0.08; + ctx.fillStyle = "#00eeff"; + ctx.fillRect(0, (Math.random() * H) | 0, W, 1); + } + if (Math.random() < 0.25) { + ctx.globalAlpha = 0.12; + ctx.fillStyle = "rgba(0,80,220,0.5)"; + ctx.fillRect( + (Math.random() * (W - 40)) | 0, + (Math.random() * (H - 8)) | 0, + (18 + Math.random() * 38) | 0, + (2 + Math.random() * 7) | 0 + ); + } + } + + /* Rolling scan line */ + if (scanOn) { + ctx.globalAlpha = 0.06; + ctx.fillStyle = "rgba(80,200,255,1)"; + ctx.fillRect(0, scanY, W, 2); + scanY = (scanY + 3) % H; + if (--scanTmr <= 0) scanOn = false; + } + + /* Phosphor burn (persistence) */ + if (Math.random() < 0.016) { + ctx.globalAlpha = 0.025; + ctx.fillStyle = "rgba(0,120,255,1)"; + ctx.fillRect( + (CX - 50 + Math.random() * 100) | 0, + (logoY - 30 + Math.random() * 60) | 0, + (Math.random() * 25) | 0, + 1 + ); + } + + ctx.globalAlpha = 1; + + /* Random artefact triggers */ + if (Math.random() < 0.012) spawnGlitch(); + if (Math.random() < 0.006) { + scanOn = true; + scanY = 0; + scanTmr = (20 + Math.random() * 30) | 0; + } + + t++; + + requestAnimationFrame(draw); + } + + draw(); + } /* end init() */ + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); + } +})(); + +/* ── BRIDGE: Skynet UI → hidden Roundcube form ── */ +document.addEventListener("DOMContentLoaded", function () { + /* Detect Roundcube auth error and show Skynet error box */ + const rcError = document.querySelector( + "#messagestack .alert-warning, #messagestack .alert-danger, #messagestack .alert" + ); + if (rcError && rcError.textContent.trim() !== "") { + const skyError = document.getElementById("sky-error"); + skyError.textContent = "⚠ AUTHENTIFICATION REFUSÉE — ACCÈS INTERDIT"; + skyError.classList.add("visible"); + const card = document.getElementById("sky-login-card"); + card.style.borderColor = "rgba(255,68,68,0.5)"; + card.style.boxShadow = "0 0 30px rgba(255,68,68,0.1)"; + const ms = document.getElementById("messagestack"); + if (ms) ms.style.display = "none"; + } + + /* Submit handler: copy values then submit real RC form */ + document + .getElementById("sky-submit-btn") + .addEventListener("click", function () { + const user = document.getElementById("sky-user").value.trim(); + const pass = document.getElementById("sky-pass").value; + + if (!user || !pass) { + document.getElementById("sky-error").textContent = + "⚠ IDENTIFIANTS MANQUANTS"; + document.getElementById("sky-error").classList.add("visible"); + return; + } + + const rcUser = + document.getElementById("rcmloginuser") || + document.querySelector('#login-form input[name="_user"]'); + const rcPass = + document.getElementById("rcmloginpwd") || + document.querySelector('#login-form input[name="_pass"]'); + + if (rcUser) rcUser.value = user; + if (rcPass) rcPass.value = pass; + + const btn = document.getElementById("sky-submit-btn"); + btn.textContent = "AUTHENTIFICATION..."; + btn.style.borderColor = "var(--cyan)"; + btn.style.color = "var(--cyan)"; + + const rcForm = document.getElementById("login-form"); + if (rcForm) { + rcForm.style.display = "block"; + rcForm.submit(); + } + }); + + /* Submit on Enter key */ + ["sky-user", "sky-pass"].forEach(function (id) { + document.getElementById(id).addEventListener("keydown", function (e) { + if (e.key === "Enter") document.getElementById("sky-submit-btn").click(); + }); + }); + + /* Auto-focus username field */ + document.getElementById("sky-user").focus(); +}); + +/* ── FULLSCREEN PARTICLE BACKGROUND ── */ +const canvas = document.getElementById("bg-canvas"); +const ctx = canvas.getContext("2d"); + +function resize() { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; +} +resize(); +window.addEventListener("resize", resize); + +const particles = []; +for (let i = 0; i < 80; i++) { + particles.push({ + x: Math.random() * window.innerWidth, + y: Math.random() * window.innerHeight, + vx: (Math.random() - 0.5) * 0.4, + vy: -Math.random() * 0.6 - 0.1, + r: Math.random() * 1.5 + 0.5, + alpha: Math.random() * 0.5 + 0.1, + color: Math.random() > 0.5 ? "#1a6fff" : "#00aaff", + }); +} + +const streams = []; +for (let i = 0; i < 14; i++) { + streams.push({ + x: Math.random() * window.innerWidth, + y: Math.random() * window.innerHeight, + speed: Math.random() * 1.5 + 0.5, + length: Math.random() * 80 + 40, + alpha: Math.random() * 0.15 + 0.05, + }); +} + +function drawBg() { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + /* Data streams */ + streams.forEach(function (s) { + const grad = ctx.createLinearGradient(s.x, s.y - s.length, s.x, s.y); + grad.addColorStop(0, "rgba(26,111,255,0)"); + grad.addColorStop(1, "rgba(0,170,255," + s.alpha + ")"); + ctx.strokeStyle = grad; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(s.x, s.y - s.length); + ctx.lineTo(s.x, s.y); + ctx.stroke(); + s.y += s.speed; + if (s.y > canvas.height + s.length) { + s.y = -s.length; + s.x = Math.random() * canvas.width; + } + }); + + /* Floating particles */ + particles.forEach(function (p) { + ctx.beginPath(); + ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2); + ctx.fillStyle = p.color; + ctx.globalAlpha = p.alpha; + ctx.fill(); + ctx.globalAlpha = 1; + p.x += p.vx; + p.y += p.vy; + if (p.y < -5) { + p.y = canvas.height + 5; + p.x = Math.random() * canvas.width; + } + if (p.x < -5) p.x = canvas.width + 5; + if (p.x > canvas.width + 5) p.x = -5; + }); + + /* Connection lines between nearby particles */ + for (let i = 0; i < particles.length; i++) { + for (let j = i + 1; j < particles.length; j++) { + const dx = particles[i].x - particles[j].x; + const dy = particles[i].y - particles[j].y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < 100) { + ctx.globalAlpha = (1 - dist / 100) * 0.3; + ctx.lineWidth = 0.5; + ctx.strokeStyle = "#00cfff"; + ctx.beginPath(); + ctx.moveTo(particles[i].x, particles[i].y); + ctx.lineTo(particles[j].x, particles[j].y); + ctx.stroke(); + ctx.globalAlpha = 1; + } + } + } + requestAnimationFrame(drawBg); +} +drawBg(); diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..a5f20d0 --- /dev/null +++ b/styles.css @@ -0,0 +1,640 @@ +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +:root { + --blue-deep: #020510; + --blue-night: #040d2a; + --blue-dark: #0a1a4a; + --blue-mid: #0d2a7a; + --blue-bright: #1a6fff; + --blue-glow: #00aaff; + --blue-light: #7ab8ff; + --blue-pale: #aad4ff; + --cyan: #00cfff; +} + +html, +body { + height: 100%; + width: 100%; + overflow: hidden; + background: var(--blue-deep) !important; + font-family: "Share Tech Mono", monospace; + display: flex; + align-items: center; + justify-content: center; + position: relative; +} + +/* ── LOGO CANVAS ── + Absolute overlay covering the entire card. + The JS renders the logo in the top zone only. + overflow:visible ensures the canvas is never clipped. */ +.sky { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: visible; + background: transparent; + pointer-events: none; + z-index: 0; +} +.sky canvas { + display: block; +} + +/* CRT scanline overlay on logo */ +.crt-s { + position: absolute; + inset: 0; + z-index: 2; + pointer-events: none; + background: repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0, 0, 0, 0.055) 2px, + rgba(0, 0, 0, 0.082) 3px + ); +} +.crt-v { + position: absolute; + inset: 0; + z-index: 3; + pointer-events: none; +} + +/* ── FULLSCREEN ANIMATED BACKGROUND ── */ +#bg-canvas { + position: fixed; + inset: 0; + z-index: 0; +} + +/* ── GRID OVERLAY ── */ +.sky-grid { + position: fixed; + inset: 0; + z-index: 1; + background-image: linear-gradient( + rgba(26, 111, 255, 0.05) 1px, + transparent 1px + ), + linear-gradient(90deg, rgba(26, 111, 255, 0.05) 1px, transparent 1px); + background-size: 48px 48px; + animation: grid-pulse 8s ease-in-out infinite; + pointer-events: none; +} +@keyframes grid-pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.4; + } +} + +/* ── SCANLINES ── */ +.sky-scanlines { + position: fixed; + inset: 0; + z-index: 2; + background: repeating-linear-gradient( + 0deg, + transparent, + transparent 3px, + rgba(0, 0, 0, 0.12) 3px, + rgba(0, 0, 0, 0.12) 4px + ); + pointer-events: none; +} + +/* ── CENTER GLOW ── */ +.sky-glow { + position: fixed; + width: 800px; + height: 800px; + border-radius: 50%; + background: radial-gradient( + circle, + rgba(0, 80, 220, 0.22) 0%, + transparent 70% + ); + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 1; + pointer-events: none; + animation: glow-breathe 6s ease-in-out infinite; +} +@keyframes glow-breathe { + 0%, + 100% { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } + 50% { + opacity: 0.6; + transform: translate(-50%, -50%) scale(1.15); + } +} + +/* ── LOGIN CARD ── */ +.sky-card { + position: relative; + z-index: 10; + background: rgba(2, 8, 30, 0.6); + border: 1px solid rgba(26, 111, 255, 0.3); + width: 440px; + padding: 0; + overflow: hidden; + animation: card-appear 1.2s cubic-bezier(0.16, 1, 0.3, 1) both; +} +@keyframes card-appear { + from { + opacity: 0; + transform: translateY(30px) scale(0.97); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} +.sky-card::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient( + 90deg, + transparent, + var(--blue-bright), + var(--cyan), + var(--blue-bright), + transparent + ); + animation: top-line-scan 3s linear infinite; +} +@keyframes top-line-scan { + 0% { + background-position: -440px 0; + } + 100% { + background-position: 440px 0; + } +} +.sky-card::after { + content: ""; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 1px; + background: linear-gradient( + 90deg, + transparent, + rgba(26, 111, 255, 0.4), + transparent + ); +} + +/* Card corner decorations */ +.sky-corner { + position: absolute; + width: 16px; + height: 16px; + z-index: 6; + border-color: var(--blue-bright); + border-style: solid; + animation: corner-flash 4s ease-in-out infinite 0s; +} +.sky-tl { + top: 8px; + left: 8px; + border-width: 1px 0 0 1px; +} +.sky-tr { + top: 8px; + right: 8px; + border-width: 1px 1px 0 0; +} +.sky-bl { + bottom: 8px; + left: 8px; + border-width: 0 0 1px 1px; +} +.sky-br { + bottom: 8px; + right: 8px; + border-width: 0 1px 1px 0; +} +@keyframes corner-flash { + 0%, + 90%, + 100% { + border-color: var(--blue-bright); + opacity: 1; + } + 95% { + border-color: var(--blue-dark) + opacity: 0.5; + } +} + +/* ── CARD BODY ── + margin-top pushes content below the logo zone (top 42% of card height). + The gradient fades the logo into the card background. */ +.sky-card-body { + position: relative; + z-index: 5; + padding: 1.5rem 2.25rem 2rem; + margin-top: 42%; + margin-bottom: 0px!; +} + +/* ── BRAND ── */ +.sky-brand-title { + font-family: "Orbitron", monospace; + color: var(--blue-glow); + font-size: 26px; + letter-spacing: 10px; + text-transform: uppercase; + text-align: center; + animation: fade-in 1s ease both 0.5s; + text-shadow: 0 0 20px rgba(0, 170, 255, 0.5); +} +.sky-brand-sub { + color: rgba(26, 111, 255, 0.5); + font-size: 9px; + letter-spacing: 3px; + text-align: center; + margin-top: 4px; + margin-bottom: 1.75rem; + animation: fade-in 1s ease both 0.6s; +} +.sky-divider { + border: none; + border-top: 1px solid #0a1a4a; + margin-bottom: 1.5rem; +} + +/* ── HIDDEN ROUNDCUBE FORM ── */ +#rc-form-hidden { + display: none !important; +} + +/* ── SKYNET INPUT FIELDS ── */ +.sky-field-wrap { + position: relative; + margin-bottom: 1.25rem; + animation: slide-up 0.6s ease both; +} +.sky-field-wrap:nth-child(1) { + animation-delay: 0.7s; +} +.sky-field-wrap:nth-child(2) { + animation-delay: 0.85s; +} +@keyframes slide-up { + from { + opacity: 0; + transform: translateY(12px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.sky-field-label { + color: rgba(26, 111, 255, 0.7); + font-size: 9px; + letter-spacing: 3px; + margin-bottom: 6px; + display: block; + text-transform: uppercase; +} +.sky-field-icon { + position: absolute; + left: 12px; + bottom: 13px; + color: var(--blue-bright); + font-size: 12px; + transition: color 0.2s; + pointer-events: none; +} +.sky-input { + background: #010820; + border: 1px solid #0d2266; + color: var(--blue-light); + font-family: "Share Tech Mono", monospace; + font-size: 13px; + padding: 12px 12px 12px 32px; + width: 100%; + outline: none; + letter-spacing: 1px; + border-radius: 0; + transition: border-color 0.2s, box-shadow 0.2s; + caret-color: var(--cyan); +} +.sky-input:focus { + border-color: var(--blue-bright); + box-shadow: 0 0 0 1px rgba(26, 111, 255, 0.15), + inset 0 0 16px rgba(26, 111, 255, 0.04); +} +.sky-input:focus ~ .sky-field-icon { + color: var(--cyan); +} +.sky-input::placeholder { + color: #0d2266; +} + +/* ── SUBMIT BUTTON ── */ +.sky-submit { + background: rgba(26, 79, 255, 0.06); + border: 1px solid rgba(26, 79, 255, 0.6); + color: var(--blue-light); + font-family: "Orbitron", monospace; + font-size: 11px; + font-weight: 700; + letter-spacing: 6px; + text-transform: uppercase; + padding: 14px; + width: 100%; + cursor: pointer; + margin-top: 0.25rem; + position: relative; + overflow: hidden; + transition: all 0.25s; + border-radius: 0; + animation: slide-up 0.6s ease both 1s; +} +.sky-submit::before { + content: ""; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(26, 111, 255, 0.12), + transparent + ); + transition: left 0.5s; +} +.sky-submit:hover { + background: rgba(26, 79, 255, 0.16); + border-color: var(--cyan); + color: var(--blue-pale); + text-shadow: 0 0 10px rgba(0, 200, 255, 0.5); +} +.sky-submit:hover::before { + left: 100%; +} +.sky-submit:active { + transform: scale(0.99); +} + +/* ── BLINK CURSOR ── */ +.blink { + animation: blink 1s step-end infinite; +} +@keyframes blink { + 50% { + opacity: 0; + } +} + +/* ── ERROR MESSAGE ── */ +#messagestack { + display: none; +} +.sky-error { + display: none; +} +#messagestack > div, +.sky-error { + color: #ff4444; + font-size: 10px; + letter-spacing: 2px; + text-align: center; + margin-bottom: 1rem; + padding: 8px; + border: 1px solid rgba(255, 68, 68, 0.3); + background: rgba(255, 68, 68, 0.05); + animation: fade-in 0.3s ease; +} +.sky-error.visible { + display: block; +} + +/* ── STATUS BAR ── */ +.sky-status { + display: flex; + justify-content: space-between; + margin-top: 1.25rem; + animation: fade-in 1s ease both 1.1s; +} +.sky-status-item { + color: rgba(13, 34, 102, 0.9); + font-size: 9px; + letter-spacing: 1px; +} +.sky-status-item span { + color: rgba(26, 111, 255, 0.55); +} +.sky-status-item.online span { + color: #00ff88; + animation: pulse-green 2s ease-in-out infinite; +} +@keyframes pulse-green { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.4; + } +} + +/* ── THREAT INDICATOR ── */ +.sky-threat { + display: flex; + align-items: center; + gap: 6px; + margin-top: 1rem; + padding: 8px 10px; + border: 1px solid rgba(13, 34, 102, 0.6); + background: rgba(1, 8, 32, 0.5); + animation: fade-in 1s ease both 1s; +} +.sky-threat-label { + color: rgba(26, 111, 255, 0.4); + font-size: 9px; + letter-spacing: 2px; + flex-shrink: 0; +} +.sky-threat-bars { + display: flex; + gap: 3px; + flex: 1; +} +.sky-tbar { + height: 8px; + flex: 1; + background: rgba(13, 34, 102, 0.4); + border: 1px solid rgba(13, 34, 102, 0.6); +} +.sky-tbar.on { + background: var(--blue-bright); + animation: bar-pulse 2s ease-in-out infinite; +} +.sky-tbar.on:nth-child(2) { + animation-delay: 0.2s; + background: var(--blue-glow); +} +.sky-tbar.on:nth-child(3) { + animation-delay: 0.4s; + background: var(--cyan); +} +@keyframes bar-pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} +.sky-threat-val { + color: rgba(0, 207, 255, 0.6); + font-size: 9px; + letter-spacing: 1px; + flex-shrink: 0; +} + +/* ── SCROLLING TICKER ── */ +.sky-ticker { + color: rgba(13, 34, 102, 0.8); + font-size: 8px; + letter-spacing: 1px; + margin-top: 0.6rem; + overflow: hidden; + white-space: nowrap; + animation: fade-in 1s ease both 1.2s; +} +.sky-ticker-inner { + display: inline-block; + animation: ticker-scroll 22s linear infinite; +} +@keyframes ticker-scroll { + 0% { + transform: translateX(100%); + } + 100% { + transform: translateX(-100%); + } +} + +/* ── NODE ID ── */ +.sky-node-id { + color: rgba(26, 74, 255, 0.25); + font-size: 8px; + text-align: center; + margin-top: 5px; + letter-spacing: 2px; + animation: fade-in 1s ease both 1.3s; +} + +/* ── HIDE ROUNDCUBE FOOTER ── */ +#login-footer { + display: none !important; +} + +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +/* ── RESPONSIVE MOBILE ── */ +@media (max-width: 480px) { + html, + body { + align-items: flex-start; + overflow-y: auto; + } + + .sky-card { + width: 100%; + margin: 30px; + display: flex; + flex-direction: column; + justify-content: center; + } + + .sky-brand-title { + font-size: 20px; + letter-spacing: 6px; + } + .sky-brand-sub { + font-size: 8px; + letter-spacing: 2px; + margin-bottom: 1.25rem; + } + + /* font-size 16px prevents iOS auto-zoom on focus */ + .sky-input { + font-size: 16px !important; + padding: 14px 12px 14px 32px !important; + } + .sky-submit { + font-size: 12px !important; + padding: 16px !important; + letter-spacing: 4px !important; + } + + .sky-status { + flex-direction: column; + gap: 4px; + align-items: center; + } + .sky-threat { + display: none; + } + .sky-ticker { + font-size: 8px; + } + .sky-node-id { + font-size: 7px; + } + .sky-error { + font-size: 11px; + letter-spacing: 1px; + } +} + +@media (max-width: 360px) { + .sky-brand-title { + font-size: 17px; + letter-spacing: 4px; + } + .sky-card { + padding: 1.5rem 1rem; + } +}