Files
invyone/frontend/invion-preview-v1.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>&rsaquo;</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">&times;</button></div>
<div class="tab-item"><span>DTG Management</span><button class="tab-close">&times;</button></div>
<div class="tab-item"><span>User Management</span><button class="tab-close">&times;</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 &rarr;</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>