Files
invyone/frontend/invion-preview-v3.html

590 lines
41 KiB
HTML

<!DOCTYPE html>
<html lang="ko" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>INVION - Preview v3</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<style>
: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);
--cyan: #00cec9; --cyan-glow: rgba(0,206,201,0.2);
--pink: #fd79a8; --pink-glow: rgba(253,121,168,0.2);
--red: #ff4757; --green: #00b894; --amber: #fdcb6e;
--border: rgba(108,92,231,0.08); --border-subtle: rgba(0,0,0,0.04);
--glass: rgba(255,255,255,0.6); --glass-border: rgba(108,92,231,0.1);
--glow-sm: 0 0 20px rgba(108,92,231,0.12); --glow-md: 0 0 40px rgba(108,92,231,0.18);
--nav-w: 64px;
}
.dark {
--bg: #06050e; --bg-subtle: #0c0b18; --surface: #11102a; --surface-hover: #191840;
--text: #eae8f4; --text-secondary: #8d8ba8; --text-muted: #5a587a;
--primary: #a29bfe; --primary-light: #c8c4ff; --primary-dark: #6c5ce7;
--primary-glow: rgba(162,155,254,0.25);
--cyan: #55efc4; --cyan-glow: rgba(85,239,196,0.15);
--pink: #fd79a8; --red: #ff6b6b; --green: #55efc4; --amber: #ffeaa7;
--border: rgba(162,155,254,0.1); --border-subtle: rgba(255,255,255,0.04);
--glass: rgba(17,16,42,0.55); --glass-border: rgba(162,155,254,0.12);
--glow-sm: 0 0 20px rgba(162,155,254,0.1); --glow-md: 0 0 40px rgba(162,155,254,0.15);
}
* { margin:0; padding:0; box-sizing:border-box; }
html,body { height:100%; overflow:hidden; }
body { font-family:'Inter',system-ui,sans-serif; background:var(--bg); color:var(--text); transition: background 0.5s, color 0.4s; }
::-webkit-scrollbar { width:5px; } ::-webkit-scrollbar-track { background:transparent; }
::-webkit-scrollbar-thumb { background:rgba(108,92,231,0.15); border-radius:3px; }
.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.03); }
/* ===== LOGIN (same as before) ===== */
#login-screen { display:flex; align-items:center; justify-content:center; overflow:hidden; }
.starfield { position:absolute; inset:0; }
.star { position:absolute; width:2px; height:2px; background:white; border-radius:50%;
animation:twinkle var(--d,3s) ease-in-out infinite alternate; animation-delay:var(--dl,0s); opacity:0; }
.star.c { width:3px; height:3px; background:var(--sc); }
@keyframes twinkle { 0%{opacity:0;transform:scale(.5)} 100%{opacity:var(--mo,.7);transform:scale(1)} }
.nebula { position:absolute; border-radius:50%; filter:blur(120px); pointer-events:none; animation:drift 14s ease-in-out infinite alternate; }
.n1 { width:600px;height:600px;top:-15%;right:-10%;background:radial-gradient(circle,var(--primary-glow),transparent 70%); }
.n2 { width:500px;height:500px;bottom:-20%;left:-10%;background:radial-gradient(circle,var(--cyan-glow),transparent 70%);animation-delay:-4s; }
.n3 { width:350px;height:350px;top:40%;left:45%;background:radial-gradient(circle,var(--pink-glow),transparent 70%);animation-delay:-7s; }
@keyframes drift { 0%{transform:translate(0,0) scale(1)} 100%{transform:translate(25px,-20px) scale(1.08)} }
.particle { position:absolute; width:var(--sz,4px); height:var(--sz,4px); background:var(--pc,var(--primary));
border-radius:50%; opacity:0; animation:floatup var(--fd,8s) ease-in-out infinite; animation-delay:var(--fdl,0s); }
@keyframes floatup { 0%{opacity:0;transform:translateY(100vh) scale(0)} 10%{opacity:.5} 90%{opacity:.5} 100%{opacity:0;transform:translateY(-80px) scale(1)} }
.login-container { position:relative; z-index:10; }
.login-card {
width:420px; backdrop-filter:blur(30px) saturate(1.5); border-radius:24px; padding:3rem;
background:var(--glass); border:1px solid var(--glass-border);
box-shadow:0 8px 40px rgba(0,0,0,0.08), inset 0 0 0 1px rgba(255,255,255,0.15);
animation:cardIn 1s cubic-bezier(.16,1,.3,1) .3s both;
}
.dark .login-card { box-shadow:0 8px 40px rgba(0,0,0,0.5), var(--glow-sm), inset 0 0 0 1px rgba(162,155,254,0.05); }
@keyframes cardIn { from{opacity:0;transform:translateY(40px) scale(.96)} to{opacity:1;transform:none} }
.login-logo { text-align:center; margin-bottom:.25rem; animation:logoIn 1.2s cubic-bezier(.16,1,.3,1) .5s both; }
.login-logo h1 { font-size:2.2rem; font-weight:900; letter-spacing:-.04em;
background:linear-gradient(135deg,var(--primary),var(--cyan) 60%,var(--pink));
background-size:200% 200%; -webkit-background-clip:text; -webkit-text-fill-color:transparent;
background-clip:text; animation:gshift 4s ease-in-out infinite; }
@keyframes logoIn { from{opacity:0;transform:translateY(-20px);filter:blur(10px)} to{opacity:1;transform:none;filter:none} }
@keyframes gshift { 0%,100%{background-position:0% 50%} 50%{background-position:100% 50%} }
.login-sub { text-align:center; font-size:.85rem; color:var(--text-muted); margin-bottom:2rem; animation:fadeIn .8s .7s both; }
.fg { margin-bottom:1.25rem; animation:fgIn .6s cubic-bezier(.16,1,.3,1) both; }
.fg:nth-child(1){animation-delay:.8s} .fg:nth-child(2){animation-delay:.9s}
@keyframes fgIn { from{opacity:0;transform:translateX(-20px)} to{opacity:1;transform:none} }
.fl { display:block; font-size:.75rem; font-weight:600; color:var(--text-secondary); margin-bottom:.5rem; }
.fi { width:100%; height:48px; padding:0 1rem; border:1.5px solid var(--border); border-radius:14px;
background:var(--surface); color:var(--text); font-size:.875rem; font-family:inherit; outline:none;
transition:all .3s cubic-bezier(.4,0,.2,1); }
.fi:focus { border-color:var(--primary); box-shadow:0 0 0 4px var(--primary-glow), var(--glow-sm); }
.pw-wrap { position:relative; }
.pw-btn { position:absolute; right:14px; top:50%; transform:translateY(-50%); background:none; border:none;
color:var(--text-muted); cursor:pointer; padding:4px; transition:color .2s; }
.pw-btn:hover { color:var(--primary); }
.pop-row { display:flex; align-items:center; justify-content:space-between; padding:.75rem 1rem;
background:var(--bg-subtle); border-radius:12px; margin-bottom:1.25rem;
animation:fgIn .6s cubic-bezier(.16,1,.3,1) 1.05s both; }
.pop-row span { font-size:.8rem; font-weight:500; color:var(--text-secondary); }
.sw { width:44px;height:24px;background:var(--border);border-radius:12px;position:relative;cursor:pointer;transition:background .3s; }
.sw.on { background:var(--primary); box-shadow:0 0 12px var(--primary-glow); }
.sw::after { content:'';position:absolute;width:18px;height:18px;background:white;border-radius:50%;
top:3px;left:3px;transition:transform .3s cubic-bezier(.68,-.55,.27,1.55);box-shadow:0 1px 3px rgba(0,0,0,.2); }
.sw.on::after { transform:translateX(20px); }
.login-btn { width:100%; height:52px; border:none; border-radius:14px; font-size:.95rem; font-weight:700;
font-family:inherit; color:white; cursor:pointer; position:relative; overflow:hidden;
background:linear-gradient(135deg,var(--primary),var(--primary-light));
box-shadow:0 4px 20px var(--primary-glow); transition:all .4s cubic-bezier(.4,0,.2,1);
animation:fgIn .6s cubic-bezier(.16,1,.3,1) 1.15s both; }
.login-btn:hover { transform:translateY(-2px); box-shadow:var(--glow-md),0 8px 30px var(--primary-glow); }
.login-btn::before { content:''; position:absolute; top:0; left:-100%; width:100%; height:100%;
background:linear-gradient(90deg,transparent,rgba(255,255,255,.2),transparent); animation:shimmer 3s ease-in-out infinite; }
@keyframes shimmer { 0%{left:-100%} 50%{left:100%} 100%{left:100%} }
.rip { position:absolute;border-radius:50%;background:rgba(255,255,255,.4);transform:scale(0);animation:ripX .6s ease-out;pointer-events:none; }
@keyframes ripX { to{transform:scale(4);opacity:0} }
.login-ft { text-align:center; margin-top:1.5rem; font-size:.75rem; color:var(--text-muted); animation:fadeIn .8s 1.3s both; }
.login-ft a { color:var(--primary); text-decoration:none; font-weight:600; }
.spinner { display:inline-block;width:20px;height:20px;border:2.5px solid rgba(255,255,255,.3);border-top-color:white;border-radius:50%;animation:spin .6s linear infinite; }
@keyframes spin { to{transform:rotate(360deg)} }
.trans-ov { position:fixed;inset:0;z-index:9999;background:var(--primary);transform:scaleX(0);transform-origin:left;pointer-events:none; }
.trans-ov.go { animation:wIn .5s cubic-bezier(.77,0,.175,1) forwards, wOut .5s cubic-bezier(.77,0,.175,1) .5s forwards; }
@keyframes wIn { to{transform:scaleX(1)} } @keyframes wOut { from{transform:scaleX(1);transform-origin:right} to{transform:scaleX(0);transform-origin:right} }
@keyframes fadeIn { from{opacity:0} to{opacity:1} }
/* ============================================
MAIN SCREEN — BENTO DASHBOARD v3
============================================ */
#main-screen { display:flex; height:100%; }
/* Icon-only nav rail */
.nav-rail {
width:var(--nav-w); background:var(--surface); border-right:1px solid var(--border);
display:flex; flex-direction:column; align-items:center; padding:1rem 0; gap:0.25rem;
position:relative; z-index:10;
}
.nav-logo {
font-size:1.1rem; font-weight:900; margin-bottom:1.5rem; width:40px; height:40px;
display:flex; align-items:center; justify-content:center; border-radius:14px;
background:linear-gradient(135deg,var(--primary),var(--cyan));
color:white; letter-spacing:-.02em;
box-shadow:0 4px 15px var(--primary-glow);
transition:transform .3s, box-shadow .3s;
}
.nav-logo:hover { transform:scale(1.1); box-shadow:var(--glow-md); }
.nav-item {
width:40px; height:40px; border-radius:12px; display:flex; align-items:center; justify-content:center;
color:var(--text-muted); cursor:pointer; transition:all .2s cubic-bezier(.4,0,.2,1);
position:relative;
}
.nav-item:hover { background:var(--surface-hover); color:var(--text); }
.nav-item.active { background:linear-gradient(135deg,rgba(108,92,231,.12),rgba(108,92,231,.06));
color:var(--primary); box-shadow:var(--glow-sm); }
.dark .nav-item.active { background:linear-gradient(135deg,rgba(162,155,254,.15),rgba(162,155,254,.06)); }
.nav-item::before { content:attr(data-tip); position:absolute; left:calc(100% + 10px); top:50%; transform:translateY(-50%);
background:var(--text); color:var(--bg); font-size:.65rem; font-weight:600; padding:.25rem .6rem;
border-radius:6px; white-space:nowrap; opacity:0; pointer-events:none; transition:opacity .2s, transform .2s;
transform:translateY(-50%) translateX(-4px); }
.nav-item:hover::before { opacity:1; transform:translateY(-50%) translateX(0); }
.nav-sep { width:28px; height:1px; background:var(--border); margin:.5rem 0; }
.nav-bottom { margin-top:auto; display:flex; flex-direction:column; align-items:center; gap:.25rem; }
.nav-avatar {
width:36px; height:36px; border-radius:50%; background:linear-gradient(135deg,var(--primary),var(--cyan));
display:flex; align-items:center; justify-content:center; font-size:.7rem; font-weight:700; color:white;
cursor:pointer; transition:transform .2s, box-shadow .3s;
}
.nav-avatar:hover { transform:scale(1.1); box-shadow:var(--glow-sm); }
/* Main content */
.main-wrap { flex:1; display:flex; flex-direction:column; overflow:hidden; position:relative; }
/* Ambient glows */
.amb { position:absolute; pointer-events:none; border-radius:50%; filter:blur(150px); opacity:.4; z-index:0; }
.amb-1 { width:500px;height:500px;top:-150px;right:-100px;background:radial-gradient(circle,rgba(108,92,231,.07),transparent 70%);animation:drift 20s ease-in-out infinite alternate; }
.amb-2 { width:400px;height:400px;bottom:-100px;left:10%;background:radial-gradient(circle,rgba(0,206,201,.05),transparent 70%);animation:drift 16s ease-in-out infinite alternate-reverse; }
/* Top bar */
.topbar {
height:52px; display:flex; align-items:center; justify-content:space-between;
padding:0 1.5rem; position:relative; z-index:5; flex-shrink:0;
}
.topbar-left { display:flex; align-items:center; gap:.75rem; }
.greet { font-size:.95rem; font-weight:700; letter-spacing:-.01em; }
.greet-sub { font-size:.7rem; color:var(--text-muted); font-weight:400; margin-left:.5rem; }
.topbar-right { display:flex; align-items:center; gap:.6rem; }
.pill { display:flex; background:var(--surface); border:1px solid var(--border); border-radius:999px; padding:2px; }
.pill button { padding:.25rem .65rem; border-radius:999px; border:none; background:transparent;
color:var(--text-muted); cursor:pointer; font-size:.6rem; font-weight:600; font-family:inherit;
transition:all .3s cubic-bezier(.4,0,.2,1); }
.pill button.on { background:var(--primary); color:white; box-shadow:var(--glow-sm); }
.bell { position:relative;width:32px;height:32px;border-radius:10px;border:1px solid var(--border);
background:var(--surface);color:var(--text-muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s; }
.bell:hover { border-color:var(--primary);color:var(--primary); }
.bell-dot { position:absolute;top:5px;right:5px;width:7px;height:7px;background:var(--red);border-radius:50%;animation:pdot 2s infinite; }
@keyframes pdot { 0%,100%{box-shadow:0 0 0 0 rgba(255,71,87,.4)} 50%{box-shadow:0 0 0 5px rgba(255,71,87,0)} }
/* Bento Grid */
.bento-scroll { flex:1; overflow-y:auto; padding:0 1.5rem 1.5rem; position:relative; z-index:1; }
.bento {
display:grid;
grid-template-columns:repeat(12,1fr);
grid-auto-rows:minmax(0,1fr);
gap:.65rem;
/* 4 rows */
grid-template-rows: auto auto auto 1fr;
}
/* Card base */
.card {
background:var(--surface); border:1px solid var(--border); border-radius:18px;
padding:1rem 1.15rem; overflow:hidden; position:relative;
transition:all .35s cubic-bezier(.4,0,.2,1);
animation:cardUp .5s cubic-bezier(.16,1,.3,1) both;
}
.card:hover { transform:translateY(-2px); box-shadow:var(--glow-sm),0 8px 25px rgba(0,0,0,.06); }
.dark .card:hover { box-shadow:var(--glow-sm),0 8px 25px rgba(0,0,0,.3); }
/* Dot pattern overlay */
.card::before {
content:''; position:absolute; inset:0; opacity:0; transition:opacity .3s;
background:radial-gradient(circle at 50% 50%,var(--border) 1px,transparent 1px); background-size:16px 16px;
}
.card:hover::before { opacity:1; }
@keyframes cardUp { from{opacity:0;transform:translateY(20px)} to{opacity:1;transform:none} }
.card-head { display:flex; justify-content:space-between; align-items:center; margin-bottom:.6rem; }
.card-title { font-size:.72rem; font-weight:700; color:var(--text-secondary); }
.card-more { font-size:.58rem; color:var(--primary); font-weight:600; cursor:pointer; }
.card-more:hover { opacity:.7; }
/* Specific card placements */
.c-kpi1 { grid-column:1/4; animation-delay:.05s; }
.c-kpi2 { grid-column:4/7; animation-delay:.1s; }
.c-kpi3 { grid-column:7/10; animation-delay:.15s; }
.c-kpi4 { grid-column:10/13; animation-delay:.2s; }
.c-approvals { grid-column:1/5; animation-delay:.25s; }
.c-activity { grid-column:5/9; animation-delay:.3s; }
.c-donut { grid-column:9/13; animation-delay:.35s; }
.c-chart { grid-column:1/8; animation-delay:.4s; }
.c-shortcuts { grid-column:8/13; animation-delay:.45s; }
.c-table { grid-column:1/13; animation-delay:.5s; }
/* KPI Cards */
.kpi { display:flex; flex-direction:column; gap:.35rem; }
.kpi-top { display:flex; justify-content:space-between; align-items:center; }
.kpi-label { font-size:.65rem; font-weight:600; color:var(--text-muted); }
.kpi-badge { font-size:.52rem; font-weight:700; padding:.1rem .4rem; border-radius:999px; }
.kpi-badge.up { background:rgba(0,184,148,.12); color:var(--green); }
.kpi-badge.down { background:rgba(255,71,87,.1); color:var(--red); }
.kpi-val { font-size:1.6rem; font-weight:800; letter-spacing:-.03em;
background:linear-gradient(135deg,var(--primary),var(--cyan)); -webkit-background-clip:text;
-webkit-text-fill-color:transparent; background-clip:text; }
.kpi-spark { display:flex; align-items:flex-end; gap:2px; height:28px; margin-top:.2rem; }
.kpi-bar { flex:1; border-radius:2px; background:var(--primary); opacity:.15; transition:all .3s; min-width:3px; }
.card:hover .kpi-bar { opacity:.4; }
.kpi-bar:last-child { opacity:.6; background:var(--cyan); }
/* Approval list */
.ap-item { display:flex; align-items:center; justify-content:space-between; padding:.4rem 0;
border-bottom:1px solid var(--border-subtle); cursor:pointer; transition:background .15s; }
.ap-item:last-child { border-bottom:none; }
.ap-item:hover { background:var(--surface-hover); margin:0 -.5rem; padding:.4rem .5rem; border-radius:8px; }
.ap-left { display:flex; align-items:center; gap:.45rem; flex:1; min-width:0; }
.ap-dot { width:6px;height:6px;border-radius:50%;flex-shrink:0; }
.ap-dot.urg { background:var(--red); box-shadow:0 0 6px rgba(255,71,87,.5); animation:pdot 2s infinite; }
.ap-dot.pen { background:var(--amber); box-shadow:0 0 6px rgba(253,203,110,.4); }
.ap-dot.ok { background:var(--green); }
.ap-text { font-size:.7rem; font-weight:500; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ap-time { font-size:.58rem; color:var(--text-muted); flex-shrink:0; margin-left:.4rem; }
/* Activity */
.act-item { display:flex; gap:.45rem; padding:.35rem 0; }
.act-av { width:24px;height:24px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;
font-size:.55rem;font-weight:700;color:white; }
.act-text { font-size:.68rem; line-height:1.35; }
.act-text b { font-weight:600; }
.act-time { font-size:.55rem; color:var(--text-muted); margin-top:.1rem; }
/* Donut */
.donut-wrap { display:flex; align-items:center; gap:.75rem; }
.donut { width:90px;height:90px;border-radius:50%;position:relative;
background:conic-gradient(var(--green) 0deg 194deg,var(--amber) 194deg 248deg,var(--primary) 248deg 302deg,var(--red) 302deg 330deg,var(--cyan) 330deg 360deg);
animation:dSpin 1.2s cubic-bezier(.16,1,.3,1) both; }
@keyframes dSpin { from{transform:rotate(-90deg);opacity:0} to{transform:none;opacity:1} }
.donut-hole { position:absolute;inset:16px;border-radius:50%;background:var(--surface);
display:flex;flex-direction:column;align-items:center;justify-content:center; }
.donut-num { font-size:1.05rem;font-weight:800; }
.donut-sub { font-size:.48rem;color:var(--text-muted); }
.legend { display:flex;flex-direction:column;gap:.3rem;flex:1; }
.leg-row { display:flex;align-items:center;gap:.35rem;font-size:.62rem; }
.leg-dot { width:7px;height:7px;border-radius:2px;flex-shrink:0; }
.leg-val { margin-left:auto;font-weight:700;font-size:.65rem; }
/* Bar Chart */
.bars { display:flex; align-items:flex-end; gap:6px; height:120px; padding-top:.5rem; }
.bar-col { flex:1; display:flex; flex-direction:column; align-items:center; gap:.3rem; }
.bar-fill { width:100%; border-radius:6px 6px 2px 2px; transition:height .8s cubic-bezier(.16,1,.3,1); position:relative; }
.bar-fill:hover { filter:brightness(1.15); }
.bar-label { font-size:.5rem; color:var(--text-muted); font-weight:600; }
/* Shortcuts */
.sc-grid { display:grid; grid-template-columns:1fr 1fr; gap:.5rem; }
.sc-item {
display:flex; align-items:center; gap:.55rem; padding:.55rem .7rem; border-radius:12px;
background:var(--bg-subtle); cursor:pointer; transition:all .2s;
}
.sc-item:hover { background:var(--surface-hover); transform:translateX(3px); }
.sc-icon { width:28px;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;
font-size:.75rem;flex-shrink:0; }
.sc-text { font-size:.68rem; font-weight:600; }
.sc-sub { font-size:.55rem; color:var(--text-muted); }
/* Table */
.tbl-bar { display:flex;justify-content:space-between;align-items:center;margin-bottom:.5rem; }
.tbl-title { font-size:.8rem;font-weight:700; }
.tbl-acts { display:flex;gap:.35rem; }
.tbl-btn { height:26px;padding:0 .6rem;border-radius:8px;border:1px solid var(--border);background:var(--surface);
color:var(--text-secondary);font-size:.62rem;font-weight:500;font-family:inherit;cursor:pointer;
display:flex;align-items:center;gap:.25rem;transition:all .2s; }
.tbl-btn:hover { border-color:var(--primary);color:var(--primary); }
.tbl-btn.pr { background:var(--primary);color:white;border-color:var(--primary); }
.tbl-btn.pr:hover { box-shadow:var(--glow-sm); }
.tbl-scroll { overflow-x:auto; }
table { width:100%;border-collapse:collapse;font-size:.7rem; }
th { text-align:left;padding:.5rem .75rem;background:var(--bg-subtle);color:var(--text-muted);
font-weight:600;font-size:.58rem;text-transform:uppercase;letter-spacing:.06em;border-bottom:1px solid var(--border); }
td { padding:.45rem .75rem;border-bottom:1px solid var(--border-subtle);transition:background .15s; }
tr:hover td { background:var(--surface-hover); }
tr { animation:rowIn .4s cubic-bezier(.16,1,.3,1) both; }
@keyframes rowIn { from{opacity:0;transform:translateX(-8px)} to{opacity:1;transform:none} }
.badge { display:inline-flex;align-items:center;padding:.12rem .45rem;border-radius:999px;font-size:.55rem;font-weight:600; }
.badge-g { background:rgba(0,184,148,.12);color:var(--green); }
.badge-y { background:rgba(253,203,110,.18);color:#b8860b; } .dark .badge-y { color:var(--amber);background:rgba(253,203,110,.1); }
.badge-p { background:rgba(108,92,231,.12);color:var(--primary); }
.badge-r { background:rgba(255,71,87,.12);color:var(--red); }
.badge-c { background:rgba(0,206,201,.12);color:var(--cyan); }
.pulse { display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--green);margin-right:3px;animation:pGreen 2s infinite; }
@keyframes pGreen { 0%,100%{box-shadow:0 0 0 0 rgba(0,184,148,.4)} 50%{box-shadow:0 0 0 4px rgba(0,184,148,0)} }
/* Toast */
.toast { position:fixed;bottom:1.25rem;right:1.25rem;background:var(--surface);border:1px solid var(--border);
border-radius:14px;padding:.7rem 1.1rem;box-shadow:0 4px 20px rgba(0,0,0,.1);
display:flex;align-items:center;gap:.6rem;z-index:1000;transform:translateY(120%) scale(.9);opacity:0;
transition:all .5s cubic-bezier(.68,-.55,.27,1.55); }
.toast.show { transform:none;opacity:1; }
.toast-bar { width:3px;height:28px;border-radius:2px;background:var(--green); }
.toast-t { font-size:.72rem;font-weight:600; }
.toast-s { font-size:.62rem;color:var(--text-muted);margin-top:.05rem; }
.preview-tag { position:fixed;top:0;left:50%;transform:translateX(-50%);z-index:10000;
background:linear-gradient(135deg,var(--primary),var(--cyan));color:white;font-size:.55rem;
font-weight:700;padding:.2rem 1.2rem;border-radius:0 0 10px 10px;letter-spacing:.06em; }
</style>
</head>
<body>
<div class="preview-tag">INVION v3 PREVIEW</div>
<div class="trans-ov" id="trans"></div>
<!-- LOGIN -->
<div class="screen" id="login-screen">
<div class="starfield" id="sf"></div>
<div class="nebula n1"></div><div class="nebula n2"></div><div class="nebula n3"></div>
<div id="pts"></div>
<div class="login-container">
<div class="login-card">
<div class="login-logo"><h1>INVION</h1></div>
<div class="login-sub">Welcome to the cosmos</div>
<div class="fg"><label class="fl">User ID</label><input class="fi" value="admin"></div>
<div class="fg"><label class="fl">Password</label>
<div class="pw-wrap"><input class="fi" type="password" value="invion2024" id="pw">
<button class="pw-btn" onclick="let p=document.getElementById('pw');p.type=p.type==='password'?'text':'password'">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
</button>
</div>
</div>
<div class="pop-row"><span>POP Mode</span><div class="sw" onclick="this.classList.toggle('on')"></div></div>
<button class="login-btn" id="lbtn" onclick="doLogin()">Sign In</button>
<div class="login-ft">Powered by <a href="#">INVION</a></div>
</div>
</div>
</div>
<!-- MAIN -->
<div class="screen hidden" id="main-screen">
<div class="nav-rail" id="nav">
<div class="nav-logo">IN</div>
<div class="nav-item active" data-tip="대시보드"><svg width="18" height="18" 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></div>
<div class="nav-item" data-tip="DTG 관리"><svg width="18" height="18" 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="nav-item" data-tip="정산"><svg width="18" height="18" 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></div>
<div class="nav-item" data-tip="물류"><svg width="18" height="18" 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></div>
<div class="nav-sep"></div>
<div class="nav-item" data-tip="사용자"><svg width="18" height="18" 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="nav-item" data-tip="화면 빌더"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="9" y1="21" x2="9" y2="9"/></svg></div>
<div class="nav-item" data-tip="리포트"><svg width="18" height="18" 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></div>
<div class="nav-bottom">
<div class="nav-item" data-tip="설정"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></div>
<div class="nav-avatar">P</div>
</div>
</div>
<div class="main-wrap">
<div class="amb amb-1"></div><div class="amb amb-2"></div>
<div class="topbar">
<div class="topbar-left">
<div class="greet">안녕하세요, Park님 <span class="greet-sub">2026. 3. 30 월요일</span></div>
</div>
<div class="topbar-right">
<div class="pill">
<button onclick="setTheme('light')">Light</button>
<button class="on" onclick="setTheme('dark')">Dark</button>
</div>
<button class="bell"><svg width="14" height="14" 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 class="bell-dot"></div></button>
</div>
</div>
<div class="bento-scroll">
<div class="bento">
<!-- KPI Row -->
<div class="card c-kpi1 kpi">
<div class="kpi-top"><div class="kpi-label">전체 장비</div><div class="kpi-badge up">+12</div></div>
<div class="kpi-val" id="s1">0</div>
<div class="kpi-spark" id="sp1"></div>
</div>
<div class="card c-kpi2 kpi">
<div class="kpi-top"><div class="kpi-label">가동률</div><div class="kpi-badge up">+0.3%</div></div>
<div class="kpi-val" id="s2">0%</div>
<div class="kpi-spark" id="sp2"></div>
</div>
<div class="card c-kpi3 kpi">
<div class="kpi-top"><div class="kpi-label">활성 세션</div><div class="kpi-badge down">-8</div></div>
<div class="kpi-val" id="s3">0</div>
<div class="kpi-spark" id="sp3"></div>
</div>
<div class="card c-kpi4 kpi">
<div class="kpi-top"><div class="kpi-label">평균 지연</div><div class="kpi-badge up">-3ms</div></div>
<div class="kpi-val" id="s4">0ms</div>
<div class="kpi-spark" id="sp4"></div>
</div>
<!-- Middle Row: Approvals | Activity | Donut -->
<div class="card c-approvals">
<div class="card-head"><div class="card-title">결재함 <span style="color:var(--red);font-weight:700">3건 대기</span></div><span class="card-more">전체보기</span></div>
<div class="ap-item"><div class="ap-left"><div class="ap-dot urg"></div><div class="ap-text">DTG-078 설치 승인요청</div></div><div class="ap-time">2분 전</div></div>
<div class="ap-item"><div class="ap-left"><div class="ap-dot pen"></div><div class="ap-text">3월 정산 보고서 검토</div></div><div class="ap-time">14분 전</div></div>
<div class="ap-item"><div class="ap-left"><div class="ap-dot pen"></div><div class="ap-text">신규 사용자 계정 생성</div></div><div class="ap-time">1시간 전</div></div>
<div class="ap-item"><div class="ap-left"><div class="ap-dot ok"></div><div class="ap-text">서버 점검 일정 확인</div></div><div class="ap-time">3시간 전</div></div>
<div class="ap-item"><div class="ap-left"><div class="ap-dot ok"></div><div class="ap-text">DTG-065 펌웨어 업데이트</div></div><div class="ap-time">어제</div></div>
</div>
<div class="card c-activity">
<div class="card-head"><div class="card-title">최근 활동 <span class="pulse"></span></div><span class="card-more">전체보기</span></div>
<div class="act-item"><div class="act-av" style="background:var(--primary)"></div><div><div class="act-text"><b>김대리</b>가 DTG-003 → <b>설치중</b></div><div class="act-time">방금 전</div></div></div>
<div class="act-item"><div class="act-av" style="background:var(--cyan)"></div><div><div class="act-text"><b>이과장</b>이 정산 보고서 <b>제출</b></div><div class="act-time">5분 전</div></div></div>
<div class="act-item"><div class="act-av" style="background:var(--pink)"></div><div><div class="act-text"><b>박사원</b>이 메뉴 <b>물류현황</b> 추가</div><div class="act-time">12분 전</div></div></div>
<div class="act-item"><div class="act-av" style="background:var(--green)">S</div><div><div class="act-text"><b>System</b> 자동 백업 완료 (2.3GB)</div><div class="act-time">30분 전</div></div></div>
<div class="act-item"><div class="act-av" style="background:var(--amber)"></div><div><div class="act-text"><b>최부장</b>이 결재 <b>3건 승인</b></div><div class="act-time">1시간 전</div></div></div>
</div>
<div class="card c-donut">
<div class="card-head"><div class="card-title">장비 현황</div><span class="card-more">상세</span></div>
<div class="donut-wrap">
<div class="donut"><div class="donut-hole"><div class="donut-num" id="dtotal">0</div><div class="donut-sub">전체</div></div></div>
<div class="legend">
<div class="leg-row"><div class="leg-dot" style="background:var(--green)"></div>운행중<span class="leg-val">672</span></div>
<div class="leg-row"><div class="leg-dot" style="background:var(--amber)"></div>대기<span class="leg-val">186</span></div>
<div class="leg-row"><div class="leg-dot" style="background:var(--primary)"></div>설치중<span class="leg-val">189</span></div>
<div class="leg-row"><div class="leg-dot" style="background:var(--red)"></div>오프라인<span class="leg-val">97</span></div>
<div class="leg-row"><div class="leg-dot" style="background:var(--cyan)"></div>동기화<span class="leg-val">103</span></div>
</div>
</div>
</div>
<!-- Chart + Shortcuts -->
<div class="card c-chart">
<div class="card-head"><div class="card-title">주간 장비 등록 추이</div><span class="card-more">상세보기</span></div>
<div class="bars" id="chart"></div>
</div>
<div class="card c-shortcuts">
<div class="card-head"><div class="card-title">바로가기</div></div>
<div class="sc-grid">
<div class="sc-item"><div class="sc-icon" style="background:rgba(108,92,231,.1);color:var(--primary)"><svg width="16" height="16" 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"/></svg></div><div><div class="sc-text">결재함</div><div class="sc-sub">3건 대기</div></div></div>
<div class="sc-item"><div class="sc-icon" style="background:rgba(0,206,201,.1);color:var(--cyan)"><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"/></svg></div><div><div class="sc-text">사용자</div><div class="sc-sub">128명 활성</div></div></div>
<div class="sc-item"><div class="sc-icon" style="background:rgba(253,121,168,.1);color:var(--pink)"><svg width="16" height="16" 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"/></svg></div><div><div class="sc-text">공지사항</div><div class="sc-sub">새 글 2건</div></div></div>
<div class="sc-item"><div class="sc-icon" style="background:rgba(0,184,148,.1);color:var(--green)"><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></div><div><div class="sc-text">리포트</div><div class="sc-sub">이번 주 분석</div></div></div>
</div>
</div>
<!-- Table -->
<div class="card c-table">
<div class="tbl-bar">
<div class="tbl-title">최근 등록 장비</div>
<div class="tbl-acts">
<button class="tbl-btn"><svg width="12" height="12" 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> 검색</button>
<button class="tbl-btn">Excel</button>
<button class="tbl-btn pr">+ 장비 등록</button>
</div>
</div>
<div class="tbl-scroll">
<table>
<thead><tr><th>장비코드</th><th>시리얼</th><th>제조사</th><th>모델</th><th>지역</th><th>상태</th><th>최근 통신</th></tr></thead>
<tbody>
<tr style="animation-delay:.5s"><td style="font-weight:600">DTG-001</td><td>0004</td><td>LOOP</td><td>LDT400BS</td><td>서울</td><td><span class="badge badge-g"><span class="pulse"></span>운행중</span></td><td style="color:var(--text-muted)">방금 전</td></tr>
<tr style="animation-delay:.53s"><td style="font-weight:600">DTG-002</td><td>0001</td><td>LOOP</td><td>LDT400BS</td><td>부산</td><td><span class="badge badge-y">대기</span></td><td style="color:var(--text-muted)">3분 전</td></tr>
<tr style="animation-delay:.56s"><td style="font-weight:600">DTG-003</td><td>0003</td><td>LOOP</td><td>LDT400BS</td><td>인천</td><td><span class="badge badge-p">설치중</span></td><td style="color:var(--text-muted)">12분 전</td></tr>
<tr style="animation-delay:.59s"><td style="font-weight:600">DTG-004</td><td>0005</td><td>LOOP</td><td>LDT400BS</td><td>대구</td><td><span class="badge badge-r">오프라인</span></td><td style="color:var(--text-muted)">2시간 전</td></tr>
<tr style="animation-delay:.62s"><td style="font-weight:600">DTG-005</td><td>0007</td><td>LOOP</td><td>LDT500BS</td><td>제주</td><td><span class="badge badge-c">동기화</span></td><td style="color:var(--text-muted)">1분 전</td></tr>
<tr style="animation-delay:.65s"><td style="font-weight:600">DTG-006</td><td>0009</td><td>LOOP</td><td>LDT500BS</td><td>광주</td><td><span class="badge badge-g"><span class="pulse"></span>운행중</span></td><td style="color:var(--text-muted)">방금 전</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="toast" id="toast"><div class="toast-bar"></div><div><div class="toast-t">로그인 성공</div><div class="toast-s">Park님 환영합니다</div></div></div>
<script>
// Stars & particles
(()=>{
const sf=document.getElementById('sf'),cs=['rgba(162,155,254,.8)','rgba(85,239,196,.7)','rgba(253,121,168,.7)'];
for(let i=0;i<120;i++){const s=document.createElement('div');s.className='star'+(Math.random()>.85?' c':'');
if(s.classList.contains('c'))s.style.setProperty('--sc',cs[Math.random()*3|0]);
s.style.left=Math.random()*100+'%';s.style.top=Math.random()*100+'%';
s.style.setProperty('--d',(2+Math.random()*5)+'s');s.style.setProperty('--dl',Math.random()*5+'s');
s.style.setProperty('--mo',(0.3+Math.random()*.7)+'');sf.appendChild(s);}
const pt=document.getElementById('pts'),pc=['var(--primary)','var(--cyan)','var(--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('--sz',(2+Math.random()*4)+'px');
p.style.setProperty('--pc',pc[Math.random()*3|0]);p.style.setProperty('--fd',(6+Math.random()*10)+'s');
p.style.setProperty('--fdl',Math.random()*8+'s');pt.appendChild(p);}
})();
function setTheme(t){
document.documentElement.classList.toggle('dark',t==='dark');
document.querySelectorAll('.pill button').forEach(b=>b.classList.toggle('on',(t==='dark'&&b.textContent==='Dark')||(t==='light'&&b.textContent==='Light')));
}
function countUp(id,end,dur,sfx){
const el=document.getElementById(id),t0=performance.now(),fl=end%1!==0;
(function tick(now){const p=Math.min((now-t0)/dur,1),e=1-Math.pow(1-p,3),v=end*e;
el.textContent=(fl?v.toFixed(1):Math.floor(v).toLocaleString())+sfx;
if(p<1)requestAnimationFrame(tick);})(t0);
}
function mkSparks(){
const d={'sp1':[40,55,45,60,50,65,55,70,60,75,80,85],'sp2':[90,88,91,89,92,90,93,91,94,95,96,98],
'sp3':[60,55,70,65,50,55,45,60,50,40,45,42],'sp4':[25,20,22,18,20,15,18,14,16,13,12,12]};
for(const[id,v]of Object.entries(d)){const el=document.getElementById(id),mx=Math.max(...v);
v.forEach(val=>{const b=document.createElement('div');b.className='kpi-bar';b.style.height=(val/mx*100)+'%';el.appendChild(b);});}
}
function mkChart(){
const el=document.getElementById('chart'),
data=[{l:'월',v:42,c:'var(--primary)'},{l:'화',v:58,c:'var(--primary)'},{l:'수',v:35,c:'var(--primary)'},{l:'목',v:71,c:'var(--cyan)'},{l:'금',v:64,c:'var(--primary)'},{l:'토',v:28,c:'var(--text-muted)'},{l:'일',v:15,c:'var(--text-muted)'}],
mx=Math.max(...data.map(d=>d.v));
data.forEach((d,i)=>{
const col=document.createElement('div');col.className='bar-col';
const fill=document.createElement('div');fill.className='bar-fill';
fill.style.background=d.c;fill.style.height='0';
setTimeout(()=>{fill.style.height=(d.v/mx*100)+'%';},300+i*80);
const lbl=document.createElement('div');lbl.className='bar-label';lbl.textContent=d.l;
col.appendChild(fill);col.appendChild(lbl);el.appendChild(col);
});
}
function doLogin(){
const btn=document.getElementById('lbtn');btn.innerHTML='<span class="spinner"></span>';btn.style.pointerEvents='none';
setTimeout(()=>{
const ov=document.getElementById('trans');ov.classList.add('go');
setTimeout(()=>{
document.getElementById('login-screen').classList.add('hidden');
document.getElementById('main-screen').classList.remove('hidden');
countUp('s1',1247,1200,'');countUp('s2',98.5,1400,'%');countUp('s3',342,1000,'');countUp('s4',12,800,'ms');
countUp('dtotal',1247,1500,'');mkSparks();mkChart();
// Nav stagger
document.querySelectorAll('.nav-item').forEach((n,i)=>{n.style.animation=`cardUp .4s cubic-bezier(.16,1,.3,1) ${.1+i*.04}s both`;});
setTimeout(()=>{const t=document.getElementById('toast');t.classList.add('show');setTimeout(()=>t.classList.remove('show'),3000);},500);
},500);
setTimeout(()=>ov.classList.remove('go'),1200);
},1000);
}
document.getElementById('lbtn').addEventListener('mousedown',function(e){
const r=this.getBoundingClientRect(),d=document.createElement('div');d.className='rip';
const sz=Math.max(r.width,r.height)*2;d.style.width=d.style.height=sz+'px';
d.style.left=(e.clientX-r.left-sz/2)+'px';d.style.top=(e.clientY-r.top-sz/2)+'px';
this.appendChild(d);setTimeout(()=>d.remove(),600);
});
document.querySelectorAll('.nav-item').forEach(n=>n.addEventListener('click',function(){
document.querySelectorAll('.nav-item').forEach(i=>i.classList.remove('active'));this.classList.add('active');
}));
</script>
</body>
</html>