Initial commit
This commit is contained in:
114
assets/starfield.js
Normal file
114
assets/starfield.js
Normal file
@@ -0,0 +1,114 @@
|
||||
(() => {
|
||||
const canvas = document.getElementById("starfield");
|
||||
if (!canvas) return;
|
||||
|
||||
const reduceMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;
|
||||
if (reduceMotion) return;
|
||||
|
||||
const ctx = canvas.getContext("2d", { alpha: true });
|
||||
|
||||
const cfg = {
|
||||
get perf(){ return (document.documentElement.dataset.perf || "auto").toLowerCase(); },
|
||||
starCount(){ return this.perf === "high" ? 1100 : this.perf === "medium" ? 650 : this.perf === "low" ? 0 : 500; },
|
||||
fps(){ return this.perf === "high" ? 60 : this.perf === "medium" ? 30 : this.perf === "low" ? 0 : 25; },
|
||||
dpr(){ return this.perf === "high" ? Math.min(devicePixelRatio||1, 2) : 1; },
|
||||
twinkle(){ return this.perf === "high" ? 0.45 : 0.35; },
|
||||
};
|
||||
|
||||
let w=0,h=0,dpr=1,stars=[],mouseX=0,mouseY=0,last=performance.now(),acc=0;
|
||||
let lastPerf = "";
|
||||
|
||||
function rand(min,max){ return min + Math.random()*(max-min); }
|
||||
|
||||
function newStar(randomZ=false){
|
||||
return { x: rand(-1,1), y: rand(-1,1), z: randomZ ? Math.random() : 1, r: rand(0.6,1.7), t: Math.random()*Math.PI*2 };
|
||||
}
|
||||
|
||||
function initStars(){
|
||||
const n = cfg.starCount();
|
||||
stars = Array.from({length:n}, ()=>newStar(true));
|
||||
}
|
||||
|
||||
function resize(){
|
||||
dpr = cfg.dpr();
|
||||
w = Math.floor(window.innerWidth);
|
||||
h = Math.floor(window.innerHeight);
|
||||
canvas.width = Math.floor(w*dpr);
|
||||
canvas.height = Math.floor(h*dpr);
|
||||
canvas.style.width = w+"px";
|
||||
canvas.style.height = h+"px";
|
||||
ctx.setTransform(dpr,0,0,dpr,0,0);
|
||||
initStars();
|
||||
}
|
||||
|
||||
function project(s){
|
||||
const cx=w*0.5, cy=h*0.5;
|
||||
const p = 1/(s.z*1.25);
|
||||
return { x: cx + s.x*cx*p, y: cy + s.y*cy*p, size: s.r*p };
|
||||
}
|
||||
|
||||
function tick(dt){
|
||||
ctx.clearRect(0,0,w,h);
|
||||
|
||||
const g = ctx.createRadialGradient(w*0.5,h*0.45,0,w*0.5,h*0.45,Math.min(w,h)*0.85);
|
||||
g.addColorStop(0,"rgba(66,245,255,0.04)");
|
||||
g.addColorStop(0.5,"rgba(255,61,242,0.02)");
|
||||
g.addColorStop(1,"rgba(0,0,0,0)");
|
||||
ctx.fillStyle=g;
|
||||
ctx.fillRect(0,0,w,h);
|
||||
|
||||
const mx=(mouseX-w*0.5)/(w*0.5), my=(mouseY-h*0.5)/(h*0.5);
|
||||
const speed = (cfg.perf==="high" ? 0.03 : 0.022);
|
||||
|
||||
for(let i=0;i<stars.length;i++){
|
||||
const s=stars[i];
|
||||
s.z -= speed*dt;
|
||||
if(s.z<=0.02) stars[i]=newStar(false);
|
||||
s.t += dt*rand(2.0,5.0);
|
||||
|
||||
const p=project(s);
|
||||
if(p.x<-60||p.x>w+60||p.y<-60||p.y>h+60) continue;
|
||||
|
||||
const tw = 0.7 + Math.sin(s.t)*cfg.twinkle()*0.25;
|
||||
const alpha = Math.min(0.95, (0.18 + (1-s.z)*0.85)*tw);
|
||||
|
||||
const par = 0.22*(1-s.z);
|
||||
const x2 = p.x + mx*18*par;
|
||||
const y2 = p.y + my*12*par;
|
||||
|
||||
ctx.fillStyle = `rgba(240,247,255,${alpha})`;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x2,y2,Math.max(0.7,p.size*0.55),0,Math.PI*2);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
function frame(now){
|
||||
const perf = cfg.perf;
|
||||
if(perf !== lastPerf){
|
||||
lastPerf = perf;
|
||||
resize();
|
||||
}
|
||||
|
||||
const fps = cfg.fps();
|
||||
if(fps <= 0){ requestAnimationFrame(frame); return; }
|
||||
|
||||
const dt = Math.min((now-last)/1000, 0.05);
|
||||
last = now;
|
||||
acc += dt;
|
||||
|
||||
const step = 1/fps;
|
||||
while(acc >= step){
|
||||
tick(step);
|
||||
acc -= step;
|
||||
}
|
||||
requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
window.addEventListener("mousemove",(e)=>{ mouseX=e.clientX; mouseY=e.clientY; }, {passive:true});
|
||||
window.addEventListener("resize", resize, {passive:true});
|
||||
|
||||
resize();
|
||||
mouseX = window.innerWidth*0.5; mouseY = window.innerHeight*0.5;
|
||||
requestAnimationFrame(frame);
|
||||
})();
|
||||
733
assets/style.css
Normal file
733
assets/style.css
Normal file
@@ -0,0 +1,733 @@
|
||||
:root{
|
||||
--gap: 16px;
|
||||
--radius: 18px;
|
||||
|
||||
--bg0: #050510;
|
||||
--bg1: #070a1f;
|
||||
--card0: rgba(10, 14, 40, .65);
|
||||
--card1: rgba(16, 22, 60, .55);
|
||||
--border: rgba(145, 220, 255, .18);
|
||||
|
||||
--text: rgba(240, 247, 255, .92);
|
||||
--muted: rgba(200, 220, 255, .65);
|
||||
|
||||
--neon-cyan: #42f5ff;
|
||||
--neon-blue: #5b7cff;
|
||||
--neon-pink: #ff3df2;
|
||||
--neon-green: #3dffb5;
|
||||
--neon-warn: #ffd34d;
|
||||
|
||||
--danger: #ff3b6a;
|
||||
--shadow: 0 12px 40px rgba(0,0,0,.45);
|
||||
--glow-cyan: 0 0 18px rgba(66,245,255,.35), 0 0 46px rgba(66,245,255,.18);
|
||||
--glow-pink: 0 0 18px rgba(255,61,242,.28), 0 0 46px rgba(255,61,242,.16);
|
||||
|
||||
--sidebarW: 320px;
|
||||
|
||||
--font-ui: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
|
||||
--font-sci: "Orbitron", system-ui, sans-serif;
|
||||
}
|
||||
|
||||
/* Performance profiles */
|
||||
html[data-perf="low"] .starfield{ display:none; }
|
||||
html[data-perf="low"] .space-bg::after{ display:none; } /* planet off in low */
|
||||
|
||||
*{ box-sizing: border-box; }
|
||||
html, body{ height: 100%; }
|
||||
body{
|
||||
margin: 0;
|
||||
color: var(--text);
|
||||
font-family: var(--font-ui);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
background:
|
||||
radial-gradient(1200px 800px at 20% 10%, rgba(91,124,255,.18), transparent 65%),
|
||||
radial-gradient(900px 700px at 80% 0%, rgba(255,61,242,.12), transparent 60%),
|
||||
radial-gradient(900px 800px at 60% 110%, rgba(66,245,255,.10), transparent 60%),
|
||||
linear-gradient(180deg, var(--bg0), var(--bg1));
|
||||
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Starfield Canvas */
|
||||
.starfield{
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
opacity: .98;
|
||||
}
|
||||
|
||||
/* Nebula/Planet Overlay */
|
||||
.space-bg{
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
.space-bg::before{
|
||||
content:"";
|
||||
position:absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(900px 600px at 20% 15%, rgba(91,124,255,.12), transparent 60%),
|
||||
radial-gradient(700px 500px at 80% 10%, rgba(255,61,242,.10), transparent 55%),
|
||||
radial-gradient(900px 700px at 60% 110%, rgba(66,245,255,.08), transparent 60%);
|
||||
opacity: .9;
|
||||
}
|
||||
.space-bg::after{
|
||||
content:"";
|
||||
position:absolute;
|
||||
width: 420px;
|
||||
height: 420px;
|
||||
left: 72%;
|
||||
top: 55%;
|
||||
border-radius: 50%;
|
||||
background:
|
||||
radial-gradient(closest-side at 30% 30%, rgba(255,255,255,.20), transparent 55%),
|
||||
radial-gradient(closest-side at 55% 60%, rgba(66,245,255,.16), transparent 62%),
|
||||
radial-gradient(closest-side at 70% 75%, rgba(0,0,0,.50), rgba(0,0,0,.80) 70%),
|
||||
radial-gradient(circle at 40% 40%, rgba(90,140,255,.30), rgba(40,60,120,.14) 55%, rgba(0,0,0,0) 72%);
|
||||
box-shadow: 0 0 50px rgba(91,124,255,.18), 0 0 120px rgba(255,61,242,.09);
|
||||
animation: planetFloat 90s ease-in-out infinite;
|
||||
opacity: .58;
|
||||
}
|
||||
@keyframes planetFloat{
|
||||
0% { transform: translate(-40px,-20px) scale(1); }
|
||||
50% { transform: translate(30px,10px) scale(1.02); }
|
||||
100% { transform: translate(-40px,-20px) scale(1); }
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
.container{
|
||||
width: min(1280px, 100% - 32px);
|
||||
margin-inline: auto;
|
||||
padding-block: 16px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.container::before{
|
||||
content:"";
|
||||
position:absolute;
|
||||
inset:-14px;
|
||||
pointer-events:none;
|
||||
background:
|
||||
radial-gradient(1200px 700px at 50% 40%, transparent 55%, rgba(0,0,0,.55) 85%),
|
||||
linear-gradient(135deg, rgba(255,255,255,.05), transparent 35%);
|
||||
border-radius: 28px;
|
||||
opacity: .55;
|
||||
filter: blur(.2px);
|
||||
}
|
||||
|
||||
.app{
|
||||
min-height: calc(100vh - 32px);
|
||||
display:grid;
|
||||
gap: var(--gap);
|
||||
grid-template-columns: var(--sidebarW) 1fr;
|
||||
grid-template-rows: auto auto auto auto 1fr auto;
|
||||
grid-template-areas:
|
||||
"sidebar topbar"
|
||||
"sidebar alertbar"
|
||||
"sidebar subnav"
|
||||
"sidebar resourcebar"
|
||||
"sidebar content"
|
||||
"footer footer";
|
||||
}
|
||||
|
||||
.sidebar{ grid-area: sidebar; }
|
||||
.topbar{ grid-area: topbar; }
|
||||
.alertbar{ grid-area: alertbar; }
|
||||
.subnav{ grid-area: subnav; }
|
||||
.resourcebar{ grid-area: resourcebar; }
|
||||
.content{ grid-area: content; }
|
||||
.footer{ grid-area: footer; }
|
||||
|
||||
.sidebar{
|
||||
display:grid;
|
||||
gap: var(--gap);
|
||||
align-content:start;
|
||||
position: sticky;
|
||||
top: 16px;
|
||||
max-height: calc(100vh - 32px);
|
||||
overflow: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
/* Cards / Panels + Hologramm */
|
||||
.card{
|
||||
border-radius: var(--radius);
|
||||
border: 1px solid var(--border);
|
||||
background: linear-gradient(180deg, rgba(10, 14, 40, .65), rgba(16, 22, 60, .55));
|
||||
box-shadow: var(--shadow);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.panel{ padding: 14px 16px; }
|
||||
|
||||
.card::after{
|
||||
content:"";
|
||||
position:absolute;
|
||||
inset:-2px;
|
||||
border-radius: inherit;
|
||||
background:
|
||||
radial-gradient(600px 120px at 20% 0%, rgba(66,245,255,.18), transparent 70%),
|
||||
radial-gradient(500px 120px at 80% 0%, rgba(255,61,242,.12), transparent 70%);
|
||||
pointer-events:none;
|
||||
opacity:.85;
|
||||
}
|
||||
.card::before{
|
||||
content:"";
|
||||
position:absolute;
|
||||
inset:0;
|
||||
pointer-events:none;
|
||||
background:
|
||||
repeating-linear-gradient(180deg, rgba(66,245,255,.00) 0px, rgba(66,245,255,.00) 3px, rgba(66,245,255,.05) 4px);
|
||||
opacity: .22;
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
|
||||
@keyframes hudShimmer{
|
||||
0% { transform: translateX(-120%) skewX(-20deg); opacity: 0; }
|
||||
20% { opacity: .35; }
|
||||
100% { transform: translateX(120%) skewX(-20deg); opacity: 0; }
|
||||
}
|
||||
.card:hover .panel::after{
|
||||
content:"";
|
||||
position:absolute;
|
||||
top:0; left:0;
|
||||
height:100%;
|
||||
width:40%;
|
||||
background: linear-gradient(90deg, transparent, rgba(66,245,255,.18), transparent);
|
||||
filter: blur(2px);
|
||||
pointer-events:none;
|
||||
animation: hudShimmer 1.1s ease;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
.panel-title,
|
||||
.brand-title,
|
||||
.hud-title,
|
||||
h1,h2,h3,
|
||||
button,.btn,input[type="submit"], .chip, .tab,
|
||||
label,.label{
|
||||
font-family: var(--font-sci);
|
||||
letter-spacing: .6px;
|
||||
}
|
||||
.hud-title{ margin: 0 0 6px; text-transform: uppercase; }
|
||||
.h2{ margin: 0 0 10px; }
|
||||
.muted{ color: var(--muted); font-size: .95rem; }
|
||||
.tiny{ font-size: .8rem; }
|
||||
|
||||
.divider{ height: 1px; background: rgba(145,220,255,.14); margin: 14px 0; }
|
||||
.w-full{ display:block; width: 100%; text-align:center; }
|
||||
|
||||
/* Topbar */
|
||||
.topbar{ position: relative; }
|
||||
.topbar-inner{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.brand{ display:flex; align-items:center; gap: 12px; }
|
||||
.brand-dot{
|
||||
width: 12px; height: 12px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(180deg, rgba(66,245,255,.85), rgba(255,61,242,.55));
|
||||
box-shadow: var(--glow-cyan);
|
||||
}
|
||||
.top-actions{ display:flex; gap: 10px; align-items:center; flex-wrap: wrap; }
|
||||
|
||||
.iconbtn{
|
||||
border: 1px solid rgba(145,220,255,.18);
|
||||
background: rgba(0,0,0,.18);
|
||||
color: var(--text);
|
||||
border-radius: 14px;
|
||||
padding: 9px 12px;
|
||||
cursor:pointer;
|
||||
font-family: var(--font-sci);
|
||||
letter-spacing: .6px;
|
||||
}
|
||||
.iconbtn:hover{ box-shadow: var(--glow-cyan); border-color: rgba(66,245,255,.45); transform: translateY(-1px); }
|
||||
.badge{
|
||||
display:inline-block;
|
||||
margin-left: 8px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(255,61,242,.25);
|
||||
background: rgba(255,61,242,.10);
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
/* Notification panel */
|
||||
.notif{
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: calc(100% + 10px);
|
||||
width: min(360px, 100%);
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(145,220,255,.18);
|
||||
background: linear-gradient(180deg, rgba(10, 14, 40, .92), rgba(16, 22, 60, .75));
|
||||
box-shadow: var(--shadow);
|
||||
overflow: hidden;
|
||||
}
|
||||
.notif-head{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid rgba(145,220,255,.12);
|
||||
}
|
||||
.notif-list{ display:grid; }
|
||||
.notif-item{
|
||||
display:flex;
|
||||
gap: 10px;
|
||||
align-items:center;
|
||||
padding: 10px 12px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
text-align:left;
|
||||
cursor:pointer;
|
||||
}
|
||||
.notif-item:hover{ background: rgba(66,245,255,.06); }
|
||||
.notif-ico{ width: 24px; text-align:center; }
|
||||
.notif-title{ display:block; font-family: var(--font-sci); letter-spacing:.6px; }
|
||||
.notif-meta{ display:block; color: var(--muted); font-size: .82rem; margin-top: 2px; }
|
||||
|
||||
/* Subnav (Kontextmenü in der Mitte) */
|
||||
.subnav{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.subnav-tabs{ display:flex; gap: 10px; flex-wrap: wrap; }
|
||||
.tab{
|
||||
display:inline-flex;
|
||||
align-items:center;
|
||||
padding: 8px 12px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(145,220,255,.18);
|
||||
background: rgba(0,0,0,.18);
|
||||
color: var(--text);
|
||||
text-decoration:none;
|
||||
transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
|
||||
}
|
||||
.tab:hover{ transform: translateY(-1px); border-color: rgba(66,245,255,.45); box-shadow: var(--glow-cyan); }
|
||||
.tab.is-active{
|
||||
border-color: rgba(66,245,255,.55);
|
||||
box-shadow: var(--glow-cyan);
|
||||
background: linear-gradient(180deg, rgba(66,245,255,.18), rgba(91,124,255,.10));
|
||||
}
|
||||
|
||||
/* Main menu list */
|
||||
.navlist{ list-style:none; padding:0; margin: 12px 0 0; display:grid; gap: 10px; }
|
||||
.navlist a{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:flex-start;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(145,220,255,.14);
|
||||
background: rgba(0,0,0,.18);
|
||||
transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
}
|
||||
.navlist a:hover{
|
||||
transform: translateY(-1px);
|
||||
border-color: rgba(66,245,255,.35);
|
||||
box-shadow: var(--glow-cyan);
|
||||
}
|
||||
.navlist a.is-active{
|
||||
border-color: rgba(66,245,255,.55);
|
||||
box-shadow: var(--glow-cyan);
|
||||
}
|
||||
.mi{ width: 22px; text-align:center; }
|
||||
|
||||
/* Planet list */
|
||||
.planetlist{ list-style:none; padding:0; margin: 12px 0 0; display:grid; gap: 10px; }
|
||||
.planetlist a{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(145,220,255,.14);
|
||||
background: rgba(0,0,0,.18);
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
justify-content:space-between;
|
||||
}
|
||||
.planetlist a:hover{ border-color: rgba(66,245,255,.35); box-shadow: var(--glow-cyan); transform: translateY(-1px); }
|
||||
.planetlist a.is-active{ border-color: rgba(66,245,255,.55); box-shadow: var(--glow-cyan); }
|
||||
.pl-name{ flex: 1; }
|
||||
.pl-dot{
|
||||
width: 10px; height: 10px; border-radius: 999px;
|
||||
background: rgba(145,220,255,.20);
|
||||
box-shadow: 0 0 10px rgba(255,255,255,.08);
|
||||
}
|
||||
.pl-dot.on{ background: var(--neon-green); box-shadow: 0 0 18px rgba(61,255,181,.24); }
|
||||
|
||||
.navhint{
|
||||
color: rgba(200,220,255,.55);
|
||||
font-size: .8rem;
|
||||
border: 1px solid rgba(145,220,255,.16);
|
||||
border-radius: 10px;
|
||||
padding: 2px 8px;
|
||||
background: rgba(0,0,0,.18);
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
button, .btn, input[type="submit"]{
|
||||
appearance:none;
|
||||
border: 1px solid rgba(66,245,255,.28);
|
||||
background: linear-gradient(180deg, rgba(66,245,255,.18), rgba(91,124,255,.10));
|
||||
color: var(--text);
|
||||
border-radius: 14px;
|
||||
padding: 10px 14px;
|
||||
cursor:pointer;
|
||||
transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease, filter .15s ease;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
button:hover, .btn:hover, input[type="submit"]:hover{
|
||||
transform: translateY(-1px);
|
||||
border-color: rgba(66,245,255,.55);
|
||||
box-shadow: var(--glow-cyan);
|
||||
filter: brightness(1.08);
|
||||
}
|
||||
button:active, .btn:active, input[type="submit"]:active{ transform: translateY(0) scale(.99); }
|
||||
.btn-primary{
|
||||
border-color: rgba(255,61,242,.32);
|
||||
background: linear-gradient(180deg, rgba(255,61,242,.18), rgba(66,245,255,.08));
|
||||
}
|
||||
.btn-primary:hover{ box-shadow: var(--glow-pink); }
|
||||
.btn-mini{ padding: 7px 10px; border-radius: 12px; font-size: .85rem; }
|
||||
|
||||
.actions{ display:flex; gap: 12px; margin-top: 14px; flex-wrap: wrap; }
|
||||
|
||||
/* Resourcebar sticky */
|
||||
.resourcebar{
|
||||
position: sticky;
|
||||
top: 16px;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.resource-row, .footer-row{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.stats{ display:flex; gap: 12px; flex-wrap: wrap; }
|
||||
.stat{
|
||||
border: 1px solid rgba(145,220,255,.14);
|
||||
background: rgba(0,0,0,.18);
|
||||
border-radius: 14px;
|
||||
padding: 8px 12px;
|
||||
min-width: 160px;
|
||||
}
|
||||
.stat-k{ color: var(--muted); font-size: .86rem; }
|
||||
.stat-v{ font-family: var(--font-sci); letter-spacing: .6px; margin-top: 2px; }
|
||||
.dot{
|
||||
display:inline-block;
|
||||
width: 8px; height: 8px;
|
||||
border-radius: 999px;
|
||||
margin-right: 8px;
|
||||
box-shadow: 0 0 14px rgba(255,255,255,.12);
|
||||
}
|
||||
.dot-cyan{ background: var(--neon-cyan); box-shadow: var(--glow-cyan); }
|
||||
.dot-pink{ background: var(--neon-pink); box-shadow: var(--glow-pink); }
|
||||
.dot-green{ background: var(--neon-green); box-shadow: 0 0 18px rgba(61,255,181,.24); }
|
||||
.dot-warn{ background: var(--neon-warn); box-shadow: 0 0 18px rgba(255,211,77,.18); }
|
||||
.stat-bar{
|
||||
margin-top: 8px;
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background: rgba(145,220,255,.10);
|
||||
overflow: hidden;
|
||||
}
|
||||
.stat-bar > span{
|
||||
display:block;
|
||||
height: 100%;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(90deg, rgba(66,245,255,.55), rgba(255,61,242,.35));
|
||||
}
|
||||
|
||||
/* Cockpit Window */
|
||||
.cockpit{ padding-top: 10px; }
|
||||
.cockpit-hud{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 12px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(145,220,255,.16);
|
||||
background: rgba(0,0,0,.18);
|
||||
}
|
||||
.hud-left, .hud-right{
|
||||
font-family: var(--font-sci);
|
||||
letter-spacing: .7px;
|
||||
font-size: .85rem;
|
||||
color: rgba(240,247,255,.88);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.hud-pill{
|
||||
display:inline-block;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(66,245,255,.20);
|
||||
background: rgba(0,0,0,.16);
|
||||
margin-left: 8px;
|
||||
}
|
||||
.cockpit::after{
|
||||
content:"";
|
||||
position:absolute;
|
||||
inset: 10px;
|
||||
border-radius: calc(var(--radius) - 6px);
|
||||
pointer-events:none;
|
||||
border: 1px solid rgba(66,245,255,.22);
|
||||
box-shadow: 0 0 22px rgba(66,245,255,.14), inset 0 0 20px rgba(255,61,242,.06);
|
||||
}
|
||||
.cockpit::before{
|
||||
content:"";
|
||||
position:absolute;
|
||||
inset: 0;
|
||||
pointer-events:none;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(66,245,255,.35), rgba(66,245,255,0)) 0 0 / 120px 2px no-repeat,
|
||||
linear-gradient(180deg, rgba(66,245,255,.35), rgba(66,245,255,0)) 0 0 / 2px 120px no-repeat,
|
||||
linear-gradient(90deg, rgba(66,245,255,0), rgba(66,245,255,.35)) 100% 0 / 120px 2px no-repeat,
|
||||
linear-gradient(180deg, rgba(66,245,255,.35), rgba(66,245,255,0)) 100% 0 / 2px 120px no-repeat,
|
||||
linear-gradient(90deg, rgba(66,245,255,.35), rgba(66,245,255,0)) 0 100% / 120px 2px no-repeat,
|
||||
linear-gradient(180deg, rgba(66,245,255,0), rgba(66,245,255,.35)) 0 100% / 2px 120px no-repeat,
|
||||
linear-gradient(90deg, rgba(66,245,255,0), rgba(66,245,255,.35)) 100% 100% / 120px 2px no-repeat,
|
||||
linear-gradient(180deg, rgba(66,245,255,0), rgba(66,245,255,.35)) 100% 100% / 2px 120px no-repeat,
|
||||
repeating-linear-gradient(90deg, rgba(66,245,255,.04) 0px, rgba(66,245,255,.04) 1px, transparent 1px, transparent 60px),
|
||||
repeating-linear-gradient(180deg, rgba(66,245,255,.03) 0px, rgba(66,245,255,.03) 1px, transparent 1px, transparent 60px),
|
||||
radial-gradient(900px 300px at 20% 0%, rgba(255,255,255,.06), transparent 55%);
|
||||
opacity: .55;
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
|
||||
/* Timeline + Queue */
|
||||
.grid-2{ display:grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
||||
.inner.card{ background: rgba(0,0,0,.10); }
|
||||
.timeline{ display:grid; gap: 10px; margin-top: 10px; }
|
||||
.tl-item{
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(145,220,255,.14);
|
||||
background: rgba(0,0,0,.18);
|
||||
}
|
||||
.tl-dot{ display:inline-block; width: 8px; height: 8px; border-radius: 999px; margin-right: 10px; vertical-align: middle; }
|
||||
.tl-cyan{ background: var(--neon-cyan); box-shadow: var(--glow-cyan); }
|
||||
.tl-pink{ background: var(--neon-pink); box-shadow: var(--glow-pink); }
|
||||
.tl-green{ background: var(--neon-green); box-shadow: 0 0 18px rgba(61,255,181,.24); }
|
||||
|
||||
.queue{ display:grid; gap: 12px; margin-top: 10px; }
|
||||
.q-card{
|
||||
border: 1px solid rgba(145,220,255,.14);
|
||||
background: rgba(0,0,0,.18);
|
||||
border-radius: 14px;
|
||||
padding: 12px;
|
||||
}
|
||||
.q-head{ display:flex; gap: 10px; align-items:center; }
|
||||
.q-bar{
|
||||
margin-top: 10px;
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: rgba(145,220,255,.10);
|
||||
overflow: hidden;
|
||||
}
|
||||
.q-bar > span{
|
||||
display:block;
|
||||
height: 100%;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(90deg, rgba(66,245,255,.55), rgba(255,61,242,.35));
|
||||
}
|
||||
.q-meta{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 10px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
/* Settings segments */
|
||||
.settings-row{ display:flex; gap: 14px; flex-wrap: wrap; align-items:flex-end; }
|
||||
.settings-block{ min-width: 240px; }
|
||||
.seg{ display:flex; gap: 10px; flex-wrap: wrap; }
|
||||
.seg-btn{
|
||||
border: 1px solid rgba(145,220,255,.18);
|
||||
background: rgba(0,0,0,.18);
|
||||
color: var(--text);
|
||||
border-radius: 999px;
|
||||
padding: 9px 12px;
|
||||
cursor:pointer;
|
||||
}
|
||||
.seg-btn:hover{ box-shadow: var(--glow-cyan); border-color: rgba(66,245,255,.45); transform: translateY(-1px); }
|
||||
.seg-btn.is-selected{
|
||||
border-color: rgba(66,245,255,.55);
|
||||
box-shadow: var(--glow-cyan);
|
||||
background: linear-gradient(180deg, rgba(66,245,255,.18), rgba(91,124,255,.10));
|
||||
}
|
||||
.check{ display:flex; gap: 10px; align-items:center; margin-top: 6px; }
|
||||
.check input{ accent-color: var(--neon-cyan); }
|
||||
|
||||
/* Alert pulse modes via html[data-alertpulse] */
|
||||
html[data-alertpulse="burst"] { --alert-iter: 3; }
|
||||
html[data-alertpulse="loop"] { --alert-iter: infinite; }
|
||||
|
||||
.alert.alert-pro{
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
padding: 14px 14px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(255,59,106,.42);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255,59,106,.16), rgba(0,0,0,.18)),
|
||||
repeating-linear-gradient(135deg, rgba(255,59,106,.06) 0px, rgba(255,59,106,.06) 10px, rgba(0,0,0,0) 10px, rgba(0,0,0,0) 22px);
|
||||
box-shadow: 0 0 18px rgba(255,59,106,.22), 0 0 52px rgba(255,59,106,.10);
|
||||
}
|
||||
.alert.alert-pro::before{
|
||||
content:"";
|
||||
position:absolute;
|
||||
inset:-10px;
|
||||
border-radius: 18px;
|
||||
pointer-events:none;
|
||||
background: radial-gradient(closest-side at 20% 30%, rgba(255,59,106,.30), rgba(255,59,106,.10) 45%, rgba(0,0,0,0) 70%);
|
||||
opacity: .55;
|
||||
transform: scale(1);
|
||||
animation: alertPulse 1.25s ease-in-out 0s var(--alert-iter) both;
|
||||
}
|
||||
.alert-led{
|
||||
flex: 0 0 auto;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 999px;
|
||||
background: radial-gradient(circle at 30% 30%, rgba(255,255,255,.30), rgba(255,59,106,.95));
|
||||
box-shadow: 0 0 10px rgba(255,59,106,.35), 0 0 24px rgba(255,59,106,.18);
|
||||
margin-top: 2px;
|
||||
animation: ledBreathe 1.25s ease-in-out 0s var(--alert-iter) both;
|
||||
}
|
||||
.alert-title{ text-transform: uppercase; letter-spacing: .8px; margin-bottom: 6px; }
|
||||
.alert-text{ color: rgba(240,247,255,.92); }
|
||||
|
||||
@keyframes alertPulse{
|
||||
0% { opacity: .45; transform: scale(1); }
|
||||
45% { opacity: .95; transform: scale(1.01); }
|
||||
100% { opacity: .55; transform: scale(1); }
|
||||
}
|
||||
@keyframes ledBreathe{
|
||||
0% { transform: scale(1); opacity: .75; }
|
||||
45% { transform: scale(1.2); opacity: 1; }
|
||||
100% { transform: scale(1); opacity: .85; }
|
||||
}
|
||||
|
||||
/* Toasts */
|
||||
.toast-host{
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
bottom: 16px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
z-index: 9999;
|
||||
width: min(360px, calc(100vw - 32px));
|
||||
}
|
||||
|
||||
.toast{
|
||||
display:flex;
|
||||
gap: 12px;
|
||||
align-items:flex-start;
|
||||
padding: 12px 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(145,220,255,.18);
|
||||
background: linear-gradient(180deg, rgba(10, 14, 40, .82), rgba(16, 22, 60, .65));
|
||||
box-shadow: var(--shadow);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
overflow: hidden;
|
||||
animation: toastIn .18s ease-out;
|
||||
position: relative;
|
||||
}
|
||||
.toast::before{
|
||||
content:"";
|
||||
position:absolute;
|
||||
inset:0;
|
||||
pointer-events:none;
|
||||
background: repeating-linear-gradient(180deg, rgba(66,245,255,.00) 0px, rgba(66,245,255,.00) 3px, rgba(66,245,255,.05) 4px);
|
||||
opacity: .18;
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
.toast-icon{ font-size: 18px; line-height: 1; margin-top: 2px; filter: drop-shadow(0 0 12px rgba(66,245,255,.22)); }
|
||||
.toast-title{ text-transform: uppercase; font-size: .92rem; }
|
||||
.toast-msg{ color: rgba(240,247,255,.85); font-size: .95rem; margin-top: 2px; }
|
||||
.toast-x{
|
||||
margin-left: auto;
|
||||
border: 1px solid rgba(145,220,255,.18);
|
||||
background: rgba(0,0,0,.18);
|
||||
color: var(--text);
|
||||
border-radius: 12px;
|
||||
padding: 6px 9px;
|
||||
}
|
||||
.toast-success{ border-color: rgba(61,255,181,.25); }
|
||||
.toast-error{ border-color: rgba(255,59,106,.30); }
|
||||
.toast-info{ border-color: rgba(66,245,255,.25); }
|
||||
@keyframes toastIn{ from{ transform: translateY(8px); opacity: 0; } to{ transform: translateY(0); opacity: 1; } }
|
||||
.toast.out{ animation: toastOut .18s ease-in forwards; }
|
||||
@keyframes toastOut{ to{ transform: translateY(8px); opacity: 0; } }
|
||||
|
||||
/* Scrollbar */
|
||||
.sidebar::-webkit-scrollbar{ width: 10px; }
|
||||
.sidebar::-webkit-scrollbar-track{ background: rgba(0,0,0,.12); border-radius: 999px; }
|
||||
.sidebar::-webkit-scrollbar-thumb{
|
||||
background: linear-gradient(180deg, rgba(66,245,255,.35), rgba(255,61,242,.25));
|
||||
border-radius: 999px;
|
||||
border: 2px solid rgba(0,0,0,.18);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 980px){
|
||||
:root{ --sidebarW: 1fr; }
|
||||
.app{
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto auto auto auto auto 1fr auto;
|
||||
grid-template-areas:
|
||||
"topbar"
|
||||
"alertbar"
|
||||
"subnav"
|
||||
"resourcebar"
|
||||
"content"
|
||||
"sidebar"
|
||||
"footer";
|
||||
}
|
||||
.sidebar{ position: static; max-height: none; overflow: visible; padding-right: 0; }
|
||||
.grid-2{ grid-template-columns: 1fr; }
|
||||
.resourcebar{ position: static; }
|
||||
}
|
||||
|
||||
/* Reduced motion */
|
||||
@media (prefers-reduced-motion: reduce){
|
||||
.starfield{ display:none; }
|
||||
.space-bg::after{ animation: none !important; }
|
||||
.alert.alert-pro::before, .alert-led{ animation: none !important; }
|
||||
}
|
||||
121
assets/ui.js
Normal file
121
assets/ui.js
Normal file
@@ -0,0 +1,121 @@
|
||||
(function(){
|
||||
const host = document.getElementById("toastHost");
|
||||
|
||||
function iconFor(type){
|
||||
if(type === "success") return "✅";
|
||||
if(type === "error") return "⛔";
|
||||
if(type === "info") return "🛰️";
|
||||
return "🔔";
|
||||
}
|
||||
|
||||
window.toast = function(type="info", title="Notice", message="", ttl=3200){
|
||||
if(!host) return;
|
||||
|
||||
const el = document.createElement("div");
|
||||
el.className = `toast toast-${type}`;
|
||||
el.innerHTML = `
|
||||
<div class="toast-icon">${iconFor(type)}</div>
|
||||
<div class="toast-body">
|
||||
<div class="toast-title">${escapeHtml(title)}</div>
|
||||
<div class="toast-msg">${escapeHtml(message)}</div>
|
||||
</div>
|
||||
<button class="toast-x" aria-label="Close">✕</button>
|
||||
`;
|
||||
|
||||
const remove = () => {
|
||||
el.classList.add("out");
|
||||
el.addEventListener("animationend", () => el.remove(), { once:true });
|
||||
};
|
||||
|
||||
el.querySelector(".toast-x").addEventListener("click", remove);
|
||||
host.appendChild(el);
|
||||
if (ttl > 0) setTimeout(remove, ttl);
|
||||
};
|
||||
|
||||
function escapeHtml(s){
|
||||
return String(s ?? "")
|
||||
.replaceAll("&","&")
|
||||
.replaceAll("<","<")
|
||||
.replaceAll(">",">")
|
||||
.replaceAll('"',""")
|
||||
.replaceAll("'","'");
|
||||
}
|
||||
|
||||
// Notifications dropdown (tiny JS)
|
||||
const notif = document.getElementById("notifPanel");
|
||||
window.toggleNotif = function(){
|
||||
if(!notif) return;
|
||||
const isHidden = notif.hasAttribute("hidden");
|
||||
if(isHidden) notif.removeAttribute("hidden");
|
||||
else notif.setAttribute("hidden","");
|
||||
};
|
||||
document.addEventListener("click", (e)=>{
|
||||
if(!notif) return;
|
||||
const btn = e.target.closest(".iconbtn");
|
||||
const inside = e.target.closest("#notifPanel");
|
||||
if(inside || btn) return;
|
||||
notif.setAttribute("hidden","");
|
||||
});
|
||||
|
||||
// Settings helpers: perf + alertpulse (no cookies)
|
||||
function setStorage(key, val, remember){
|
||||
try{
|
||||
if (remember) {
|
||||
localStorage.setItem(key, val);
|
||||
sessionStorage.removeItem(key);
|
||||
} else {
|
||||
sessionStorage.setItem(key, val);
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
}catch(e){}
|
||||
}
|
||||
function applyDataset(key, val){
|
||||
document.documentElement.dataset[key] = val;
|
||||
if (key === "perf") {
|
||||
const label = document.getElementById("perfLabel");
|
||||
if(label) label.textContent = val.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
window.applyPerfFromUI = function(){
|
||||
const chosen = document.querySelector(".seg-btn.is-selected[data-perf]")?.dataset.perf;
|
||||
const remember = document.getElementById("rememberPerf")?.checked;
|
||||
const val = chosen || "auto";
|
||||
setStorage("perf", val, remember);
|
||||
applyDataset("perf", val);
|
||||
toast("success","Performance", `Profil: ${val.toUpperCase()}`, 2200);
|
||||
if (val === "low") toast("info","Hint","Low deaktiviert Starfield", 1800);
|
||||
};
|
||||
|
||||
window.applyPulseFromUI = function(){
|
||||
const chosen = document.querySelector(".seg-btn.is-selected[data-alertpulse]")?.dataset.alertpulse;
|
||||
const remember = document.getElementById("rememberPulse")?.checked;
|
||||
const val = chosen || "burst";
|
||||
setStorage("alertpulse", val, remember);
|
||||
applyDataset("alertpulse", val);
|
||||
toast("success","Alerts", `Pulse: ${val.toUpperCase()}`, 2200);
|
||||
};
|
||||
|
||||
// Segment button selection behavior
|
||||
document.addEventListener("click", (e)=>{
|
||||
const b = e.target.closest(".seg-btn");
|
||||
if(!b) return;
|
||||
const parent = b.closest(".seg");
|
||||
if(parent){
|
||||
parent.querySelectorAll(".seg-btn").forEach(x=>x.classList.remove("is-selected"));
|
||||
b.classList.add("is-selected");
|
||||
}
|
||||
});
|
||||
|
||||
// Pre-select in settings pages
|
||||
document.addEventListener("DOMContentLoaded", ()=>{
|
||||
const perf = document.documentElement.dataset.perf || "auto";
|
||||
document.querySelectorAll('.seg-btn[data-perf]').forEach(b=>{
|
||||
if(b.dataset.perf === perf) b.classList.add("is-selected");
|
||||
});
|
||||
const pulse = document.documentElement.dataset.alertpulse || "burst";
|
||||
document.querySelectorAll('.seg-btn[data-alertpulse]').forEach(b=>{
|
||||
if(b.dataset.alertpulse === pulse) b.classList.add("is-selected");
|
||||
});
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user