v2
Cette révision appartient à :
+23
-893
@@ -4,744 +4,26 @@
|
|||||||
/>
|
/>
|
||||||
<style>
|
<style>
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Orbitron:wght@400;700&display=swap");
|
@import url("https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Orbitron:wght@400;700&display=swap");
|
||||||
|
@import url("./skins/skynet/styles/styles.css");
|
||||||
*,
|
@import url("./styles.css");
|
||||||
*::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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Masquer les éléments Roundcube par défaut */
|
|
||||||
#layout-content {
|
|
||||||
all: unset;
|
|
||||||
display: contents;
|
|
||||||
}
|
|
||||||
#logo {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── CANVAS 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── 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: 2.5rem 2.25rem;
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* corners */
|
|
||||||
.sky-corner {
|
|
||||||
position: absolute;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
border-color: var(--blue-bright);
|
|
||||||
border-style: solid;
|
|
||||||
}
|
|
||||||
.sky-tl {
|
|
||||||
top: 8px;
|
|
||||||
left: 8px;
|
|
||||||
border-width: 1px 0 0 1px;
|
|
||||||
animation: corner-flash 4s ease-in-out infinite 0s;
|
|
||||||
}
|
|
||||||
.sky-tr {
|
|
||||||
top: 8px;
|
|
||||||
right: 8px;
|
|
||||||
border-width: 1px 1px 0 0;
|
|
||||||
animation: corner-flash 4s ease-in-out infinite 1s;
|
|
||||||
}
|
|
||||||
.sky-bl {
|
|
||||||
bottom: 8px;
|
|
||||||
left: 8px;
|
|
||||||
border-width: 0 0 1px 1px;
|
|
||||||
animation: corner-flash 4s ease-in-out infinite 2s;
|
|
||||||
}
|
|
||||||
.sky-br {
|
|
||||||
bottom: 8px;
|
|
||||||
right: 8px;
|
|
||||||
border-width: 0 1px 1px 0;
|
|
||||||
animation: corner-flash 4s ease-in-out infinite 3s;
|
|
||||||
}
|
|
||||||
@keyframes corner-flash {
|
|
||||||
0%,
|
|
||||||
90%,
|
|
||||||
100% {
|
|
||||||
border-color: var(--blue-bright);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
95% {
|
|
||||||
border-color: var(--cyan);
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── LOGO ── */
|
|
||||||
.sky-logo {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 0.6rem;
|
|
||||||
animation: fade-in 1s ease both 0.4s;
|
|
||||||
}
|
|
||||||
.sky-logo img {
|
|
||||||
height: 68px;
|
|
||||||
filter: drop-shadow(0 0 8px rgba(0, 170, 255, 0.6));
|
|
||||||
animation: logo-glow 3s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
@keyframes logo-glow {
|
|
||||||
0%,
|
|
||||||
100% {
|
|
||||||
filter: drop-shadow(0 0 8px rgba(0, 170, 255, 0.6));
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
filter: drop-shadow(0 0 18px rgba(0, 200, 255, 0.95)) brightness(1.15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── ROUNDCUBE FORM OVERRIDE ── */
|
|
||||||
/* On surcharge les styles du formulaire généré par Roundcube */
|
|
||||||
#login-form {
|
|
||||||
animation: fade-in 0.6s ease both 0.7s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form .form-group,
|
|
||||||
#login-form .input-group {
|
|
||||||
margin-bottom: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form label {
|
|
||||||
color: rgba(26, 111, 255, 0.7) !important;
|
|
||||||
font-size: 9px !important;
|
|
||||||
letter-spacing: 3px !important;
|
|
||||||
font-family: "Share Tech Mono", monospace !important;
|
|
||||||
text-transform: uppercase !important;
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form input[type="text"],
|
|
||||||
#login-form input[type="password"],
|
|
||||||
#login-form input[type="email"] {
|
|
||||||
background: #010820 !important;
|
|
||||||
border: 1px solid #0d2266 !important;
|
|
||||||
color: var(--blue-light) !important;
|
|
||||||
font-family: "Share Tech Mono", monospace !important;
|
|
||||||
font-size: 13px !important;
|
|
||||||
padding: 12px !important;
|
|
||||||
width: 100% !important;
|
|
||||||
outline: none !important;
|
|
||||||
letter-spacing: 1px !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
|
||||||
caret-color: var(--cyan);
|
|
||||||
}
|
|
||||||
#login-form input:focus {
|
|
||||||
border-color: var(--blue-bright) !important;
|
|
||||||
box-shadow: 0 0 0 1px rgba(26, 111, 255, 0.15),
|
|
||||||
inset 0 0 16px rgba(26, 111, 255, 0.04) !important;
|
|
||||||
}
|
|
||||||
#login-form input::placeholder {
|
|
||||||
color: #0d2266 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bouton submit généré par Roundcube */
|
|
||||||
#login-form input[type="submit"],
|
|
||||||
#login-form button[type="submit"],
|
|
||||||
#login-form .btn-primary {
|
|
||||||
background: rgba(26, 79, 255, 0.06) !important;
|
|
||||||
border: 1px solid rgba(26, 79, 255, 0.6) !important;
|
|
||||||
color: var(--blue-light) !important;
|
|
||||||
font-family: "Orbitron", monospace !important;
|
|
||||||
font-size: 11px !important;
|
|
||||||
font-weight: 700 !important;
|
|
||||||
letter-spacing: 6px !important;
|
|
||||||
text-transform: uppercase !important;
|
|
||||||
padding: 14px !important;
|
|
||||||
width: 100% !important;
|
|
||||||
cursor: pointer !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
transition: all 0.25s !important;
|
|
||||||
margin-top: 0.5rem !important;
|
|
||||||
}
|
|
||||||
#login-form input[type="submit"]:hover,
|
|
||||||
#login-form button[type="submit"]:hover,
|
|
||||||
#login-form .btn-primary:hover {
|
|
||||||
background: rgba(26, 79, 255, 0.16) !important;
|
|
||||||
border-color: var(--cyan) !important;
|
|
||||||
color: var(--blue-pale) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Select (timezone, langue) */
|
|
||||||
#login-form select {
|
|
||||||
background: #010820 !important;
|
|
||||||
border: 1px solid #0d2266 !important;
|
|
||||||
color: var(--blue-light) !important;
|
|
||||||
font-family: "Share Tech Mono", monospace !important;
|
|
||||||
font-size: 12px !important;
|
|
||||||
padding: 8px !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fade-in {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── FORMULAIRE ROUNDCUBE CACHÉ ── */
|
|
||||||
#rc-form-hidden {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── CHAMPS CUSTOM SKYNET ── */
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── BOUTON CUSTOM ── */
|
|
||||||
.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 ── */
|
|
||||||
.blink {
|
|
||||||
animation: blink 1s step-end infinite;
|
|
||||||
}
|
|
||||||
@keyframes blink {
|
|
||||||
50% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── MESSAGE ERREUR ── */
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Masquer le footer Roundcube par défaut */
|
|
||||||
#login-footer {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── RESPONSIVE MOBILE ── */
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
align-items: flex-start;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sky-card {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 100dvh;
|
|
||||||
margin: 0;
|
|
||||||
padding: 2rem 1.25rem 2rem;
|
|
||||||
border-left: none;
|
|
||||||
border-right: none;
|
|
||||||
border-top: none;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sky-logo img {
|
|
||||||
height: 54px;
|
|
||||||
}
|
|
||||||
.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 evite le zoom auto iOS sur 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-status-item {
|
|
||||||
font-size: 9px;
|
|
||||||
}
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Background animé -->
|
<!-- Animated background -->
|
||||||
<canvas id="bg-canvas"></canvas>
|
<canvas id="bg-canvas"></canvas>
|
||||||
<div class="sky-grid"></div>
|
<div class="sky-grid"></div>
|
||||||
<div class="sky-scanlines"></div>
|
<div class="sky-scanlines"></div>
|
||||||
<div class="sky-glow"></div>
|
<div class="sky-glow"></div>
|
||||||
|
|
||||||
<!-- Carte de login -->
|
<!-- Login card -->
|
||||||
<div class="sky-card" id="sky-login-card">
|
<div class="sky-card" id="sky-login-card">
|
||||||
<div class="sky-corner sky-tl"></div>
|
<!-- Logo canvas — absolute background, top of card -->
|
||||||
<div class="sky-corner sky-tr"></div>
|
<div class="sky">
|
||||||
<div class="sky-corner sky-bl"></div>
|
<canvas id="skc" width="440" height="600"></canvas>
|
||||||
<div class="sky-corner sky-br"></div>
|
<div class="crt-s"></div>
|
||||||
|
<div class="crt-v"></div>
|
||||||
<div class="sky-logo">
|
|
||||||
<img src="./skins/skynet/images/logo-skynet.png" alt="Skynet" />
|
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Main content — pushed down by margin-top to leave room for logo -->
|
||||||
|
<div class="sky-card-body">
|
||||||
<div class="sky-brand-title">SKYNET</div>
|
<div class="sky-brand-title">SKYNET</div>
|
||||||
<div class="sky-brand-sub">
|
<div class="sky-brand-sub">
|
||||||
GLOBAL DEFENSE NETWORK // SECURE NODE ACCESS
|
GLOBAL DEFENSE NETWORK // SECURE NODE ACCESS
|
||||||
@@ -749,7 +31,7 @@
|
|||||||
|
|
||||||
<hr class="sky-divider" />
|
<hr class="sky-divider" />
|
||||||
|
|
||||||
<!-- FORMULAIRE ROUNDCUBE CACHÉ — gère le token CSRF et la soumission réelle -->
|
<!-- Hidden Roundcube form — handles CSRF token and real submission -->
|
||||||
<div id="rc-form-hidden">
|
<div id="rc-form-hidden">
|
||||||
<roundcube:form
|
<roundcube:form
|
||||||
id="login-form"
|
id="login-form"
|
||||||
@@ -767,7 +49,7 @@
|
|||||||
</roundcube:form>
|
</roundcube:form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- UI SKYNET — recopie les valeurs dans le vrai form au submit -->
|
<!-- Skynet UI — copies values to real form on submit -->
|
||||||
<div id="sky-ui-form">
|
<div id="sky-ui-form">
|
||||||
<div class="sky-error" id="sky-error">
|
<div class="sky-error" id="sky-error">
|
||||||
⚠ AUTHENTIFICATION ÉCHOUÉE
|
⚠ AUTHENTIFICATION ÉCHOUÉE
|
||||||
@@ -834,172 +116,20 @@
|
|||||||
<div class="sky-node-id">
|
<div class="sky-node-id">
|
||||||
SYS-ID: 4A2F-9C01-DELTA | v4.0.2026 | SKYNET CORP.
|
SYS-ID: 4A2F-9C01-DELTA | v4.0.2026 | SKYNET CORP.
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.sky-card-body -->
|
||||||
|
|
||||||
|
<div class="sky-corner sky-tl"></div>
|
||||||
|
<div class="sky-corner sky-tr"></div>
|
||||||
|
<div class="sky-corner sky-bl"></div>
|
||||||
|
<div class="sky-corner sky-br"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- /.sky-card -->
|
||||||
|
|
||||||
<noscript>
|
<noscript>
|
||||||
<p class="noscriptwarning"><roundcube:label name="noscriptwarning" /></p>
|
<p class="noscriptwarning"><roundcube:label name="noscriptwarning" /></p>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
<roundcube:include file="includes/footer.html" />
|
<roundcube:include file="includes/footer.html" />
|
||||||
|
<script src="./skins/skynet/script.js" defer></script>
|
||||||
<script>
|
<script src="./script.js" defer></script>
|
||||||
/* ── BRIDGE : UI Skynet → Formulaire Roundcube caché ── */
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
// Détecter si Roundcube a affiché une erreur via #messagestack
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recopier dans les vrais champs Roundcube
|
|
||||||
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;
|
|
||||||
|
|
||||||
// Animation de soumission
|
|
||||||
const btn = document.getElementById("sky-submit-btn");
|
|
||||||
btn.textContent = "AUTHENTIFICATION...";
|
|
||||||
btn.style.borderColor = "var(--cyan)";
|
|
||||||
btn.style.color = "var(--cyan)";
|
|
||||||
|
|
||||||
// Soumettre le vrai formulaire Roundcube
|
|
||||||
const rcForm = document.getElementById("login-form");
|
|
||||||
if (rcForm) {
|
|
||||||
rcForm.style.display = "block"; // visible l'instant du submit
|
|
||||||
rcForm.submit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Soumettre avec Entrée
|
|
||||||
["sky-user", "sky-pass"].forEach((id) => {
|
|
||||||
document.getElementById(id).addEventListener("keydown", function (e) {
|
|
||||||
if (e.key === "Enter")
|
|
||||||
document.getElementById("sky-submit-btn").click();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Focus automatique
|
|
||||||
document.getElementById("sky-user").focus();
|
|
||||||
});
|
|
||||||
|
|
||||||
/* ── CANVAS 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 draw() {
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
||||||
|
|
||||||
streams.forEach((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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
particles.forEach((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;
|
|
||||||
});
|
|
||||||
|
|
||||||
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.strokeStyle = "#1a4aff";
|
|
||||||
ctx.lineWidth = 0.3;
|
|
||||||
ctx.globalAlpha = (1 - dist / 100) * 0.15;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(particles[i].x, particles[i].y);
|
|
||||||
ctx.lineTo(particles[j].x, particles[j].y);
|
|
||||||
ctx.stroke();
|
|
||||||
ctx.globalAlpha = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
requestAnimationFrame(draw);
|
|
||||||
}
|
|
||||||
draw();
|
|
||||||
</script>
|
|
||||||
|
|||||||
Fichier binaire non affiché.
|
Avant Largeur: | Hauteur: | Taille: 66 KiB |
+518
@@ -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();
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Référencer dans un nouveau ticket
Bloquer un utilisateur