812 lines
41 KiB
HTML
812 lines
41 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>INVION - Preview v1</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
|
<style>
|
|
/* ===== Design Tokens ===== */
|
|
:root {
|
|
--bg: #fafaff;
|
|
--bg-subtle: #f3f2fa;
|
|
--surface: #ffffff;
|
|
--surface-hover: #f7f6fd;
|
|
--text: #0f0e1a;
|
|
--text-secondary: #6b6a80;
|
|
--text-muted: #9998ad;
|
|
--primary: #6c5ce7;
|
|
--primary-light: #a29bfe;
|
|
--primary-dark: #5041c2;
|
|
--primary-glow: rgba(108, 92, 231, 0.25);
|
|
--accent-cyan: #00cec9;
|
|
--accent-cyan-light: #55efc4;
|
|
--accent-pink: #fd79a8;
|
|
--destructive: #ff4757;
|
|
--success: #00b894;
|
|
--warning: #fdcb6e;
|
|
--border: rgba(108, 92, 231, 0.1);
|
|
--border-subtle: rgba(0,0,0,0.06);
|
|
--card-shadow: 0 1px 3px rgba(0,0,0,0.04), 0 4px 12px rgba(108,92,231,0.06);
|
|
--glow-sm: 0 0 15px rgba(108, 92, 231, 0.15);
|
|
--glow-md: 0 0 30px rgba(108, 92, 231, 0.2);
|
|
--glow-lg: 0 0 60px rgba(108, 92, 231, 0.25);
|
|
--sidebar-bg: #f5f4fc;
|
|
--sidebar-width: 256px;
|
|
--header-height: 56px;
|
|
--tabbar-height: 40px;
|
|
}
|
|
|
|
.dark {
|
|
--bg: #08070f;
|
|
--bg-subtle: #0e0d1a;
|
|
--surface: #13122a;
|
|
--surface-hover: #1a1940;
|
|
--text: #e8e6f0;
|
|
--text-secondary: #8b89a6;
|
|
--text-muted: #5c5a75;
|
|
--primary: #a29bfe;
|
|
--primary-light: #c8c4ff;
|
|
--primary-dark: #6c5ce7;
|
|
--primary-glow: rgba(162, 155, 254, 0.3);
|
|
--accent-cyan: #55efc4;
|
|
--accent-cyan-light: #7dfcd2;
|
|
--accent-pink: #fd79a8;
|
|
--destructive: #ff6b6b;
|
|
--success: #55efc4;
|
|
--warning: #ffeaa7;
|
|
--border: rgba(162, 155, 254, 0.12);
|
|
--border-subtle: rgba(255,255,255,0.05);
|
|
--card-shadow: 0 1px 3px rgba(0,0,0,0.3), 0 8px 24px rgba(0,0,0,0.4);
|
|
--glow-sm: 0 0 15px rgba(162, 155, 254, 0.15);
|
|
--glow-md: 0 0 30px rgba(162, 155, 254, 0.2);
|
|
--glow-lg: 0 0 60px rgba(162, 155, 254, 0.3);
|
|
--sidebar-bg: #0b0a16;
|
|
}
|
|
|
|
/* ===== Reset ===== */
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
html, body { height: 100%; overflow: hidden; }
|
|
body {
|
|
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
transition: background 0.6s cubic-bezier(0.4, 0, 0.2, 1), color 0.4s ease;
|
|
}
|
|
|
|
::-webkit-scrollbar { width: 6px; }
|
|
::-webkit-scrollbar-track { background: transparent; }
|
|
::-webkit-scrollbar-thumb { background: rgba(108,92,231,0.15); border-radius: 3px; }
|
|
::-webkit-scrollbar-thumb:hover { background: rgba(108,92,231,0.3); }
|
|
|
|
.screen { position: fixed; inset: 0; transition: opacity 0.6s, transform 0.6s cubic-bezier(0.4, 0, 0.2, 1); }
|
|
.screen.hidden { opacity: 0; pointer-events: none; transform: scale(1.02); }
|
|
|
|
/* ============================================
|
|
LOGIN SCREEN
|
|
============================================ */
|
|
#login-screen {
|
|
display: flex; align-items: center; justify-content: center;
|
|
background: var(--bg); overflow: hidden;
|
|
}
|
|
|
|
.starfield { position: absolute; inset: 0; overflow: hidden; }
|
|
.star {
|
|
position: absolute; width: 2px; height: 2px; background: white; border-radius: 50%;
|
|
animation: twinkle var(--duration, 3s) ease-in-out infinite alternate;
|
|
animation-delay: var(--delay, 0s); opacity: 0;
|
|
}
|
|
.star.colored { background: var(--star-color); width: 3px; height: 3px; }
|
|
@keyframes twinkle {
|
|
0% { opacity: 0; transform: scale(0.5); }
|
|
100% { opacity: var(--max-opacity, 0.7); transform: scale(1); }
|
|
}
|
|
|
|
.nebula {
|
|
position: absolute; border-radius: 50%; filter: blur(120px); pointer-events: none;
|
|
animation: nebula-drift 12s ease-in-out infinite alternate;
|
|
}
|
|
.nebula-1 { width: 600px; height: 600px; top: -15%; right: -10%; background: radial-gradient(circle, var(--primary-glow) 0%, transparent 70%); animation-duration: 15s; }
|
|
.nebula-2 { width: 500px; height: 500px; bottom: -20%; left: -10%; background: radial-gradient(circle, rgba(0,206,201,0.15) 0%, transparent 70%); animation-duration: 12s; animation-delay: -3s; }
|
|
.nebula-3 { width: 350px; height: 350px; top: 40%; left: 45%; background: radial-gradient(circle, rgba(253,121,168,0.12) 0%, transparent 70%); animation-duration: 10s; animation-delay: -6s; }
|
|
@keyframes nebula-drift {
|
|
0% { transform: translate(0, 0) scale(1); }
|
|
100% { transform: translate(30px, -20px) scale(1.1); }
|
|
}
|
|
|
|
.particle {
|
|
position: absolute; width: var(--size, 4px); height: var(--size, 4px);
|
|
background: var(--particle-color, var(--primary)); border-radius: 50%; opacity: 0;
|
|
animation: float-up var(--float-duration, 8s) ease-in-out infinite;
|
|
animation-delay: var(--float-delay, 0s);
|
|
}
|
|
@keyframes float-up {
|
|
0% { opacity: 0; transform: translateY(100vh) scale(0); }
|
|
10% { opacity: 0.6; } 90% { opacity: 0.6; }
|
|
100% { opacity: 0; transform: translateY(-100px) scale(1); }
|
|
}
|
|
|
|
.login-container { position: relative; z-index: 10; display: flex; flex-direction: column; align-items: center; gap: 2rem; }
|
|
.login-card {
|
|
width: 420px; background: rgba(255,255,255,0.65); backdrop-filter: blur(30px) saturate(1.5);
|
|
-webkit-backdrop-filter: blur(30px) saturate(1.5);
|
|
border: 1px solid rgba(108,92,231,0.12); border-radius: 24px; padding: 3rem;
|
|
box-shadow: 0 8px 40px rgba(0,0,0,0.06), 0 0 0 1px rgba(255,255,255,0.3) inset;
|
|
animation: card-entrance 1s cubic-bezier(0.16, 1, 0.3, 1) 0.3s both;
|
|
}
|
|
.dark .login-card {
|
|
background: rgba(19,18,42,0.65); border: 1px solid rgba(162,155,254,0.15);
|
|
box-shadow: 0 8px 40px rgba(0,0,0,0.5), var(--glow-sm), 0 0 0 1px rgba(162,155,254,0.05) inset;
|
|
}
|
|
@keyframes card-entrance { from { opacity: 0; transform: translateY(40px) scale(0.96); } to { opacity: 1; transform: translateY(0) scale(1); } }
|
|
|
|
.login-logo { text-align: center; margin-bottom: 0.25rem; animation: logo-entrance 1.2s cubic-bezier(0.16, 1, 0.3, 1) 0.5s both; }
|
|
.login-logo h1 {
|
|
font-size: 2.2rem; font-weight: 900; letter-spacing: -0.04em;
|
|
background: linear-gradient(135deg, var(--primary) 0%, var(--accent-cyan) 60%, var(--accent-pink) 100%);
|
|
background-size: 200% 200%; -webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
|
background-clip: text; animation: gradient-shift 4s ease-in-out infinite;
|
|
}
|
|
@keyframes logo-entrance { from { opacity: 0; transform: translateY(-20px); filter: blur(10px); } to { opacity: 1; transform: translateY(0); filter: blur(0); } }
|
|
@keyframes gradient-shift { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
|
|
|
|
.login-subtitle { text-align: center; font-size: 0.85rem; color: var(--text-muted); margin-bottom: 2rem; animation: fade-in 0.8s ease 0.7s both; }
|
|
|
|
.form-group { margin-bottom: 1.25rem; animation: form-stagger 0.6s cubic-bezier(0.16, 1, 0.3, 1) both; }
|
|
.form-group:nth-child(1) { animation-delay: 0.8s; }
|
|
.form-group:nth-child(2) { animation-delay: 0.9s; }
|
|
@keyframes form-stagger { from { opacity: 0; transform: translateX(-20px); } to { opacity: 1; transform: translateX(0); } }
|
|
|
|
.form-label { display: block; font-size: 0.75rem; font-weight: 600; color: var(--text-secondary); margin-bottom: 0.5rem; letter-spacing: 0.02em; }
|
|
.form-input {
|
|
width: 100%; height: 48px; padding: 0 1rem; border: 1.5px solid var(--border); border-radius: 14px;
|
|
background: var(--surface); color: var(--text); font-size: 0.875rem; font-family: inherit; outline: none;
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
.form-input::placeholder { color: var(--text-muted); }
|
|
.form-input:focus { border-color: var(--primary); box-shadow: 0 0 0 4px var(--primary-glow), var(--glow-sm); }
|
|
|
|
.password-wrapper { position: relative; }
|
|
.password-toggle {
|
|
position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
|
|
background: none; border: none; color: var(--text-muted); cursor: pointer; font-size: 0.8rem; padding: 4px; transition: color 0.2s;
|
|
}
|
|
.password-toggle:hover { color: var(--primary); }
|
|
|
|
.pop-toggle {
|
|
display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem;
|
|
background: var(--bg-subtle); border-radius: 12px; margin-bottom: 1.25rem;
|
|
animation: form-stagger 0.6s cubic-bezier(0.16, 1, 0.3, 1) 1.05s both;
|
|
}
|
|
.pop-label { font-size: 0.8rem; font-weight: 500; color: var(--text-secondary); }
|
|
.toggle-switch {
|
|
width: 44px; height: 24px; background: var(--border); border-radius: 12px;
|
|
position: relative; cursor: pointer; transition: background 0.3s;
|
|
}
|
|
.toggle-switch.active { background: var(--primary); box-shadow: 0 0 12px var(--primary-glow); }
|
|
.toggle-switch::after {
|
|
content: ''; position: absolute; width: 18px; height: 18px; background: white; border-radius: 50%;
|
|
top: 3px; left: 3px; transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
|
|
}
|
|
.toggle-switch.active::after { transform: translateX(20px); }
|
|
|
|
.login-btn {
|
|
width: 100%; height: 52px; border: none; border-radius: 14px; font-size: 0.95rem; font-weight: 700;
|
|
font-family: inherit; color: white; cursor: pointer; position: relative; overflow: hidden;
|
|
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
|
|
box-shadow: 0 4px 20px var(--primary-glow);
|
|
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
animation: form-stagger 0.6s cubic-bezier(0.16, 1, 0.3, 1) 1.15s both;
|
|
}
|
|
.login-btn:hover { transform: translateY(-2px); box-shadow: var(--glow-lg), 0 8px 30px var(--primary-glow); }
|
|
.login-btn:active { transform: translateY(0) scale(0.98); }
|
|
.login-btn::before {
|
|
content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
|
|
animation: shimmer 3s ease-in-out infinite;
|
|
}
|
|
@keyframes shimmer { 0% { left: -100%; } 50% { left: 100%; } 100% { left: 100%; } }
|
|
|
|
.ripple {
|
|
position: absolute; border-radius: 50%; background: rgba(255,255,255,0.4);
|
|
transform: scale(0); animation: ripple-expand 0.6s ease-out; pointer-events: none;
|
|
}
|
|
@keyframes ripple-expand { to { transform: scale(4); opacity: 0; } }
|
|
|
|
.login-footer { text-align: center; margin-top: 1.5rem; font-size: 0.75rem; color: var(--text-muted); animation: fade-in 0.8s ease 1.3s both; }
|
|
.login-footer a { color: var(--primary); text-decoration: none; font-weight: 600; transition: color 0.2s; }
|
|
|
|
.spinner { display: inline-block; width: 20px; height: 20px; border: 2.5px solid rgba(255,255,255,0.3); border-top-color: white; border-radius: 50%; animation: spin 0.6s linear infinite; }
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
|
|
.transition-overlay {
|
|
position: fixed; inset: 0; z-index: 9999; background: var(--primary);
|
|
transform: scaleX(0); transform-origin: left; pointer-events: none;
|
|
}
|
|
.transition-overlay.active {
|
|
animation: wipe-in 0.5s cubic-bezier(0.77, 0, 0.175, 1) forwards,
|
|
wipe-out 0.5s cubic-bezier(0.77, 0, 0.175, 1) 0.5s forwards;
|
|
}
|
|
@keyframes wipe-in { to { transform: scaleX(1); } }
|
|
@keyframes wipe-out { from { transform: scaleX(1); transform-origin: right; } to { transform: scaleX(0); transform-origin: right; } }
|
|
|
|
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
|
|
/* ============================================
|
|
MAIN SCREEN — v1 Spacious Layout
|
|
============================================ */
|
|
#main-screen { display: flex; flex-direction: column; background: var(--bg); }
|
|
|
|
.main-header {
|
|
height: var(--header-height); display: flex; align-items: center; justify-content: space-between;
|
|
padding: 0 1.5rem; background: var(--surface); border-bottom: 1px solid var(--border);
|
|
position: relative; z-index: 50; backdrop-filter: blur(12px);
|
|
}
|
|
.dark .main-header { background: rgba(19,18,42,0.8); }
|
|
.header-left { display: flex; align-items: center; gap: 1.25rem; }
|
|
.header-logo {
|
|
font-size: 1.15rem; font-weight: 900; letter-spacing: -0.03em;
|
|
background: linear-gradient(135deg, var(--primary), var(--accent-cyan));
|
|
-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
|
|
}
|
|
.header-breadcrumb { font-size: 0.8rem; color: var(--text-muted); }
|
|
.header-breadcrumb span { color: var(--text); font-weight: 500; }
|
|
.header-right { display: flex; align-items: center; gap: 1rem; }
|
|
|
|
.theme-pill {
|
|
display: flex; background: var(--bg-subtle); border: 1px solid var(--border);
|
|
border-radius: 999px; padding: 3px; gap: 2px;
|
|
}
|
|
.theme-pill button {
|
|
padding: 0.35rem 1rem; border-radius: 999px; border: none; background: transparent;
|
|
color: var(--text-muted); cursor: pointer; font-size: 0.7rem; font-weight: 600;
|
|
font-family: inherit; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
.theme-pill button.active { background: var(--primary); color: white; box-shadow: var(--glow-sm); }
|
|
|
|
.notif-btn {
|
|
position: relative; width: 36px; height: 36px; border-radius: 10px; border: 1px solid var(--border);
|
|
background: var(--surface); color: var(--text-secondary); cursor: pointer;
|
|
display: flex; align-items: center; justify-content: center; font-size: 1rem; transition: all 0.2s;
|
|
}
|
|
.notif-btn:hover { border-color: var(--primary); color: var(--primary); }
|
|
.notif-dot {
|
|
position: absolute; top: 6px; right: 6px; width: 8px; height: 8px;
|
|
background: var(--destructive); border-radius: 50%; animation: pulse-dot 2s ease-in-out infinite;
|
|
}
|
|
@keyframes pulse-dot { 0%, 100% { box-shadow: 0 0 0 0 rgba(255,71,87,0.4); } 50% { box-shadow: 0 0 0 6px rgba(255,71,87,0); } }
|
|
|
|
.user-avatar {
|
|
width: 36px; height: 36px; border-radius: 50%;
|
|
background: linear-gradient(135deg, var(--primary), var(--accent-cyan));
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-size: 0.8rem; font-weight: 700; color: white; cursor: pointer;
|
|
transition: transform 0.2s, box-shadow 0.3s;
|
|
}
|
|
.user-avatar:hover { transform: scale(1.1); box-shadow: var(--glow-sm); }
|
|
|
|
.tab-bar {
|
|
height: var(--tabbar-height); display: flex; align-items: stretch;
|
|
background: var(--bg-subtle); border-bottom: 1px solid var(--border);
|
|
padding: 0 0.5rem; gap: 2px; overflow-x: auto;
|
|
}
|
|
.tab-item {
|
|
display: flex; align-items: center; gap: 0.5rem; padding: 0 1rem;
|
|
font-size: 0.75rem; font-weight: 500; color: var(--text-muted); cursor: pointer;
|
|
border-bottom: 2px solid transparent; white-space: nowrap; transition: all 0.25s;
|
|
}
|
|
.tab-item:hover { color: var(--text-secondary); background: var(--surface-hover); }
|
|
.tab-item.active { color: var(--primary); font-weight: 600; border-bottom-color: var(--primary); background: var(--surface); }
|
|
.tab-close {
|
|
width: 16px; height: 16px; border-radius: 4px; border: none; background: transparent;
|
|
color: var(--text-muted); font-size: 0.65rem; cursor: pointer;
|
|
display: flex; align-items: center; justify-content: center; opacity: 0; transition: all 0.15s;
|
|
}
|
|
.tab-item:hover .tab-close { opacity: 1; }
|
|
.tab-close:hover { background: rgba(255,71,87,0.15); color: var(--destructive); }
|
|
|
|
.main-body { display: flex; flex: 1; overflow: hidden; }
|
|
|
|
.sidebar {
|
|
width: var(--sidebar-width); background: var(--sidebar-bg); border-right: 1px solid var(--border);
|
|
padding: 1.25rem 0.75rem; overflow-y: auto; display: flex; flex-direction: column; gap: 2px;
|
|
transition: background 0.6s;
|
|
}
|
|
.sidebar-section {
|
|
font-size: 0.6rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em;
|
|
color: var(--text-muted); padding: 1.25rem 0.75rem 0.5rem;
|
|
}
|
|
.sidebar-section:first-child { padding-top: 0.25rem; }
|
|
.sidebar-item {
|
|
padding: 0.55rem 0.75rem; border-radius: 10px; font-size: 0.8rem; color: var(--text-secondary);
|
|
cursor: pointer; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); font-weight: 450;
|
|
display: flex; align-items: center; gap: 0.65rem; position: relative; overflow: hidden;
|
|
}
|
|
.sidebar-item .icon { width: 18px; height: 18px; display: flex; align-items: center; justify-content: center; font-size: 0.85rem; opacity: 0.7; flex-shrink: 0; }
|
|
.sidebar-item:hover { background: var(--surface-hover); color: var(--text); transform: translateX(2px); }
|
|
.sidebar-item.active {
|
|
background: linear-gradient(135deg, rgba(108,92,231,0.1), rgba(108,92,231,0.05));
|
|
color: var(--primary); font-weight: 600; border: 1px solid rgba(108,92,231,0.15);
|
|
}
|
|
.sidebar-item.active .icon { opacity: 1; }
|
|
.dark .sidebar-item.active { background: linear-gradient(135deg, rgba(162,155,254,0.12), rgba(162,155,254,0.05)); border-color: rgba(162,155,254,0.15); }
|
|
.sidebar-item::before {
|
|
content: ''; position: absolute; left: 0; top: 0; width: 3px; height: 100%;
|
|
background: var(--primary); border-radius: 0 2px 2px 0; transform: scaleY(0);
|
|
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
.sidebar-item.active::before { transform: scaleY(1); }
|
|
|
|
.sidebar-animate .sidebar-item { animation: slide-right 0.4s cubic-bezier(0.16, 1, 0.3, 1) both; }
|
|
@keyframes slide-right { from { opacity: 0; transform: translateX(-16px); } to { opacity: 1; transform: translateX(0); } }
|
|
|
|
/* Content */
|
|
.content-area { flex: 1; overflow-y: auto; padding: 2rem; background: var(--bg); }
|
|
|
|
.greeting { margin-bottom: 2rem; }
|
|
.greeting h2 { font-size: 1.5rem; font-weight: 800; letter-spacing: -0.03em; margin-bottom: 0.25rem; }
|
|
.greeting p { font-size: 0.85rem; color: var(--text-muted); }
|
|
|
|
.stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 2rem; }
|
|
.stat-card {
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 1.5rem;
|
|
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1); cursor: default; position: relative; overflow: hidden;
|
|
}
|
|
.stat-card::after {
|
|
content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px;
|
|
background: linear-gradient(90deg, var(--primary), var(--accent-cyan));
|
|
transform: scaleX(0); transform-origin: left; transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
.stat-card:hover { transform: translateY(-4px); box-shadow: var(--card-shadow), var(--glow-sm); }
|
|
.stat-card:hover::after { transform: scaleX(1); }
|
|
.stat-icon {
|
|
width: 40px; height: 40px; border-radius: 12px;
|
|
display: flex; align-items: center; justify-content: center; font-size: 1.1rem; margin-bottom: 1rem;
|
|
}
|
|
.stat-value {
|
|
font-size: 1.8rem; font-weight: 800; letter-spacing: -0.03em;
|
|
background: linear-gradient(135deg, var(--primary), var(--accent-cyan));
|
|
-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
|
|
}
|
|
.stat-label { font-size: 0.75rem; color: var(--text-muted); font-weight: 500; margin-top: 0.25rem; }
|
|
.stat-change { font-size: 0.7rem; font-weight: 600; margin-top: 0.5rem; }
|
|
.stat-change.up { color: var(--success); }
|
|
.stat-change.down { color: var(--destructive); }
|
|
|
|
/* Quick Access */
|
|
.quick-section { margin-bottom: 2rem; }
|
|
.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; }
|
|
.section-title { font-size: 1rem; font-weight: 700; letter-spacing: -0.01em; }
|
|
.section-link { font-size: 0.75rem; color: var(--primary); font-weight: 600; cursor: pointer; text-decoration: none; transition: opacity 0.2s; }
|
|
.section-link:hover { opacity: 0.7; }
|
|
|
|
.quick-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 0.75rem; }
|
|
.quick-card {
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: 16px;
|
|
padding: 1.25rem; text-align: center; cursor: pointer;
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); position: relative; overflow: hidden;
|
|
}
|
|
.quick-card:hover { transform: translateY(-3px) scale(1.02); box-shadow: var(--card-shadow); }
|
|
.quick-icon {
|
|
width: 44px; height: 44px; border-radius: 14px; margin: 0 auto 0.75rem;
|
|
display: flex; align-items: center; justify-content: center; font-size: 1.2rem;
|
|
transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
|
}
|
|
.quick-card:hover .quick-icon { transform: scale(1.15) rotate(-5deg); }
|
|
.quick-name { font-size: 0.75rem; font-weight: 600; color: var(--text); }
|
|
|
|
/* Table */
|
|
.table-section {
|
|
background: var(--surface); border: 1px solid var(--border); border-radius: 20px; overflow: hidden;
|
|
}
|
|
.table-toolbar {
|
|
display: flex; justify-content: space-between; align-items: center;
|
|
padding: 1.25rem 1.5rem; border-bottom: 1px solid var(--border);
|
|
}
|
|
.table-title { font-size: 0.95rem; font-weight: 700; }
|
|
.toolbar-actions { display: flex; gap: 0.5rem; }
|
|
.toolbar-btn {
|
|
height: 34px; padding: 0 0.85rem; border-radius: 10px; border: 1px solid var(--border);
|
|
background: var(--surface); color: var(--text-secondary); font-size: 0.75rem; font-weight: 500;
|
|
font-family: inherit; cursor: pointer; display: flex; align-items: center; gap: 0.4rem; transition: all 0.2s;
|
|
}
|
|
.toolbar-btn:hover { border-color: var(--primary); color: var(--primary); }
|
|
.toolbar-btn.primary { background: var(--primary); color: white; border-color: var(--primary); }
|
|
.toolbar-btn.primary:hover { box-shadow: var(--glow-sm); transform: translateY(-1px); }
|
|
|
|
table { width: 100%; border-collapse: collapse; font-size: 0.8rem; }
|
|
th {
|
|
text-align: left; padding: 0.85rem 1.5rem; background: var(--bg-subtle); color: var(--text-muted);
|
|
font-weight: 600; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
td { padding: 0.85rem 1.5rem; border-bottom: 1px solid var(--border-subtle); color: var(--text); transition: background 0.15s; }
|
|
tr:last-child td { border-bottom: none; }
|
|
tr:hover td { background: var(--surface-hover); }
|
|
tr { animation: row-entrance 0.4s cubic-bezier(0.16, 1, 0.3, 1) both; }
|
|
@keyframes row-entrance { from { opacity: 0; transform: translateX(-10px); } to { opacity: 1; transform: translateX(0); } }
|
|
|
|
.badge {
|
|
display: inline-flex; align-items: center; padding: 0.2rem 0.65rem;
|
|
border-radius: 999px; font-size: 0.65rem; font-weight: 600; letter-spacing: 0.01em;
|
|
}
|
|
.badge-success { background: rgba(0,184,148,0.12); color: var(--success); }
|
|
.badge-warning { background: rgba(253,203,110,0.2); color: #b8860b; }
|
|
.dark .badge-warning { color: var(--warning); background: rgba(253,203,110,0.1); }
|
|
.badge-primary { background: rgba(108,92,231,0.12); color: var(--primary); }
|
|
.badge-destructive { background: rgba(255,71,87,0.12); color: var(--destructive); }
|
|
.badge-cyan { background: rgba(0,206,201,0.12); color: var(--accent-cyan); }
|
|
|
|
.animate-in { animation: slide-up 0.5s cubic-bezier(0.16, 1, 0.3, 1) both; }
|
|
@keyframes slide-up { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
|
|
|
|
/* Toast */
|
|
.toast {
|
|
position: fixed; bottom: 2rem; right: 2rem; background: var(--surface); border: 1px solid var(--border);
|
|
border-radius: 16px; padding: 1rem 1.5rem; box-shadow: var(--card-shadow);
|
|
display: flex; align-items: center; gap: 0.75rem; z-index: 1000;
|
|
transform: translateY(120%) scale(0.9); opacity: 0;
|
|
transition: all 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
|
}
|
|
.toast.show { transform: translateY(0) scale(1); opacity: 1; }
|
|
.toast-bar { width: 4px; height: 36px; border-radius: 2px; background: var(--success); }
|
|
.toast-text { font-size: 0.8rem; font-weight: 500; }
|
|
.toast-sub { font-size: 0.7rem; color: var(--text-muted); margin-top: 0.15rem; }
|
|
|
|
.preview-banner {
|
|
position: fixed; top: 0; left: 50%; transform: translateX(-50%); z-index: 10000;
|
|
background: linear-gradient(135deg, var(--primary), var(--accent-cyan));
|
|
color: white; font-size: 0.7rem; font-weight: 600; padding: 0.3rem 1.5rem;
|
|
border-radius: 0 0 12px 12px; letter-spacing: 0.05em; box-shadow: 0 4px 15px var(--primary-glow);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="preview-banner">INVION DESIGN PREVIEW — v1</div>
|
|
<div class="transition-overlay" id="transition"></div>
|
|
|
|
<!-- ===== LOGIN ===== -->
|
|
<div class="screen" id="login-screen">
|
|
<div class="starfield" id="starfield"></div>
|
|
<div class="nebula nebula-1"></div>
|
|
<div class="nebula nebula-2"></div>
|
|
<div class="nebula nebula-3"></div>
|
|
<div id="particles"></div>
|
|
<div class="login-container">
|
|
<div class="login-card">
|
|
<div class="login-logo"><h1>INVION</h1></div>
|
|
<div class="login-subtitle">Welcome to the cosmos</div>
|
|
<div class="form-group">
|
|
<label class="form-label">User ID</label>
|
|
<input class="form-input" type="text" placeholder="Enter your ID" value="admin" id="login-id">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">Password</label>
|
|
<div class="password-wrapper">
|
|
<input class="form-input" type="password" placeholder="Enter password" value="invion2024" id="login-pw">
|
|
<button class="password-toggle" onclick="togglePassword()" id="pw-toggle">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="pop-toggle">
|
|
<span class="pop-label">POP Mode</span>
|
|
<div class="toggle-switch" onclick="this.classList.toggle('active')"></div>
|
|
</div>
|
|
<button class="login-btn" id="login-btn" onclick="handleLogin()">Sign In</button>
|
|
<div class="login-footer">Powered by <a href="#">INVION</a></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ===== MAIN ===== -->
|
|
<div class="screen hidden" id="main-screen">
|
|
<header class="main-header">
|
|
<div class="header-left">
|
|
<div class="header-logo">INVION</div>
|
|
<div class="header-breadcrumb">Dashboard <span>›</span> <span>Home</span></div>
|
|
</div>
|
|
<div class="header-right">
|
|
<div class="theme-pill">
|
|
<button class="active" onclick="setTheme('light')">Light</button>
|
|
<button onclick="setTheme('dark')">Dark</button>
|
|
</div>
|
|
<button class="notif-btn">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
|
|
<div class="notif-dot"></div>
|
|
</button>
|
|
<div class="user-avatar">P</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="tab-bar">
|
|
<div class="tab-item active"><span>Dashboard</span><button class="tab-close">×</button></div>
|
|
<div class="tab-item"><span>DTG Management</span><button class="tab-close">×</button></div>
|
|
<div class="tab-item"><span>User Management</span><button class="tab-close">×</button></div>
|
|
</div>
|
|
|
|
<div class="main-body">
|
|
<nav class="sidebar sidebar-animate" id="sidebar">
|
|
<div class="sidebar-section">Management</div>
|
|
<div class="sidebar-item active">
|
|
<span class="icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg></span>
|
|
Dashboard
|
|
</div>
|
|
<div class="sidebar-item">
|
|
<span class="icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg></span>
|
|
DTG Management
|
|
</div>
|
|
<div class="sidebar-item">
|
|
<span class="icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg></span>
|
|
Settlement
|
|
</div>
|
|
<div class="sidebar-item">
|
|
<span class="icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="3" width="15" height="13"/><polygon points="16 8 20 8 23 11 23 16 16 16 16 8"/><circle cx="5.5" cy="18.5" r="2.5"/><circle cx="18.5" cy="18.5" r="2.5"/></svg></span>
|
|
Logistics Control
|
|
</div>
|
|
<div class="sidebar-section">Configuration</div>
|
|
<div class="sidebar-item">
|
|
<span class="icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg></span>
|
|
Menu Settings
|
|
</div>
|
|
<div class="sidebar-item">
|
|
<span class="icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg></span>
|
|
User Management
|
|
</div>
|
|
<div class="sidebar-item">
|
|
<span class="icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg></span>
|
|
Screen Builder
|
|
</div>
|
|
<div class="sidebar-section">Analytics</div>
|
|
<div class="sidebar-item">
|
|
<span class="icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg></span>
|
|
Reports
|
|
</div>
|
|
<div class="sidebar-item">
|
|
<span class="icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg></span>
|
|
AI Assistant
|
|
</div>
|
|
</nav>
|
|
|
|
<main class="content-area" id="content-area">
|
|
<div class="greeting animate-in" style="animation-delay: 0.1s">
|
|
<h2>Park님, 좋은 하루 되세요</h2>
|
|
<p>2026년 3월 30일 월요일</p>
|
|
</div>
|
|
|
|
<div class="stats-grid">
|
|
<div class="stat-card animate-in" style="animation-delay: 0.15s">
|
|
<div class="stat-icon" style="background: rgba(108,92,231,0.1); color: var(--primary)">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
|
|
</div>
|
|
<div class="stat-value" id="stat-1">0</div>
|
|
<div class="stat-label">Total Devices</div>
|
|
<div class="stat-change up">+12 this month</div>
|
|
</div>
|
|
<div class="stat-card animate-in" style="animation-delay: 0.2s">
|
|
<div class="stat-icon" style="background: rgba(0,184,148,0.1); color: var(--success)">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
|
|
</div>
|
|
<div class="stat-value" id="stat-2">0%</div>
|
|
<div class="stat-label">Uptime</div>
|
|
<div class="stat-change up">+0.3% from last week</div>
|
|
</div>
|
|
<div class="stat-card animate-in" style="animation-delay: 0.25s">
|
|
<div class="stat-icon" style="background: rgba(0,206,201,0.1); color: var(--accent-cyan)">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg>
|
|
</div>
|
|
<div class="stat-value" id="stat-3">0</div>
|
|
<div class="stat-label">Active Sessions</div>
|
|
<div class="stat-change down">-8 from yesterday</div>
|
|
</div>
|
|
<div class="stat-card animate-in" style="animation-delay: 0.3s">
|
|
<div class="stat-icon" style="background: rgba(253,121,168,0.1); color: var(--accent-pink)">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
|
</div>
|
|
<div class="stat-value" id="stat-4">0ms</div>
|
|
<div class="stat-label">Avg Latency</div>
|
|
<div class="stat-change up">-3ms improved</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="quick-section animate-in" style="animation-delay: 0.35s">
|
|
<div class="section-header">
|
|
<div class="section-title">Quick Access</div>
|
|
<a class="section-link">View all →</a>
|
|
</div>
|
|
<div class="quick-grid">
|
|
<div class="quick-card">
|
|
<div class="quick-icon" style="background: rgba(108,92,231,0.1); color: var(--primary)">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="15" x2="15" y2="15"/></svg>
|
|
</div>
|
|
<div class="quick-name">Approval Box</div>
|
|
</div>
|
|
<div class="quick-card">
|
|
<div class="quick-icon" style="background: rgba(162,155,254,0.12); color: #a29bfe">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
|
</div>
|
|
<div class="quick-name">Menu Mgmt</div>
|
|
</div>
|
|
<div class="quick-card">
|
|
<div class="quick-icon" style="background: rgba(0,184,148,0.1); color: var(--success)">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
|
|
</div>
|
|
<div class="quick-name">Users</div>
|
|
</div>
|
|
<div class="quick-card">
|
|
<div class="quick-icon" style="background: rgba(253,203,110,0.15); color: #e17055">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
|
|
</div>
|
|
<div class="quick-name">Notices</div>
|
|
</div>
|
|
<div class="quick-card">
|
|
<div class="quick-icon" style="background: rgba(255,71,87,0.08); color: var(--destructive)">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
|
|
</div>
|
|
<div class="quick-name">Audit Log</div>
|
|
</div>
|
|
<div class="quick-card">
|
|
<div class="quick-icon" style="background: rgba(0,206,201,0.1); color: var(--accent-cyan)">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="9" y1="21" x2="9" y2="9"/></svg>
|
|
</div>
|
|
<div class="quick-name">Screens</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-section animate-in" style="animation-delay: 0.4s">
|
|
<div class="table-toolbar">
|
|
<div class="table-title">Recent Devices</div>
|
|
<div class="toolbar-actions">
|
|
<button class="toolbar-btn">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
|
Search
|
|
</button>
|
|
<button class="toolbar-btn">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
|
Excel
|
|
</button>
|
|
<button class="toolbar-btn primary">+ New Device</button>
|
|
</div>
|
|
</div>
|
|
<table>
|
|
<thead><tr><th>Device</th><th>Serial No.</th><th>Manufacturer</th><th>Model</th><th>Region</th><th>Status</th></tr></thead>
|
|
<tbody>
|
|
<tr style="animation-delay: 0.45s"><td style="font-weight:600">DTG-001</td><td>0004</td><td>LOOP</td><td>LDT400BS</td><td>Seoul</td><td><span class="badge badge-success">Active</span></td></tr>
|
|
<tr style="animation-delay: 0.5s"><td style="font-weight:600">DTG-002</td><td>0001</td><td>LOOP</td><td>LDT400BS</td><td>Busan</td><td><span class="badge badge-warning">Standby</span></td></tr>
|
|
<tr style="animation-delay: 0.55s"><td style="font-weight:600">DTG-003</td><td>0003</td><td>LOOP</td><td>LDT400BS</td><td>Incheon</td><td><span class="badge badge-primary">Installing</span></td></tr>
|
|
<tr style="animation-delay: 0.6s"><td style="font-weight:600">DTG-004</td><td>0005</td><td>LOOP</td><td>LDT400BS</td><td>Daegu</td><td><span class="badge badge-destructive">Offline</span></td></tr>
|
|
<tr style="animation-delay: 0.65s"><td style="font-weight:600">DTG-005</td><td>0007</td><td>LOOP</td><td>LDT500BS</td><td>Jeju</td><td><span class="badge badge-cyan">Syncing</span></td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toast" id="toast">
|
|
<div class="toast-bar"></div>
|
|
<div>
|
|
<div class="toast-text">Login successful</div>
|
|
<div class="toast-sub">Welcome back, Park</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function createStars() {
|
|
const field = document.getElementById('starfield');
|
|
const colors = ['rgba(162,155,254,0.8)', 'rgba(85,239,196,0.7)', 'rgba(253,121,168,0.7)'];
|
|
for (let i = 0; i < 120; i++) {
|
|
const s = document.createElement('div');
|
|
s.className = 'star' + (Math.random() > 0.85 ? ' colored' : '');
|
|
if (s.classList.contains('colored')) s.style.setProperty('--star-color', colors[Math.floor(Math.random() * 3)]);
|
|
s.style.left = Math.random() * 100 + '%';
|
|
s.style.top = Math.random() * 100 + '%';
|
|
s.style.setProperty('--duration', (2 + Math.random() * 5) + 's');
|
|
s.style.setProperty('--delay', (Math.random() * 5) + 's');
|
|
s.style.setProperty('--max-opacity', (0.3 + Math.random() * 0.7).toString());
|
|
field.appendChild(s);
|
|
}
|
|
}
|
|
|
|
function createParticles() {
|
|
const c = document.getElementById('particles');
|
|
const colors = ['var(--primary)', 'var(--accent-cyan)', 'var(--accent-pink)'];
|
|
for (let i = 0; i < 15; i++) {
|
|
const p = document.createElement('div');
|
|
p.className = 'particle';
|
|
p.style.left = Math.random() * 100 + '%';
|
|
p.style.setProperty('--size', (2 + Math.random() * 4) + 'px');
|
|
p.style.setProperty('--particle-color', colors[Math.floor(Math.random() * 3)]);
|
|
p.style.setProperty('--float-duration', (6 + Math.random() * 10) + 's');
|
|
p.style.setProperty('--float-delay', (Math.random() * 8) + 's');
|
|
c.appendChild(p);
|
|
}
|
|
}
|
|
|
|
function setTheme(t) {
|
|
document.documentElement.classList.toggle('dark', t === 'dark');
|
|
document.querySelectorAll('.theme-pill button').forEach(b => {
|
|
b.classList.toggle('active', (t === 'dark' && b.textContent === 'Dark') || (t === 'light' && b.textContent === 'Light'));
|
|
});
|
|
}
|
|
|
|
function togglePassword() {
|
|
const pw = document.getElementById('login-pw');
|
|
pw.type = pw.type === 'password' ? 'text' : 'password';
|
|
}
|
|
|
|
function addRipple(e, el) {
|
|
const rect = el.getBoundingClientRect();
|
|
const r = document.createElement('div');
|
|
r.className = 'ripple';
|
|
const size = Math.max(rect.width, rect.height) * 2;
|
|
r.style.width = r.style.height = size + 'px';
|
|
r.style.left = (e.clientX - rect.left - size / 2) + 'px';
|
|
r.style.top = (e.clientY - rect.top - size / 2) + 'px';
|
|
el.appendChild(r);
|
|
setTimeout(() => r.remove(), 600);
|
|
}
|
|
|
|
function countUp(id, start, end, duration, suffix) {
|
|
const el = document.getElementById(id);
|
|
const t0 = performance.now();
|
|
const isFloat = end % 1 !== 0;
|
|
function tick(now) {
|
|
const p = Math.min((now - t0) / duration, 1);
|
|
const e = 1 - Math.pow(1 - p, 3);
|
|
const v = start + (end - start) * e;
|
|
el.textContent = (isFloat ? v.toFixed(1) : Math.floor(v).toLocaleString()) + suffix;
|
|
if (p < 1) requestAnimationFrame(tick);
|
|
}
|
|
requestAnimationFrame(tick);
|
|
}
|
|
|
|
function handleLogin() {
|
|
const btn = document.getElementById('login-btn');
|
|
btn.innerHTML = '<span class="spinner"></span>';
|
|
btn.style.pointerEvents = 'none';
|
|
setTimeout(() => {
|
|
const ov = document.getElementById('transition');
|
|
ov.classList.add('active');
|
|
setTimeout(() => {
|
|
document.getElementById('login-screen').classList.add('hidden');
|
|
document.getElementById('main-screen').classList.remove('hidden');
|
|
const items = document.querySelectorAll('#sidebar .sidebar-item');
|
|
items.forEach((item, i) => { item.style.animationDelay = (0.1 + i * 0.05) + 's'; });
|
|
countUp('stat-1', 0, 1247, 1200, '');
|
|
countUp('stat-2', 0, 98.5, 1400, '%');
|
|
countUp('stat-3', 0, 342, 1000, '');
|
|
countUp('stat-4', 0, 12, 800, 'ms');
|
|
setTimeout(() => {
|
|
const toast = document.getElementById('toast');
|
|
toast.classList.add('show');
|
|
setTimeout(() => toast.classList.remove('show'), 3500);
|
|
}, 600);
|
|
}, 500);
|
|
setTimeout(() => ov.classList.remove('active'), 1200);
|
|
}, 1200);
|
|
}
|
|
|
|
document.getElementById('login-btn').addEventListener('mousedown', function(e) { addRipple(e, this); });
|
|
document.querySelectorAll('.sidebar-item').forEach(item => {
|
|
item.addEventListener('click', function() {
|
|
document.querySelectorAll('.sidebar-item').forEach(i => i.classList.remove('active'));
|
|
this.classList.add('active');
|
|
});
|
|
});
|
|
document.querySelectorAll('.tab-item').forEach(tab => {
|
|
tab.addEventListener('click', function() {
|
|
document.querySelectorAll('.tab-item').forEach(t => t.classList.remove('active'));
|
|
this.classList.add('active');
|
|
});
|
|
});
|
|
|
|
createStars();
|
|
createParticles();
|
|
if (window.matchMedia('(prefers-color-scheme: dark)').matches) setTheme('dark');
|
|
</script>
|
|
</body>
|
|
</html>
|