Auth-herding: roller, kontoadmin, token-revokering + søkbar planliste + neon

Auth (app-modus):
- Roller admin/standard. Admin oppretter/sletter kontoer, resetter passord,
  endrer roller (konto-modal). Eksisterende brukere → admin ved migrering;
  bootstrap-bruker via proxy-bypass → admin. Siste admin vernet.
- Tilbakekalling via per-bruker epoke i tokenet: passordbytte, admin-reset,
  «Logg ut alle enheter» og sletting dreper alle aktive sesjoner. Token-format
  endret (3- → 4-delt) — alle logges ut én gang ved oppgradering.

Dagsplan: søkbar/velgbar planliste («📋 Planer») avledet fra planIndex —
finn plan på navn/dato/person uten å gjette dato.

GUI: login-neon trukket inn i hovedappen (header/knapper/KPI-kanter), og
login-siden fikk mer tech (HUD-hjørner, glitch-tittel, scanlines).

40 integrasjonstester (6 nye), alle grønne. README + CLAUDE.md oppdatert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jon Vanvik 2026-06-15 14:56:39 +02:00
parent f6268440d9
commit 3f69cd4248
9 changed files with 518 additions and 36 deletions

View file

@ -47,21 +47,54 @@
.card {
position: relative; z-index: 2; width: 100%; max-width: 420px;
background: rgba(10,14,22,0.72); backdrop-filter: blur(12px);
border: 1px solid rgba(79,230,255,0.25); border-radius: 14px; padding: 34px 30px 28px;
box-shadow: 0 0 0 1px rgba(255,92,244,0.08), 0 24px 70px rgba(0,0,0,0.6), 0 0 60px rgba(79,230,255,0.12);
border: 1px solid rgba(79,230,255,0.35); border-radius: 14px; padding: 34px 30px 28px;
box-shadow: 0 0 0 1px rgba(255,92,244,0.1), 0 24px 70px rgba(0,0,0,0.6), 0 0 60px rgba(79,230,255,0.12);
animation: borderpulse 3.6s ease-in-out infinite;
}
.prompt { font-family: var(--mono); font-size: 12px; color: var(--cyan); letter-spacing: 1px; margin-bottom: 8px; opacity: 0.85; }
@keyframes borderpulse {
0%,100% { border-color: rgba(79,230,255,0.35); box-shadow: 0 0 0 1px rgba(255,92,244,0.1), 0 24px 70px rgba(0,0,0,0.6), 0 0 50px rgba(79,230,255,0.12); }
50% { border-color: rgba(79,230,255,0.6); box-shadow: 0 0 0 1px rgba(255,92,244,0.18), 0 24px 70px rgba(0,0,0,0.6), 0 0 80px rgba(79,230,255,0.28); }
}
/* Tynn scanline over kortet + svak CRT-støy */
.card::before {
content: ""; position: absolute; inset: 0; border-radius: 14px; pointer-events: none;
background: repeating-linear-gradient(0deg, transparent 0, transparent 2px, rgba(79,230,255,0.025) 3px, transparent 4px);
mix-blend-mode: screen;
}
/* HUD-hjørner */
.corner { position: absolute; width: 18px; height: 18px; border: 2px solid var(--cyan);
filter: drop-shadow(0 0 4px var(--cyan)); pointer-events: none; }
.corner.tl { top: -1px; left: -1px; border-right: 0; border-bottom: 0; border-top-left-radius: 14px; }
.corner.tr { top: -1px; right: -1px; border-left: 0; border-bottom: 0; border-top-right-radius: 14px; border-color: var(--magenta); filter: drop-shadow(0 0 4px var(--magenta)); }
.corner.bl { bottom: -1px; left: -1px; border-right: 0; border-top: 0; border-bottom-left-radius: 14px; border-color: var(--magenta); filter: drop-shadow(0 0 4px var(--magenta)); }
.corner.br { bottom: -1px; right: -1px; border-left: 0; border-top: 0; border-bottom-right-radius: 14px; }
.hud { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
.prompt { font-family: var(--mono); font-size: 12px; color: var(--cyan); letter-spacing: 1px; opacity: 0.85; }
.prompt::before { content: " "; }
.lock { font-family: var(--mono); font-size: 10px; letter-spacing: 1.5px; color: var(--magenta);
border: 1px solid rgba(255,92,244,0.4); border-radius: 4px; padding: 3px 7px;
text-shadow: 0 0 8px rgba(255,92,244,0.7); animation: lockblink 2.4s steps(1) infinite; }
@keyframes lockblink { 0%,70%,100% { opacity: 1; } 78%,88% { opacity: 0.3; } }
h1 {
font-size: 40px; font-weight: 800; letter-spacing: 1px; line-height: 1;
color: #eafcff; text-shadow: 0 0 8px var(--cyan), 0 0 22px rgba(79,230,255,0.7), 0 0 40px rgba(79,230,255,0.4);
animation: flicker 4.5s infinite;
font-size: 42px; font-weight: 800; letter-spacing: 1px; line-height: 1; position: relative;
color: #eafcff;
text-shadow: -1.5px 0 rgba(255,92,244,0.55), 1.5px 0 rgba(79,230,255,0.55),
0 0 8px var(--cyan), 0 0 22px rgba(79,230,255,0.75), 0 0 44px rgba(79,230,255,0.45);
animation: flicker 4.5s infinite, glitch 7s steps(1) infinite;
}
h1 .x { color: var(--magenta); text-shadow: 0 0 8px var(--magenta), 0 0 22px rgba(255,92,244,0.7), 0 0 40px rgba(255,92,244,0.4); }
h1 .x { color: var(--magenta); text-shadow: 0 0 8px var(--magenta), 0 0 22px rgba(255,92,244,0.75), 0 0 44px rgba(255,92,244,0.45); }
@keyframes flicker {
0%,18%,22%,25%,53%,57%,100% { opacity: 1; }
20%,24%,55% { opacity: 0.72; }
}
/* Sporadisk kromatisk «glitch»-forskyvning */
@keyframes glitch {
0%,90%,100% { transform: translate(0,0); text-shadow: -1.5px 0 rgba(255,92,244,0.55), 1.5px 0 rgba(79,230,255,0.55), 0 0 8px var(--cyan), 0 0 22px rgba(79,230,255,0.75); }
91% { transform: translate(2px,-1px); text-shadow: -3px 0 var(--magenta), 3px 0 var(--cyan), 0 0 26px rgba(79,230,255,0.9); }
93% { transform: translate(-2px,1px); text-shadow: 3px 0 var(--magenta), -3px 0 var(--cyan), 0 0 26px rgba(79,230,255,0.9); }
95% { transform: translate(1px,0); }
}
.teaser { color: var(--dim); font-size: 13.5px; line-height: 1.6; margin: 14px 0 22px; }
.teaser b { color: var(--text); font-weight: 600; }
.ticker { font-family: var(--mono); font-size: 11px; color: var(--green);
@ -87,7 +120,8 @@
background: linear-gradient(90deg, var(--cyan), #7fe9ff);
box-shadow: 0 0 22px rgba(79,230,255,0.5); transition: all 0.18s;
}
button:hover { box-shadow: 0 0 34px rgba(79,230,255,0.85); transform: translateY(-1px); }
button:hover { box-shadow: 0 0 40px rgba(79,230,255,1); transform: translateY(-1px); letter-spacing: 1.5px; }
button::after { content: ""; }
button:disabled { opacity: 0.6; cursor: wait; }
.msg { min-height: 1.2em; font-size: 13px; color: var(--magenta); font-family: var(--mono);
text-shadow: 0 0 10px rgba(255,92,244,0.5); text-align: center; }
@ -105,7 +139,12 @@
<div class="scan"></div>
<div class="card">
<div class="prompt">network-engineer@nms:~$ ssh netops</div>
<span class="corner tl"></span><span class="corner tr"></span>
<span class="corner bl"></span><span class="corner br"></span>
<div class="hud">
<div class="prompt">network-engineer@nms:~$ ssh netops</div>
<span class="lock">● LOCKED</span>
</div>
<h1>NetOps<span class="x">//</span></h1>
<p class="teaser">
Bak denne porten <b>puster nettet</b>. Pakker flyr, rutene konvergerer,