feat(ui): light/dark SaaS design system + redesigned sidebar & dashboard
Phase A+dashboard of the UI redesign: - variables.css: light-default tokens + [data-theme=dark] overrides; old var names aliased to theme tokens so existing markup adapts to both themes - style.css: refined components (card/btn/nav/badge/bar/table/forms), tokenized the dark-assuming rgba colors - theme toggle: pre-paint init in base.html <head> + toggleTheme() in common.js, persisted to localStorage; toggle button in sidebar - sidebar.html: labeled nav with sections (분석/파이프라인/제작), active state, account; Korean labels - dashboard.html: tokenized inline colors; verified light & dark with real data Spec: docs/superpowers/specs/2026-06-12-ui-redesign-design.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9c276789f3
commit
adb51943a3
@ -1,93 +1,28 @@
|
||||
/* @import 'variables.css'; - Loaded via HTML */
|
||||
/* variables.css loaded separately via HTML */
|
||||
|
||||
/* Reset & Basics */
|
||||
.container {
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
padding: 0 2.5rem;
|
||||
}
|
||||
/* ===== Reset & Basics ===== */
|
||||
.container { max-width: 1600px; margin: 0 auto; padding: 0 2.5rem; }
|
||||
a { text-decoration: none; color: inherit; }
|
||||
ul { list-style: none; padding: 0; margin: 0; }
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
/* ===== Utility Classes ===== */
|
||||
.flex { display: flex; }
|
||||
.flex-col { flex-direction: column; }
|
||||
.items-center { align-items: center; }
|
||||
.justify-between { justify-content: space-between; }
|
||||
.gap-2 { gap: var(--space-2); }
|
||||
.gap-4 { gap: 1rem; }
|
||||
.w-full { width: 100%; }
|
||||
.text-sm { font-size: 0.875rem; }
|
||||
.text-lg { font-size: 1.125rem; font-weight: 600; }
|
||||
.text-xl { font-size: 1.5rem; font-weight: 700; }
|
||||
.text-muted { color: var(--text-muted); }
|
||||
.font-bold { font-weight: 700; }
|
||||
.mt-4 { margin-top: 1rem; }
|
||||
.mb-4 { margin-bottom: 1rem; }
|
||||
.p-4 { padding: 1rem; }
|
||||
.p-6 { padding: 1.5rem; }
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Utility Classes */
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.gap-4 {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text-xl {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.p-4 {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.p-6 {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* ===== Utility backfill (classes already used in templates, previously undefined) ===== */
|
||||
|
||||
/* Spacing — padding */
|
||||
.p-0 { padding: 0; }
|
||||
.p-2 { padding: var(--space-2); }
|
||||
.p-3 { padding: var(--space-3); }
|
||||
@ -98,7 +33,6 @@ ul {
|
||||
.py-2 { padding-top: var(--space-2); padding-bottom: var(--space-2); }
|
||||
.pr-4 { padding-right: var(--space-4); }
|
||||
|
||||
/* Spacing — margin */
|
||||
.mt-1 { margin-top: var(--space-1); }
|
||||
.mt-2 { margin-top: var(--space-2); }
|
||||
.mb-1 { margin-bottom: var(--space-1); }
|
||||
@ -107,11 +41,9 @@ ul {
|
||||
.mb-6 { margin-bottom: var(--space-6); }
|
||||
.ml-2 { margin-left: var(--space-2); }
|
||||
|
||||
/* Spacing — gap */
|
||||
.gap-1 { gap: var(--space-1); }
|
||||
.gap-3 { gap: var(--space-3); }
|
||||
|
||||
/* Fl/grid helpers */
|
||||
.flex-wrap { flex-wrap: wrap; }
|
||||
.flex-shrink-0 { flex-shrink: 0; }
|
||||
.justify-center { justify-content: center; }
|
||||
@ -135,141 +67,96 @@ ul {
|
||||
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.text-center { text-align: center; }
|
||||
|
||||
/* Typography */
|
||||
.text-xs { font-size: 0.75rem; }
|
||||
.text-2xl { font-size: 1.875rem; font-weight: 700; }
|
||||
.font-medium { font-weight: 500; }
|
||||
.font-semibold { font-weight: 600; }
|
||||
.font-normal { font-weight: 400; }
|
||||
|
||||
/* Colors */
|
||||
.text-white { color: #fff; }
|
||||
.text-secondary { color: var(--text-secondary); }
|
||||
.text-danger { color: var(--danger); }
|
||||
.text-success { color: var(--success); }
|
||||
.text-blue-400 { color: #60a5fa; }
|
||||
.text-cyan-400 { color: #22d3ee; }
|
||||
.text-pink-400 { color: #f472b6; }
|
||||
.text-purple-400 { color: #c084fc; }
|
||||
.text-orange-400 { color: #fb923c; }
|
||||
.text-yellow-400 { color: #facc15; }
|
||||
.text-emerald-400 { color: #34d399; }
|
||||
.text-blue-400 { color: var(--accent); }
|
||||
.text-cyan-400 { color: var(--sky); }
|
||||
.text-pink-400 { color: var(--red); }
|
||||
.text-purple-400 { color: var(--purple); }
|
||||
.text-orange-400 { color: var(--amber); }
|
||||
.text-yellow-400 { color: var(--amber); }
|
||||
.text-emerald-400 { color: var(--green); }
|
||||
|
||||
/* Transitions */
|
||||
.transition-colors { transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease; }
|
||||
.transition-opacity { transition: opacity 0.2s ease; }
|
||||
.transition-all { transition: all 0.2s ease; }
|
||||
|
||||
/* Hover utilities (escaped selectors) */
|
||||
.hover\:text-white:hover { color: #fff; }
|
||||
.hover\:text-white:hover { color: var(--text); }
|
||||
.hover\:underline:hover { text-decoration: underline; }
|
||||
.hover\:text-\[var\(--primary\)\]:hover { color: var(--primary); }
|
||||
.hover\:border-\[var\(--primary\)\]:hover { border-color: var(--primary); }
|
||||
.border-\[var\(--glass-border\)\] { border: 1px solid var(--glass-border); }
|
||||
.hover\:bg-blue-400\/10:hover { background: rgba(96, 165, 250, 0.1); }
|
||||
.hover\:bg-cyan-400\/10:hover { background: rgba(34, 211, 238, 0.1); }
|
||||
.hover\:bg-pink-400\/10:hover { background: rgba(244, 114, 182, 0.1); }
|
||||
.hover\:bg-purple-400\/10:hover { background: rgba(192, 132, 252, 0.1); }
|
||||
.hover\:bg-orange-400\/10:hover { background: rgba(251, 146, 60, 0.1); }
|
||||
.hover\:bg-emerald-400\/10:hover { background: rgba(52, 211, 153, 0.1); }
|
||||
.hover\:bg-yellow-400\/10:hover { background: rgba(250, 204, 21, 0.1); }
|
||||
.border-\[var\(--glass-border\)\] { border: 1px solid var(--border); }
|
||||
.hover\:bg-blue-400\/10:hover { background: rgba(79, 124, 255, 0.10); }
|
||||
.hover\:bg-cyan-400\/10:hover { background: rgba(14, 165, 233, 0.10); }
|
||||
.hover\:bg-pink-400\/10:hover { background: rgba(244, 63, 94, 0.10); }
|
||||
.hover\:bg-purple-400\/10:hover { background: rgba(139, 92, 246, 0.10); }
|
||||
.hover\:bg-orange-400\/10:hover { background: rgba(245, 158, 11, 0.10); }
|
||||
.hover\:bg-emerald-400\/10:hover { background: rgba(16, 185, 129, 0.10); }
|
||||
.hover\:bg-yellow-400\/10:hover { background: rgba(245, 158, 11, 0.10); }
|
||||
|
||||
/* Group hover reveal */
|
||||
.group:hover .group-hover\:opacity-100 { opacity: 1; }
|
||||
|
||||
/* Spin animation */
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.animate-spin { animation: spin 1s linear infinite; }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.animate-spin { animation: none; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) { .animate-spin { animation: none; } }
|
||||
|
||||
/* Components */
|
||||
/* ===== Components ===== */
|
||||
|
||||
/* GLASS CARD */
|
||||
/* CARD — clean surface, subtle border + shadow */
|
||||
.card {
|
||||
background: var(--bg-glass);
|
||||
backdrop-filter: blur(var(--backdrop-blur));
|
||||
-webkit-backdrop-filter: blur(var(--backdrop-blur));
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r);
|
||||
padding: 1.25rem 1.4rem;
|
||||
box-shadow: var(--shadow);
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.2);
|
||||
border-color: var(--glass-highlight);
|
||||
}
|
||||
.card:hover { border-color: var(--border-strong); }
|
||||
|
||||
/* BUTTON */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.6rem 1.25rem;
|
||||
border-radius: var(--radius-md);
|
||||
gap: 0.45rem;
|
||||
padding: 0.55rem 1rem;
|
||||
border-radius: var(--r-sm);
|
||||
font-weight: 600;
|
||||
transition: all 0.2s;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.18s ease;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--primary-gradient);
|
||||
color: white;
|
||||
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 8px rgba(79, 99, 235, 0.25);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
box-shadow: 0 6px 20px rgba(37, 99, 235, 0.5);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.btn-ghost:hover {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--glass-border);
|
||||
}
|
||||
|
||||
.btn-primary:hover { filter: brightness(1.05); box-shadow: 0 4px 14px rgba(79, 99, 235, 0.35); }
|
||||
.btn-ghost { background: transparent; color: var(--text-2); }
|
||||
.btn-ghost:hover { background: var(--hover); color: var(--text); }
|
||||
.btn-secondary {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--glass-border);
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border-strong);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
.btn-secondary:hover { background: var(--surface-2); }
|
||||
.btn-outline { background: transparent; color: var(--text-2); border: 1px solid var(--border-strong); }
|
||||
.btn-outline:hover { color: var(--text); border-color: var(--primary); }
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: var(--glass-highlight);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
color: var(--text-primary);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* SIDEBAR SPECIFIC */
|
||||
:root {
|
||||
--sidebar-width: 260px;
|
||||
--sidebar-collapsed-width: 80px;
|
||||
--header-height: 64px;
|
||||
}
|
||||
/* ===== Sidebar ===== */
|
||||
:root { --sidebar-width: 244px; --sidebar-collapsed-width: 76px; --header-height: 64px; }
|
||||
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
@ -279,468 +166,211 @@ ul {
|
||||
top: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--bg-glass);
|
||||
backdrop-filter: blur(20px);
|
||||
border-right: 1px solid var(--glass-border);
|
||||
background: var(--side-bg);
|
||||
border-right: 1px solid var(--border);
|
||||
z-index: 50;
|
||||
transition: width 0.3s ease;
|
||||
overflow-x: hidden;
|
||||
/* Prevent horizontal scroll during transition */
|
||||
}
|
||||
|
||||
/* User Profile Section */
|
||||
.nav-section {
|
||||
font-size: 10.5px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .08em;
|
||||
color: var(--text-3);
|
||||
font-weight: 600;
|
||||
padding: 14px 1.25rem 6px;
|
||||
}
|
||||
|
||||
.user-profile-container {
|
||||
padding: 1rem;
|
||||
margin: 0 1rem 1rem 1rem;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--glass-border);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0) 100%);
|
||||
transition: all 0.3s ease;
|
||||
padding: 0.85rem 1rem;
|
||||
margin-top: auto;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
.user-profile-content { display: flex; align-items: center; gap: 0.7rem; overflow: hidden; }
|
||||
|
||||
.user-profile-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Toggle Button */
|
||||
.sidebar-toggle-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
width: 28px; height: 28px;
|
||||
border-radius: 6px;
|
||||
color: var(--text-muted);
|
||||
color: var(--text-3);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: 1px solid transparent;
|
||||
background: transparent;
|
||||
}
|
||||
.sidebar-toggle-btn:hover { color: var(--text); background: var(--hover); }
|
||||
|
||||
.sidebar-toggle-btn:hover {
|
||||
color: var(--text-primary);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-color: var(--glass-border);
|
||||
}
|
||||
|
||||
/* Collapsed State */
|
||||
.sidebar.collapsed {
|
||||
width: var(--sidebar-collapsed-width);
|
||||
/* Theme toggle */
|
||||
.theme-toggle {
|
||||
display: flex; align-items: center; gap: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0.55rem 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
border-radius: var(--r-sm);
|
||||
background: transparent;
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-2);
|
||||
font-size: 0.8rem; font-weight: 500;
|
||||
cursor: pointer; transition: all 0.18s ease;
|
||||
}
|
||||
.theme-toggle:hover { background: var(--hover); color: var(--text); }
|
||||
.theme-toggle .sun { display: none; }
|
||||
.theme-toggle .moon { display: inline-flex; }
|
||||
:root[data-theme="dark"] .theme-toggle .sun { display: inline-flex; }
|
||||
:root[data-theme="dark"] .theme-toggle .moon { display: none; }
|
||||
|
||||
/* Collapsed state */
|
||||
.sidebar.collapsed { width: var(--sidebar-collapsed-width); }
|
||||
.sidebar.collapsed .brand-text,
|
||||
.sidebar.collapsed .nav-text,
|
||||
.sidebar.collapsed .user-info {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Header in collapsed menu */
|
||||
.sidebar.collapsed .p-6 {
|
||||
/* Target the header div which has p-6 */
|
||||
padding: 1rem;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .sidebar-toggle-btn {
|
||||
transform: rotate(180deg);
|
||||
/* Rotate chevron */
|
||||
}
|
||||
|
||||
.sidebar.collapsed .nav-item {
|
||||
justify-content: center;
|
||||
padding: 0.85rem 0;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .nav-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .user-profile-container {
|
||||
padding: 0.75rem;
|
||||
margin: 0 0.5rem 1rem 0.5rem;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .user-profile-content {
|
||||
justify-content: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .sign-out-btn span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .sign-out-btn {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .nav-section,
|
||||
.sidebar.collapsed .user-info,
|
||||
.sidebar.collapsed .theme-toggle span { display: none; }
|
||||
.sidebar.collapsed .nav-item { justify-content: center; padding: 0.7rem 0; }
|
||||
.sidebar.collapsed .nav-icon { margin-right: 0; }
|
||||
.sidebar.collapsed .sidebar-toggle-btn { transform: rotate(180deg); }
|
||||
.sidebar.collapsed .theme-toggle { justify-content: center; }
|
||||
|
||||
/* NAV ITEM */
|
||||
.nav-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.85rem 1rem;
|
||||
border-radius: 0 var(--radius-md) var(--radius-md) 0;
|
||||
transition: all 0.2s ease;
|
||||
color: var(--text-secondary);
|
||||
border-left: 3px solid transparent;
|
||||
padding: 0.6rem 0.75rem;
|
||||
margin: 1px 0;
|
||||
border-radius: var(--r-sm);
|
||||
transition: all 0.15s ease;
|
||||
color: var(--text-2);
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
white-space: nowrap;
|
||||
/* Keep text on one line */
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
color: var(--text-primary);
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
.nav-item:hover { color: var(--text); background: var(--surface-2); }
|
||||
.nav-item.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
|
||||
.nav-item.active::before {
|
||||
content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
|
||||
width: 3px; height: 18px; border-radius: 0 3px 3px 0; background: var(--accent);
|
||||
}
|
||||
.nav-icon { margin-right: 0.7rem; color: currentColor; min-width: 18px; width: 18px; height: 18px; }
|
||||
|
||||
.nav-item.active {
|
||||
background: linear-gradient(90deg, rgba(59, 130, 246, 0.15), transparent);
|
||||
border-left: 3px solid var(--primary);
|
||||
color: var(--text-primary);
|
||||
box-shadow: 0 4px 12px -4px rgba(59, 130, 246, 0.2);
|
||||
font-weight: 600;
|
||||
}
|
||||
/* Main content offset */
|
||||
#mainContent { transition: margin-left 0.3s ease; margin-left: var(--sidebar-width); }
|
||||
body.sidebar-collapsed #mainContent { margin-left: var(--sidebar-collapsed-width); }
|
||||
|
||||
.nav-icon {
|
||||
margin-right: 0.875rem;
|
||||
color: currentColor;
|
||||
min-width: 24px;
|
||||
/* Fix icon size */
|
||||
}
|
||||
/* ===== Page header (shared) ===== */
|
||||
.page-header { display: flex; justify-content: space-between; align-items: flex-end; flex-wrap: wrap; gap: 1rem; margin-bottom: 1.5rem; }
|
||||
.page-header h1 { font-size: 1.5rem; font-weight: 700; letter-spacing: -0.4px; }
|
||||
.page-header .sub { color: var(--text-3); font-size: 0.85rem; margin-top: 0.3rem; }
|
||||
.page-header .actions { display: flex; gap: 0.6rem; align-items: center; }
|
||||
|
||||
.nav-item.active .nav-icon {
|
||||
color: var(--primary);
|
||||
filter: drop-shadow(0 0 8px var(--primary-glow));
|
||||
}
|
||||
|
||||
/* Main Content Adjustment based on sidebar */
|
||||
#mainContent {
|
||||
transition: margin-left 0.3s ease;
|
||||
margin-left: var(--sidebar-width);
|
||||
}
|
||||
|
||||
body.sidebar-collapsed #mainContent {
|
||||
margin-left: var(--sidebar-collapsed-width);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
/* ===== Responsive ===== */
|
||||
@media (max-width: 1024px) {
|
||||
.sidebar {
|
||||
width: var(--sidebar-collapsed-width);
|
||||
}
|
||||
|
||||
.sidebar .brand-text,
|
||||
.sidebar .nav-text,
|
||||
.sidebar .user-info {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar .nav-item {
|
||||
justify-content: center;
|
||||
padding: 0.85rem 0;
|
||||
}
|
||||
|
||||
.sidebar .nav-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* Auto-collapse helpers handled via JS class, but default CSS fallback */
|
||||
#mainContent {
|
||||
margin-left: var(--sidebar-collapsed-width);
|
||||
}
|
||||
.sidebar { width: var(--sidebar-collapsed-width); }
|
||||
.sidebar .brand-text, .sidebar .nav-text, .sidebar .nav-section,
|
||||
.sidebar .user-info, .sidebar .theme-toggle span { display: none; }
|
||||
.sidebar .nav-item { justify-content: center; padding: 0.7rem 0; }
|
||||
.sidebar .nav-icon { margin-right: 0; }
|
||||
#mainContent { margin-left: var(--sidebar-collapsed-width); }
|
||||
}
|
||||
|
||||
/* Mobile Responsiveness Improvements */
|
||||
@media (max-width: 768px) {
|
||||
.mobile-col { flex-direction: column !important; }
|
||||
.mobile-w-full { width: 100% !important; }
|
||||
.mobile-items-start { align-items: flex-start !important; }
|
||||
.mobile-gap-4 { gap: 1rem !important; }
|
||||
.mobile-border-0 { border: none !important; }
|
||||
.container { padding: 0 1rem; }
|
||||
|
||||
/* Layout Helpers */
|
||||
.mobile-col {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
:root { --sidebar-collapsed-width: 0px; }
|
||||
.sidebar { transform: translateX(-100%); }
|
||||
.sidebar:not(.collapsed) { transform: translateX(0); width: 100%; background: var(--surface); }
|
||||
body.sidebar-collapsed #mainContent { margin-left: 0; }
|
||||
#mainContent { margin-left: 0 !important; }
|
||||
|
||||
.mobile-w-full {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.mobile-items-start {
|
||||
align-items: flex-start !important;
|
||||
}
|
||||
|
||||
.mobile-gap-4 {
|
||||
gap: 1rem !important;
|
||||
}
|
||||
|
||||
.mobile-border-0 {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* Container adjustments */
|
||||
.container {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
/* Sidebar Overrides for Mobile */
|
||||
:root {
|
||||
--sidebar-collapsed-width: 0px;
|
||||
/* Hide completely on mobile if collapsed */
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.sidebar:not(.collapsed) {
|
||||
transform: translateX(0);
|
||||
width: 100%;
|
||||
/* Full width sidebar on mobile */
|
||||
background: rgba(10, 10, 10, 0.95);
|
||||
/* Darker opaque bg */
|
||||
}
|
||||
|
||||
body.sidebar-collapsed #mainContent {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
#mainContent {
|
||||
margin-left: 0 !important;
|
||||
/* Always 0 on mobile unless overlay logic added, but for now stack */
|
||||
}
|
||||
|
||||
/* Video Detail Specifics */
|
||||
.video-header-image {
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
aspect-ratio: 16/9;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Stats Grid */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.tabs-container {
|
||||
overflow-x: auto;
|
||||
padding-bottom: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Mobile Menu Toggle */
|
||||
.video-header-image { width: 100% !important; height: auto !important; aspect-ratio: 16/9; margin-bottom: 1rem; }
|
||||
.stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; width: 100%; }
|
||||
.tabs-container { overflow-x: auto; padding-bottom: 0.5rem; margin-bottom: 1rem; -webkit-overflow-scrolling: touch; }
|
||||
.tab-btn { white-space: nowrap; }
|
||||
.mobile-menu-btn {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-bottom: 1rem;
|
||||
background: transparent;
|
||||
color: white;
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
display: flex !important; align-items: center; justify-content: center;
|
||||
width: 40px; height: 40px; margin-bottom: 1rem;
|
||||
background: var(--surface); color: var(--text);
|
||||
border: 1px solid var(--border); border-radius: var(--radius-md); cursor: pointer;
|
||||
}
|
||||
}
|
||||
.mobile-menu-btn { display: none; }
|
||||
|
||||
/* Default hidden for desktop */
|
||||
.mobile-menu-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Mobile Responsive Tables (Card View) */
|
||||
/* Mobile responsive tables (card view) */
|
||||
@media (max-width: 768px) {
|
||||
|
||||
table,
|
||||
thead,
|
||||
tbody,
|
||||
th,
|
||||
td,
|
||||
tr {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Hide table headers (but not display:none for accessibility, usually, but here simplicity) */
|
||||
thead tr {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
left: -9999px;
|
||||
}
|
||||
|
||||
tr {
|
||||
margin-bottom: 1rem;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
td {
|
||||
border: none;
|
||||
position: relative;
|
||||
padding-left: 50% !important;
|
||||
/* Make space for label */
|
||||
padding-top: 0.5rem !important;
|
||||
padding-bottom: 0.5rem !important;
|
||||
min-height: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: right;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
td::before {
|
||||
content: attr(data-label);
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
width: 45%;
|
||||
padding-right: 10px;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Specific adjustments for certain columns if needed */
|
||||
td[data-label="Video"] {
|
||||
padding-left: 0 !important;
|
||||
display: block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td[data-label="Video"]::before {
|
||||
display: none;
|
||||
/* Hide label for video title/thumb block to give it full width */
|
||||
}
|
||||
|
||||
td[data-label="Rank"] {
|
||||
display: inline-block;
|
||||
padding: 0 !important;
|
||||
margin-bottom: 0.5rem;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
td[data-label="Rank"]::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
td[data-label="Rank"] span {
|
||||
font-size: 0.875rem !important;
|
||||
background: rgba(var(--primary-rgb), 0.1);
|
||||
/* fallback if var not ready */
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
color: var(--text-muted);
|
||||
border: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
/* Customize Rank appearance to look like a badge */
|
||||
table, thead, tbody, th, td, tr { display: block; }
|
||||
thead tr { position: absolute; top: -9999px; left: -9999px; }
|
||||
tr { margin-bottom: 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 1rem; }
|
||||
td { border: none; position: relative; padding-left: 50% !important; padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; min-height: 2rem; display: flex; align-items: center; text-align: right; justify-content: flex-end; }
|
||||
td::before { content: attr(data-label); position: absolute; left: 1rem; width: 45%; padding-right: 10px; white-space: nowrap; text-align: left; font-weight: bold; color: var(--text-muted); }
|
||||
td[data-label="Video"] { padding-left: 0 !important; display: block; text-align: left; }
|
||||
td[data-label="Video"]::before { display: none; }
|
||||
td[data-label="Rank"] { display: inline-block; padding: 0 !important; margin-bottom: 0.5rem; width: auto; }
|
||||
td[data-label="Rank"]::before { display: none; }
|
||||
td[data-label="Rank"] span { font-size: 0.875rem !important; background: var(--accent-soft); padding: 2px 8px; border-radius: 4px; color: var(--text-muted); border: 1px solid var(--border); }
|
||||
}
|
||||
|
||||
/* ===== Shared components (added) ===== */
|
||||
/* ===== Shared components ===== */
|
||||
|
||||
/* Progress bar (dashboard funnel, distributions, …) */
|
||||
.bar-track {
|
||||
flex: 1;
|
||||
height: 8px;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border-radius: var(--radius-full);
|
||||
overflow: hidden;
|
||||
}
|
||||
.bar-fill {
|
||||
height: 100%;
|
||||
border-radius: var(--radius-full);
|
||||
transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.bar-fill { transition: none; }
|
||||
}
|
||||
/* Progress bar */
|
||||
.bar-track { flex: 1; height: 8px; background: var(--inset); border-radius: var(--radius-full); overflow: hidden; }
|
||||
.bar-fill { height: 100%; border-radius: var(--radius-full); transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1); }
|
||||
@media (prefers-reduced-motion: reduce) { .bar-fill { transition: none; } }
|
||||
|
||||
/* Badge */
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
padding: 2px 8px;
|
||||
border-radius: var(--radius-full);
|
||||
border: 1px solid transparent;
|
||||
line-height: 1.4;
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
font-size: 0.7rem; font-weight: 700;
|
||||
padding: 2px 8px; border-radius: 6px;
|
||||
border: 1px solid transparent; line-height: 1.4;
|
||||
}
|
||||
.badge-muted { background: rgba(255,255,255,0.06); color: var(--text-secondary); }
|
||||
.badge-primary { background: rgba(var(--primary-rgb), 0.15); color: var(--primary); }
|
||||
.badge-success { background: rgba(var(--success-rgb), 0.15); color: var(--success); }
|
||||
.badge-warning { background: rgba(245, 158, 11, 0.15); color: var(--warning); }
|
||||
.badge-danger { background: rgba(var(--danger-rgb), 0.15); color: var(--danger); }
|
||||
.badge-muted { background: var(--surface-2); color: var(--text-secondary); }
|
||||
.badge-primary { background: var(--accent-soft); color: var(--accent); }
|
||||
.badge-success { background: rgba(var(--success-rgb), 0.14); color: var(--success); }
|
||||
.badge-warning { background: rgba(245, 158, 11, 0.14); color: var(--warning); }
|
||||
.badge-danger { background: rgba(var(--danger-rgb), 0.14); color: var(--danger); }
|
||||
|
||||
/* Skeleton shimmer */
|
||||
.skeleton {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
.skeleton { position: relative; overflow: hidden; background: var(--surface-2); border-radius: var(--radius-md); }
|
||||
.skeleton::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
transform: translateX(-100%);
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.08), transparent);
|
||||
content: ""; position: absolute; inset: 0; transform: translateX(-100%);
|
||||
background: linear-gradient(90deg, transparent, var(--hover-strong), transparent);
|
||||
animation: skeleton-shimmer 1.4s infinite;
|
||||
}
|
||||
@keyframes skeleton-shimmer { 100% { transform: translateX(100%); } }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.skeleton::after { animation: none; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) { .skeleton::after { animation: none; } }
|
||||
|
||||
/* Dark form controls (unify select/input across pages) */
|
||||
/* Form controls */
|
||||
select, input[type="text"], input[type="search"], input[type="number"], input[type="date"], input[type="datetime-local"], textarea {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: var(--r-sm);
|
||||
padding: 0.5rem 0.7rem;
|
||||
font-family: inherit;
|
||||
font-size: 0.875rem;
|
||||
transition: border-color 0.2s ease, background 0.2s ease;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
select:focus, input[type="text"]:focus, input[type="search"]:focus, input[type="number"]:focus, input[type="date"]:focus, input[type="datetime-local"]:focus, textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px var(--accent-soft);
|
||||
}
|
||||
select option { background: #0f172a; color: var(--text-primary); }
|
||||
select option { background: var(--surface); color: var(--text); }
|
||||
|
||||
/* Table polish */
|
||||
/* Table */
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
thead th {
|
||||
text-align: left;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--text-muted);
|
||||
padding: 0.6rem 0.75rem;
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
text-align: left; font-size: 0.75rem; font-weight: 600;
|
||||
text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted);
|
||||
padding: 0.65rem 0.75rem; border-bottom: 1px solid var(--border);
|
||||
}
|
||||
tbody td { padding: 0.6rem 0.75rem; border-bottom: 1px solid rgba(255, 255, 255, 0.04); }
|
||||
tbody td { padding: 0.65rem 0.75rem; border-bottom: 1px solid var(--border); }
|
||||
tbody tr { transition: background 0.15s ease; }
|
||||
tbody tr:hover { background: rgba(255, 255, 255, 0.03); }
|
||||
tbody tr:hover { background: var(--hover); }
|
||||
|
||||
@ -1,60 +1,104 @@
|
||||
/* ===========================================================================
|
||||
Design tokens — Refined SaaS, LIGHT default + DARK via [data-theme="dark"].
|
||||
Old variable names are aliased to theme tokens so existing markup adapts.
|
||||
=========================================================================== */
|
||||
:root {
|
||||
/* Premium Dark Theme Palette (Cyber/Space) */
|
||||
/* Surfaces (light) */
|
||||
--bg: #f6f7f9;
|
||||
--surface: #ffffff;
|
||||
--surface-2: #f1f3f6;
|
||||
--inset: #eef0f3;
|
||||
--border: #e6e8ec;
|
||||
--border-strong: #d9dce1;
|
||||
|
||||
/* Backgrounds */
|
||||
--bg-primary: #050508;
|
||||
/* Ultra dark, almost black */
|
||||
--bg-glass: rgba(15, 23, 42, 0.6);
|
||||
/* Text (light) */
|
||||
--text: #15181d;
|
||||
--text-2: #5a626d;
|
||||
--text-3: #9aa1ad;
|
||||
|
||||
/* Glassmorphism */
|
||||
--glass-border: rgba(255, 255, 255, 0.08);
|
||||
--glass-highlight: rgba(255, 255, 255, 0.15);
|
||||
--backdrop-blur: 12px;
|
||||
/* On-surface translucent (hover/active) */
|
||||
--hover: rgba(16, 24, 40, .045);
|
||||
--hover-strong: rgba(16, 24, 40, .07);
|
||||
|
||||
/* Typography */
|
||||
--text-primary: #f1f5f9;
|
||||
--text-secondary: #94a3b8;
|
||||
--text-muted: #64748b;
|
||||
|
||||
/* Brand Colors (Neon/Vibrant) */
|
||||
--primary: #3b82f6;
|
||||
--primary-glow: rgba(59, 130, 246, 0.5);
|
||||
--primary-gradient: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
||||
/* Elevation */
|
||||
--shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 4px 12px rgba(16, 24, 40, .05);
|
||||
--shadow-lg: 0 10px 30px rgba(16, 24, 40, .10);
|
||||
--side-bg: #ffffff;
|
||||
|
||||
/* Accent + status (shared across themes) */
|
||||
--accent: #4f7cff;
|
||||
--accent-2: #7c5cff;
|
||||
--accent-soft: rgba(79, 124, 255, .10);
|
||||
--primary: #4f7cff;
|
||||
--primary-gradient: linear-gradient(135deg, #4f7cff 0%, #7c5cff 100%);
|
||||
--primary-glow: rgba(79, 124, 255, .35);
|
||||
--success: #10b981;
|
||||
--danger: #ef4444;
|
||||
|
||||
/* UI Elements */
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 20px;
|
||||
--radius-full: 9999px;
|
||||
|
||||
/* Semantic (added) */
|
||||
--danger: #f43f5e;
|
||||
--warning: #f59e0b;
|
||||
--primary-rgb: 59, 130, 246; /* matches --primary #3b82f6; used by rgba(var(--primary-rgb), …) */
|
||||
--green: #10b981;
|
||||
--amber: #f59e0b;
|
||||
--red: #f43f5e;
|
||||
--purple: #8b5cf6;
|
||||
--sky: #0ea5e9;
|
||||
--primary-rgb: 79, 124, 255;
|
||||
--success-rgb: 16, 185, 129;
|
||||
--danger-rgb: 239, 68, 68;
|
||||
--danger-rgb: 244, 63, 94;
|
||||
|
||||
/* Spacing scale */
|
||||
--space-1: 0.25rem;
|
||||
--space-2: 0.5rem;
|
||||
--space-3: 0.75rem;
|
||||
/* === Back-compat aliases (old names → theme tokens) === */
|
||||
--bg-primary: var(--bg);
|
||||
--bg-glass: var(--surface);
|
||||
--glass-border: var(--border);
|
||||
--glass-highlight: var(--border-strong);
|
||||
--text-primary: var(--text);
|
||||
--text-secondary: var(--text-2);
|
||||
--text-muted: var(--text-3);
|
||||
--bg-hover: var(--surface-2);
|
||||
--backdrop-blur: 0px;
|
||||
|
||||
/* Radius / spacing / font */
|
||||
--radius-md: 10px;
|
||||
--radius-lg: 14px;
|
||||
--radius-full: 9999px;
|
||||
--r: 12px;
|
||||
--r-sm: 9px;
|
||||
--space-1: .25rem;
|
||||
--space-2: .5rem;
|
||||
--space-3: .75rem;
|
||||
--space-4: 1rem;
|
||||
--space-6: 1.5rem;
|
||||
--space-8: 2rem;
|
||||
|
||||
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
}
|
||||
|
||||
/* ===== Dark theme overrides ===== */
|
||||
:root[data-theme="dark"] {
|
||||
--bg: #0a0b0e;
|
||||
--surface: #131519;
|
||||
--surface-2: #191c22;
|
||||
--inset: #0e1014;
|
||||
--border: rgba(255, 255, 255, .08);
|
||||
--border-strong: rgba(255, 255, 255, .13);
|
||||
|
||||
--text: #e8eaef;
|
||||
--text-2: #9aa1ad;
|
||||
--text-3: #6b7280;
|
||||
|
||||
--hover: rgba(255, 255, 255, .05);
|
||||
--hover-strong: rgba(255, 255, 255, .09);
|
||||
|
||||
--shadow: 0 1px 2px rgba(0, 0, 0, .3);
|
||||
--shadow-lg: 0 12px 30px rgba(0, 0, 0, .45);
|
||||
--side-bg: linear-gradient(180deg, #0c0e12, #0a0b0e);
|
||||
--accent-soft: rgba(79, 124, 255, .16);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-primary);
|
||||
background-image: radial-gradient(circle at 15% 50%, rgba(56, 189, 248, 0.08), transparent 25%),
|
||||
radial-gradient(circle at 85% 30%, rgba(99, 102, 241, 0.08), transparent 25%);
|
||||
background-attachment: fixed;
|
||||
color: var(--text-primary);
|
||||
background-color: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: var(--font-sans);
|
||||
font-feature-settings: "tnum" 1, "cv01" 1;
|
||||
margin: 0;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
min-height: 100vh;
|
||||
}
|
||||
transition: background-color .2s ease, color .2s ease;
|
||||
}
|
||||
|
||||
@ -1,3 +1,12 @@
|
||||
// Theme toggle (light/dark). State applied pre-paint in base.html <head>.
|
||||
function toggleTheme() {
|
||||
const root = document.documentElement;
|
||||
const next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
|
||||
root.setAttribute('data-theme', next);
|
||||
try { localStorage.setItem('theme', next); } catch (e) { /* ignore */ }
|
||||
if (window.lucide) window.lucide.createIcons();
|
||||
}
|
||||
|
||||
// Sidebar Logic
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
|
||||
@ -193,7 +193,7 @@
|
||||
else {
|
||||
opList.innerHTML = op.map((v,i)=>{
|
||||
const ratio = v.viewsPerSubRatio!=null ? Number(v.viewsPerSubRatio).toFixed(1)+'x' : '-';
|
||||
return '<a href="https://www.youtube.com/watch?v='+v.videoId+'" target="_blank" class="flex items-center justify-between p-3" style="border-radius:var(--radius-md); background:rgba(255,255,255,0.03); text-decoration:none;">' +
|
||||
return '<a href="https://www.youtube.com/watch?v='+v.videoId+'" target="_blank" class="flex items-center justify-between p-3" style="border-radius:var(--radius-md); background:var(--surface-2); text-decoration:none;">' +
|
||||
'<div class="flex items-center gap-2" style="min-width:0;">' +
|
||||
'<span class="badge badge-muted" style="flex-shrink:0;">'+(i+1)+'</span>' +
|
||||
'<img src="'+esc(v.thumbnailUrl)+'" style="width:48px;height:27px;object-fit:cover;border-radius:4px;flex-shrink:0;">' +
|
||||
@ -220,8 +220,8 @@
|
||||
? '<p class="text-muted text-sm" style="margin-top:4px;">발행 패키지가 없습니다.</p>'
|
||||
: '<div class="text-sm text-muted" style="margin:6px 0 2px;">최근</div>' + recent.map(p=>{
|
||||
const st = PUB_ST[p.status] || { t:p.status, cls:'badge-muted' };
|
||||
return '<a href="/rework/'+p.channelVideoId+'" class="flex items-center justify-between p-2" style="border-radius:6px; background:rgba(255,255,255,0.03); text-decoration:none;">' +
|
||||
'<span class="text-sm truncate" style="max-width:170px; color:#e2e8f0;">'+esc(p.title||'(제목 없음)')+'</span>' +
|
||||
return '<a href="/rework/'+p.channelVideoId+'" class="flex items-center justify-between p-2" style="border-radius:6px; background:var(--surface-2); text-decoration:none;">' +
|
||||
'<span class="text-sm truncate" style="max-width:170px; color:var(--text);">'+esc(p.title||'(제목 없음)')+'</span>' +
|
||||
'<span class="badge '+st.cls+'" style="flex-shrink:0;">'+st.t+'</span></a>';
|
||||
}).join('');
|
||||
|
||||
|
||||
@ -6,6 +6,14 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>h-lab - Dashboard</title>
|
||||
|
||||
<!-- Theme init (before paint, no flash) -->
|
||||
<script>
|
||||
(function () {
|
||||
try { document.documentElement.setAttribute('data-theme', localStorage.getItem('theme') || 'light'); }
|
||||
catch (e) { document.documentElement.setAttribute('data-theme', 'light'); }
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
|
||||
@ -3,118 +3,66 @@
|
||||
|
||||
<body th:fragment="sidebar">
|
||||
<aside id="sidebar" class="sidebar">
|
||||
<div class="p-6 mb-4 flex items-center justify-between">
|
||||
<!-- Brand -->
|
||||
<div class="flex items-center justify-between" style="padding: 1.1rem 1rem 0.5rem;">
|
||||
<div class="flex items-center gap-3">
|
||||
<div style="
|
||||
width: 32px; height: 32px;
|
||||
background: var(--primary-gradient);
|
||||
border-radius: 8px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
box-shadow: 0 0 15px var(--primary-glow);
|
||||
flex-shrink: 0;
|
||||
">
|
||||
<span style="font-weight: bold; color: white; font-size: 18px;">H</span>
|
||||
<div style="width:30px;height:30px;background:var(--primary-gradient);border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0;">
|
||||
<span style="font-weight:800;color:#fff;font-size:15px;">H</span>
|
||||
</div>
|
||||
<div class="brand-text" style="white-space:nowrap;">
|
||||
<div style="font-size:15px;font-weight:700;letter-spacing:-0.2px;color:var(--text);line-height:1.1;">h-lab</div>
|
||||
<div style="font-size:11px;color:var(--text-3);font-weight:500;">yanalyst</div>
|
||||
</div>
|
||||
<h1 class="text-xl font-bold brand-text" style="
|
||||
background: linear-gradient(to right, #fff, #94a3b8);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
white-space: nowrap;
|
||||
">
|
||||
h-lab
|
||||
</h1>
|
||||
</div>
|
||||
<!-- Toggle Button -->
|
||||
<button id="sidebarToggle" class="sidebar-toggle-btn">
|
||||
<i data-lucide="chevron-left" style="width: 18px; height: 18px;"></i>
|
||||
<i data-lucide="chevron-left" style="width:18px;height:18px;"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav style="flex: 1; padding: 0 1rem; overflow-y: auto;">
|
||||
<ul class="flex-col gap-2">
|
||||
<li>
|
||||
<a th:href="@{/}" class="nav-item" th:classappend="${currentPage == 'dashboard'} ? 'active'">
|
||||
<i data-lucide="layout-dashboard" class="nav-icon"></i>
|
||||
<span class="nav-text">Dashboard</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a th:href="@{/channels}" class="nav-item" th:classappend="${currentPage == 'channels'} ? 'active'">
|
||||
<i data-lucide="users" class="nav-icon"></i>
|
||||
<span class="nav-text">Channels</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a th:href="@{/videos}" class="nav-item" th:classappend="${currentPage == 'videos'} ? 'active'">
|
||||
<i data-lucide="video" class="nav-icon"></i>
|
||||
<span class="nav-text">Videos</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a th:href="@{/collection}" class="nav-item"
|
||||
th:classappend="${currentPage == 'collection'} ? 'active'">
|
||||
<i data-lucide="library" class="nav-icon"></i>
|
||||
<span class="nav-text">수집함</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a th:href="@{/discover}" class="nav-item"
|
||||
th:classappend="${currentPage == 'discover'} ? 'active'">
|
||||
<i data-lucide="radar" class="nav-icon"></i>
|
||||
<span class="nav-text">발굴</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a th:href="@{/board}" class="nav-item"
|
||||
th:classappend="${currentPage == 'board'} ? 'active'">
|
||||
<i data-lucide="kanban-square" class="nav-icon"></i>
|
||||
<span class="nav-text">보드</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a th:href="@{/publish}" class="nav-item"
|
||||
th:classappend="${currentPage == 'publish'} ? 'active'">
|
||||
<i data-lucide="send" class="nav-icon"></i>
|
||||
<span class="nav-text">발행</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a th:href="@{/production}" class="nav-item"
|
||||
th:classappend="${currentPage == 'production'} ? 'active'">
|
||||
<i data-lucide="clapperboard" class="nav-icon"></i>
|
||||
<span class="nav-text">Production</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="nav-item">
|
||||
<i data-lucide="settings" class="nav-icon"></i>
|
||||
<span class="nav-text">Settings</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<nav style="flex:1;padding:0.5rem 0.75rem;overflow-y:auto;">
|
||||
<div class="nav-section">분석</div>
|
||||
<a th:href="@{/}" class="nav-item" th:classappend="${currentPage == 'dashboard'} ? 'active'">
|
||||
<i data-lucide="layout-dashboard" class="nav-icon"></i><span class="nav-text">대시보드</span>
|
||||
</a>
|
||||
<a th:href="@{/discover}" class="nav-item" th:classappend="${currentPage == 'discover'} ? 'active'">
|
||||
<i data-lucide="radar" class="nav-icon"></i><span class="nav-text">발굴</span>
|
||||
</a>
|
||||
<a th:href="@{/channels}" class="nav-item" th:classappend="${currentPage == 'channels'} ? 'active'">
|
||||
<i data-lucide="users" class="nav-icon"></i><span class="nav-text">채널</span>
|
||||
</a>
|
||||
|
||||
<div class="nav-section">파이프라인</div>
|
||||
<a th:href="@{/collection}" class="nav-item" th:classappend="${currentPage == 'collection'} ? 'active'">
|
||||
<i data-lucide="library" class="nav-icon"></i><span class="nav-text">수집함</span>
|
||||
</a>
|
||||
<a th:href="@{/board}" class="nav-item" th:classappend="${currentPage == 'board'} ? 'active'">
|
||||
<i data-lucide="kanban-square" class="nav-icon"></i><span class="nav-text">칸반 보드</span>
|
||||
</a>
|
||||
<a th:href="@{/publish}" class="nav-item" th:classappend="${currentPage == 'publish'} ? 'active'">
|
||||
<i data-lucide="send" class="nav-icon"></i><span class="nav-text">발행 큐</span>
|
||||
</a>
|
||||
|
||||
<div class="nav-section">제작</div>
|
||||
<a th:href="@{/production}" class="nav-item" th:classappend="${currentPage == 'production'} ? 'active'">
|
||||
<i data-lucide="clapperboard" class="nav-icon"></i><span class="nav-text">프로덕션</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="user-profile-container">
|
||||
<button class="theme-toggle" onclick="toggleTheme()" type="button" title="테마 전환">
|
||||
<span class="moon"><i data-lucide="moon" style="width:15px;height:15px;"></i></span>
|
||||
<span class="sun"><i data-lucide="sun" style="width:15px;height:15px;"></i></span>
|
||||
<span class="nav-text">테마 전환</span>
|
||||
</button>
|
||||
<div class="user-profile-content">
|
||||
<div style="
|
||||
width: 36px; height: 36px; border-radius: 50%;
|
||||
background: linear-gradient(135deg, #475569 0%, #0f172a 100%);
|
||||
border: 2px solid rgba(255,255,255,0.1);
|
||||
flex-shrink: 0;
|
||||
"></div>
|
||||
<div class="user-info">
|
||||
<div class="text-sm font-bold" style="color: var(--text-primary); white-space: nowrap;">Admin User
|
||||
</div>
|
||||
<div class="text-sm" style="color: var(--text-muted); font-size: 0.75rem; white-space: nowrap;">Pro
|
||||
Plan</div>
|
||||
<div style="width:30px;height:30px;border-radius:50%;background:var(--surface-2);border:1px solid var(--border);flex-shrink:0;"></div>
|
||||
<div class="user-info" style="white-space:nowrap;">
|
||||
<div style="font-size:12.5px;font-weight:600;color:var(--text);">hehiho</div>
|
||||
<div style="font-size:11px;color:var(--text-3);">개인 작업공간</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="w-full btn-ghost sign-out-btn"
|
||||
style="justify-content: center; padding: 0.5rem; gap: 0.5rem; border-radius: 4px; background: rgba(0,0,0,0.2);">
|
||||
<i data-lucide="log-out" style="width: 14px; height: 14px;"></i> <span>Sign Out</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user