Files
invest-copilot/design-systems/financial/dashboard.html
T

1223 lines
54 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, user-scalable=no" />
<meta name="theme-color" content="#0c0b14" />
<title>Invest Copilot</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg-deep: #0c0b14;
--bg-surface: #131220;
--bg-raised: #1a1930;
--bg-hover: #222140;
--border-subtle: rgba(255,255,255,0.06);
--border-default: rgba(255,255,255,0.10);
--text-primary: #f1f0f7;
--text-secondary: #8b87a3;
--text-tertiary: #5c5878;
--accent-start: #6366f1;
--accent-end: #a855f7;
--positive: #34d399;
--positive-dim: rgba(52,211,153,0.10);
--negative: #f87171;
--negative-dim: rgba(248,113,113,0.10);
--warning: #fbbf24;
--warning-dim: rgba(251,191,36,0.10);
--info: #38bdf8;
--info-dim: rgba(56,189,248,0.10);
--gradient-accent: linear-gradient(135deg, #6366f1, #a855f7);
--gradient-positive: linear-gradient(135deg, #34d399, #22d3ee);
--shadow-card: 0 1px 3px rgba(0,0,0,0.4), 0 0 1px rgba(0,0,0,0.6);
--shadow-raised: 0 4px 16px rgba(0,0,0,0.5), 0 0 1px rgba(0,0,0,0.6);
--shadow-glow: 0 0 40px rgba(99,102,241,0.15);
--glass-bg: rgba(19,18,32,0.85);
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 20px;
--radius-full: 9999px;
}
html { font-size: 16px; }
body {
font-family: 'Inter', -apple-system, sans-serif;
background: var(--bg-deep);
color: var(--text-primary);
line-height: 1.5;
min-height: 100vh;
overflow-x: hidden;
}
.mono { font-family: 'JetBrains Mono', monospace; }
/* Layout */
.app { display: flex; min-height: 100vh; }
.sidebar {
width: 240px;
background: var(--bg-surface);
border-right: 1px solid var(--border-subtle);
padding: 24px 0;
display: flex;
flex-direction: column;
position: fixed;
top: 0; left: 0; bottom: 0;
z-index: 50;
}
.main {
flex: 1;
margin-left: 240px;
padding: 32px 48px 64px;
max-width: 1400px;
}
/* Sidebar */
.sidebar__logo {
padding: 0 24px;
margin-bottom: 32px;
display: flex;
align-items: center;
gap: 12px;
}
.sidebar__logo-icon {
width: 36px; height: 36px;
background: var(--gradient-accent);
border-radius: var(--radius-sm);
display: flex; align-items: center; justify-content: center;
box-shadow: 0 0 20px rgba(99,102,241,0.3);
}
.sidebar__logo-icon svg { width: 20px; height: 20px; }
.sidebar__logo-text { font-size: 1.125rem; font-weight: 700; letter-spacing: -0.02em; }
.sidebar__nav { flex: 1; padding: 0 12px; }
.sidebar__section-label {
font-size: 0.6875rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-tertiary);
padding: 16px 12px 8px;
}
.sidebar__link {
display: flex; align-items: center; gap: 12px;
padding: 10px 12px;
border-radius: var(--radius-sm);
color: var(--text-secondary);
font-size: 0.875rem;
font-weight: 500;
transition: all 150ms ease-out;
cursor: pointer;
margin-bottom: 2px;
text-decoration: none;
}
.sidebar__link:hover { background: var(--bg-raised); color: var(--text-primary); }
.sidebar__link--active {
background: rgba(99,102,241,0.12);
color: var(--accent-start);
}
.sidebar__link--active:hover { background: rgba(99,102,241,0.18); }
.sidebar__link svg { width: 18px; height: 18px; flex-shrink: 0; }
.sidebar__link .badge {
margin-left: auto;
background: var(--accent-start);
color: white;
font-size: 0.625rem;
font-weight: 600;
padding: 2px 7px;
border-radius: var(--radius-full);
font-family: 'JetBrains Mono', monospace;
}
.sidebar__bottom {
padding: 0 12px;
border-top: 1px solid var(--border-subtle);
padding-top: 16px;
margin-top: 16px;
}
.sidebar__user {
display: flex; align-items: center; gap: 10px;
padding: 8px;
border-radius: var(--radius-sm);
cursor: pointer;
}
.sidebar__user:hover { background: var(--bg-raised); }
.sidebar__avatar {
width: 32px; height: 32px;
border-radius: var(--radius-sm);
background: var(--gradient-accent);
display: flex; align-items: center; justify-content: center;
font-size: 0.75rem; font-weight: 700;
}
.sidebar__user-info { flex: 1; }
.sidebar__user-name { font-size: 0.8125rem; font-weight: 600; }
.sidebar__user-role { font-size: 0.6875rem; color: var(--text-tertiary); }
/* Top Bar */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 32px;
}
.topbar__title { font-size: 1.5rem; font-weight: 700; letter-spacing: -0.02em; }
.topbar__actions { display: flex; align-items: center; gap: 8px; }
.topbar__search {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
padding: 10px 16px 10px 40px;
color: var(--text-primary);
font-size: 0.875rem;
width: 280px;
outline: none;
font-family: 'Inter', sans-serif;
transition: border-color 150ms;
}
.topbar__search::placeholder { color: var(--text-tertiary); }
.topbar__search:focus { border-color: var(--accent-start); box-shadow: 0 0 0 3px rgba(99,102,241,0.15); }
.topbar__search-wrap {
position: relative;
}
.topbar__search-wrap svg {
position: absolute;
left: 12px; top: 50%;
transform: translateY(-50%);
width: 16px; height: 16px;
color: var(--text-tertiary);
}
.topbar__btn {
width: 40px; height: 40px;
border-radius: var(--radius-sm);
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
display: flex; align-items: center; justify-content: center;
color: var(--text-secondary);
cursor: pointer;
transition: all 150ms;
position: relative;
}
.topbar__btn:hover { background: var(--bg-raised); color: var(--text-primary); }
.topbar__btn svg { width: 18px; height: 18px; }
.topbar__btn .notif-dot {
position: absolute; top: 8px; right: 8px;
width: 8px; height: 8px;
background: var(--negative);
border-radius: 50%;
border: 2px solid var(--bg-surface);
}
/* Dashboard Grid */
.dashboard { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.span-full { grid-column: 1 / -1; }
/* Cards */
.card {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
padding: 24px;
box-shadow: var(--shadow-card);
transition: all 200ms ease-out;
}
.card:hover { box-shadow: var(--shadow-raised); transform: translateY(-2px); }
.card__header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.card__title { font-size: 0.9375rem; font-weight: 600; }
.card__action {
font-size: 0.75rem;
color: var(--text-tertiary);
cursor: pointer;
transition: color 150ms;
background: none; border: none;
font-family: 'Inter', sans-serif;
}
.card__action:hover { color: var(--text-primary); }
.card--hero {
background: linear-gradient(135deg, rgba(99,102,241,0.08), rgba(168,85,247,0.08));
border-color: rgba(99,102,241,0.15);
}
.card--hero:hover { box-shadow: var(--shadow-glow); }
/* Hero Card */
.hero { display: flex; align-items: flex-end; justify-content: space-between; gap: 32px; }
.hero__info { flex: 1; }
.hero__label { font-size: 0.8125rem; color: var(--text-secondary); margin-bottom: 4px; }
.hero__value {
font-size: 2.75rem;
font-weight: 800;
letter-spacing: -0.03em;
line-height: 1;
margin-bottom: 8px;
}
.hero__change {
display: inline-flex; align-items: center; gap: 6px;
padding: 6px 14px;
border-radius: var(--radius-full);
font-size: 0.875rem;
font-weight: 600;
font-family: 'JetBrains Mono', monospace;
}
.hero__change--positive { background: var(--positive-dim); color: var(--positive); }
.hero__change--negative { background: var(--negative-dim); color: var(--negative); }
.hero__change svg { width: 14px; height: 14px; }
.hero__meta { display: flex; gap: 32px; margin-top: 20px; }
.hero__meta-item { display: flex; flex-direction: column; gap: 2px; }
.hero__meta-label { font-size: 0.6875rem; color: var(--text-tertiary); }
.hero__meta-value { font-size: 0.9375rem; font-weight: 600; font-family: 'JetBrains Mono', monospace; }
/* Chart */
.chart-area { width: 100%; height: 200px; position: relative; }
.chart-area svg { width: 100%; height: 100%; }
.chart-area line.grid { stroke: var(--border-subtle); stroke-width: 0.5; stroke-dasharray: 4 4; }
.chart-area text { fill: var(--text-tertiary); font-size: 10px; font-family: 'JetBrains Mono', monospace; }
.chart-area .area-fill { fill: url(#chartGradient); opacity: 0.3; }
.chart-area .chart-line {
fill: none;
stroke: url(#lineGradient);
stroke-width: 2.5;
stroke-linecap: round;
stroke-linejoin: round;
}
.chart-area .chart-dot {
fill: var(--accent-start);
stroke: var(--bg-surface);
stroke-width: 3;
filter: drop-shadow(0 0 6px rgba(99,102,241,0.5));
}
/* Stat Cards */
.stat-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
.stat-card {
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
padding: 20px;
box-shadow: var(--shadow-card);
transition: all 200ms ease-out;
}
.stat-card:hover { box-shadow: var(--shadow-raised); transform: translateY(-2px); }
.stat-card__top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.stat-card__icon {
width: 36px; height: 36px;
border-radius: var(--radius-sm);
display: flex; align-items: center; justify-content: center;
}
.stat-card__icon--accent { background: rgba(99,102,241,0.12); color: var(--accent-start); }
.stat-card__icon--positive { background: var(--positive-dim); color: var(--positive); }
.stat-card__icon--negative { background: var(--negative-dim); color: var(--negative); }
.stat-card__icon svg { width: 18px; height: 18px; }
.stat-card__trend {
font-size: 0.6875rem;
font-weight: 600;
font-family: 'JetBrains Mono', monospace;
display: flex; align-items: center; gap: 3px;
}
.stat-card__trend--up { color: var(--positive); }
.stat-card__trend--down { color: var(--negative); }
.stat-card__trend svg { width: 12px; height: 12px; }
.stat-card__value { font-size: 1.375rem; font-weight: 700; font-family: 'JetBrains Mono', monospace; letter-spacing: -0.01em; }
.stat-card__label { font-size: 0.75rem; color: var(--text-tertiary); margin-top: 2px; }
/* Watchlist Table */
.watchlist-table { width: 100%; }
.watchlist-table thead th {
text-align: left;
font-size: 0.6875rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-tertiary);
padding: 0 12px 16px;
border-bottom: 1px solid var(--border-subtle);
}
.watchlist-table thead th:last-child { text-align: right; }
.watchlist-table tbody tr {
transition: background 150ms;
cursor: pointer;
}
.watchlist-table tbody tr:hover { background: var(--bg-raised); }
.watchlist-table td {
padding: 14px 12px;
border-bottom: 1px solid var(--border-subtle);
font-size: 0.875rem;
}
.watchlist-table td:last-child { text-align: right; }
.watchlist-table td.mono { font-family: 'JetBrains Mono', monospace; font-weight: 500; }
.watchlist-table .ticker-cell { display: flex; align-items: center; gap: 12px; }
.watchlist-table .ticker-icon {
width: 32px; height: 32px;
border-radius: var(--radius-sm);
display: flex; align-items: center; justify-content: center;
font-size: 0.6875rem;
font-weight: 700;
font-family: 'JetBrains Mono', monospace;
flex-shrink: 0;
}
.watchlist-table .ticker-icon--green { background: var(--positive-dim); color: var(--positive); }
.watchlist-table .ticker-icon--blue { background: rgba(99,102,241,0.12); color: var(--accent-start); }
.watchlist-table .ticker-icon--purple { background: rgba(168,85,247,0.12); color: var(--accent-end); }
.watchlist-table .ticker-name { font-weight: 600; }
.watchlist-table .ticker-sub { font-size: 0.6875rem; color: var(--text-tertiary); }
.change-pill {
display: inline-flex; align-items: center; gap: 3px;
padding: 3px 8px;
border-radius: var(--radius-full);
font-size: 0.6875rem;
font-weight: 600;
font-family: 'JetBrains Mono', monospace;
}
.change-pill--positive { background: var(--positive-dim); color: var(--positive); }
.change-pill--negative { background: var(--negative-dim); color: var(--negative); }
.change-pill svg { width: 10px; height: 10px; }
/* Mini Sparkline */
.sparkline { width: 80px; height: 28px; }
.sparkline svg { width: 100%; height: 100%; }
.sparkline .spark-line { fill: none; stroke-width: 1.5; stroke-linecap: round; }
.sparkline .spark-line--positive { stroke: var(--positive); }
.sparkline .spark-line--negative { stroke: var(--negative); }
/* Sector Heatmap */
.heatmap { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.heatmap-cell {
border-radius: var(--radius-sm);
padding: 16px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
cursor: pointer;
transition: transform 150ms, filter 150ms;
min-height: 72px;
}
.heatmap-cell:hover { transform: scale(1.05); filter: brightness(1.2); }
.heatmap-cell .label { font-size: 0.75rem; font-weight: 700; font-family: 'JetBrains Mono', monospace; }
.heatmap-cell .value { font-size: 0.8125rem; font-weight: 600; font-family: 'JetBrains Mono', monospace; }
.heatmap-cell--strong-pos { background: rgba(52,211,153,0.15); color: var(--positive); }
.heatmap-cell--pos { background: rgba(52,211,153,0.08); color: var(--positive); }
.heatmap-cell--neutral { background: rgba(255,255,255,0.03); color: var(--text-secondary); }
.heatmap-cell--neg { background: rgba(248,113,113,0.08); color: var(--negative); }
.heatmap-cell--strong-neg { background: rgba(248,113,113,0.15); color: var(--negative); }
/* Alerts */
.alert-list { display: flex; flex-direction: column; gap: 10px; }
.alert-item {
display: flex; align-items: flex-start; gap: 12px;
padding: 14px 16px;
border-radius: var(--radius-sm);
background: var(--bg-raised);
border: 1px solid var(--border-subtle);
transition: all 150ms;
}
.alert-item:hover { border-color: var(--border-default); }
.alert-item--unread { border-left: 3px solid var(--accent-start); }
.alert-item__dot {
width: 8px; height: 8px;
border-radius: 50%;
margin-top: 5px;
flex-shrink: 0;
}
.alert-item__dot--accent { background: var(--accent-start); box-shadow: 0 0 8px rgba(99,102,241,0.4); }
.alert-item__dot--positive { background: var(--positive); }
.alert-item__dot--warning { background: var(--warning); }
.alert-item__dot--negative { background: var(--negative); }
.alert-item__content { flex: 1; }
.alert-item__title { font-size: 0.8125rem; font-weight: 500; }
.alert-item__desc { font-size: 0.6875rem; color: var(--text-tertiary); margin-top: 2px; }
.alert-item__time { font-size: 0.625rem; color: var(--text-tertiary); font-family: 'JetBrains Mono', monospace; white-space: nowrap; margin-left: auto; }
/* Buttons */
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
padding: 10px 20px;
border-radius: var(--radius-md);
font-size: 0.875rem;
font-weight: 600;
font-family: 'Inter', sans-serif;
cursor: pointer;
transition: all 200ms;
border: none;
text-decoration: none;
}
.btn--primary {
background: var(--gradient-accent);
color: white;
box-shadow: 0 0 20px rgba(99,102,241,0.25);
}
.btn--primary:hover { transform: translateY(-1px); box-shadow: 0 0 30px rgba(99,102,241,0.4); }
.btn--primary:active { transform: translateY(0); }
.btn--primary svg { width: 16px; height: 16px; }
.btn--ghost {
background: transparent;
color: var(--text-secondary);
border: 1px solid var(--border-subtle);
}
.btn--ghost:hover { background: var(--bg-raised); color: var(--text-primary); }
.btn--sm { padding: 6px 14px; font-size: 0.75rem; }
.btn-group { display: flex; gap: 8px; }
/* Theme: Light */
[data-theme="light"] {
--bg-deep: #f5f5f7;
--bg-surface: #ffffff;
--bg-raised: #f0f0f3;
--bg-hover: #e8e8ed;
--border-subtle: rgba(0,0,0,0.06);
--border-default: rgba(0,0,0,0.12);
--text-primary: #1a1a2e;
--text-secondary: #6b6b80;
--text-tertiary: #9999aa;
--shadow-card: 0 1px 3px rgba(0,0,0,0.06), 0 0 1px rgba(0,0,0,0.1);
--shadow-raised: 0 4px 16px rgba(0,0,0,0.08), 0 0 1px rgba(0,0,0,0.1);
--shadow-glow: 0 0 40px rgba(99,102,241,0.1);
--glass-bg: rgba(255,255,255,0.85);
/* Gradient defs don't need to change — they're accent colors */
}
[data-theme="light"] body { background: var(--bg-deep); }
[data-theme="light"] .sidebar { background: var(--bg-surface); border-color: var(--border-subtle); }
[data-theme="light"] .card { background: var(--bg-surface); border-color: var(--border-subtle); }
[data-theme="light"] .card--hero {
background: linear-gradient(135deg, rgba(99,102,241,0.04), rgba(168,85,247,0.04));
border-color: rgba(99,102,241,0.12);
}
[data-theme="light"] .stat-card { background: var(--bg-surface); border-color: var(--border-subtle); }
[data-theme="light"] .stat-card:hover { box-shadow: var(--shadow-raised); }
[data-theme="light"] .topbar__search { background: var(--bg-surface); border-color: var(--border-subtle); color: var(--text-primary); }
[data-theme="light"] .topbar__search::placeholder { color: var(--text-tertiary); }
[data-theme="light"] .topbar__search:focus { box-shadow: 0 0 0 3px rgba(99,102,241,0.12); }
[data-theme="light"] .topbar__btn { background: var(--bg-surface); border-color: var(--border-subtle); color: var(--text-secondary); }
[data-theme="light"] .topbar__btn:hover { background: var(--bg-raised); }
[data-theme="light"] .topbar__btn .notif-dot { border-color: var(--bg-surface); }
[data-theme="light"] .watchlist-table thead th { border-color: var(--border-subtle); }
[data-theme="light"] .watchlist-table td { border-color: var(--border-subtle); }
[data-theme="light"] .watchlist-table tbody tr:hover { background: var(--bg-raised); }
[data-theme="light"] .alert-item { background: var(--bg-raised); border-color: var(--border-subtle); }
[data-theme="light"] .alert-item:hover { border-color: var(--border-default); }
[data-theme="light"] .btn--ghost { background: transparent; border-color: var(--border-subtle); color: var(--text-secondary); }
[data-theme="light"] .btn--ghost:hover { background: var(--bg-raised); color: var(--text-primary); }
[data-theme="light"] .chart-area line.grid { stroke: var(--border-subtle); }
[data-theme="light"] .chart-area text { fill: var(--text-tertiary); }
[data-theme="light"] .chart-area .chart-dot { stroke: #ffffff; }
[data-theme="light"] .stat-card__icon--positive { background: rgba(52,211,153,0.10); }
[data-theme="light"] .stat-card__icon--negative { background: rgba(248,113,113,0.10); }
[data-theme="light"] .change-pill--positive { background: rgba(52,211,153,0.10); }
[data-theme="light"] .change-pill--negative { background: rgba(248,113,113,0.10); }
[data-theme="light"] .ticker-icon--green { background: rgba(52,211,153,0.10); }
[data-theme="light"] .ticker-icon--blue { background: rgba(99,102,241,0.10); }
[data-theme="light"] .ticker-icon--purple { background: rgba(168,85,247,0.10); }
[data-theme="light"] .sparkline .spark-line--positive { stroke: #22996e; }
[data-theme="light"] .sparkline .spark-line--negative { stroke: #d44; }
[data-theme="light"] .heatmap-cell--strong-pos { background: rgba(52,211,153,0.12); }
[data-theme="light"] .heatmap-cell--pos { background: rgba(52,211,153,0.06); }
[data-theme="light"] .heatmap-cell--neutral { background: rgba(0,0,0,0.02); }
[data-theme="light"] .heatmap-cell--neg { background: rgba(248,113,113,0.06); }
[data-theme="light"] .heatmap-cell--strong-neg { background: rgba(248,113,113,0.12); }
/* Theme: System auto */
[data-theme="system"] {
/* defaults are dark (already set above) */
}
@media (prefers-color-scheme: light) {
[data-theme="system"] {
--bg-deep: #f5f5f7;
--bg-surface: #ffffff;
--bg-raised: #f0f0f3;
--bg-hover: #e8e8ed;
--border-subtle: rgba(0,0,0,0.06);
--border-default: rgba(0,0,0,0.12);
--text-primary: #1a1a2e;
--text-secondary: #6b6b80;
--text-tertiary: #9999aa;
--shadow-card: 0 1px 3px rgba(0,0,0,0.06), 0 0 1px rgba(0,0,0,0.1);
--shadow-raised: 0 4px 16px rgba(0,0,0,0.08), 0 0 1px rgba(0,0,0,0.1);
--shadow-glow: 0 0 40px rgba(99,102,241,0.1);
--glass-bg: rgba(255,255,255,0.85);
}
[data-theme="system"] body { background: var(--bg-deep); }
[data-theme="system"] .sidebar { background: var(--bg-surface); border-color: var(--border-subtle); }
[data-theme="system"] .card { background: var(--bg-surface); border-color: var(--border-subtle); }
[data-theme="system"] .card--hero {
background: linear-gradient(135deg, rgba(99,102,241,0.04), rgba(168,85,247,0.04));
border-color: rgba(99,102,241,0.12);
}
[data-theme="system"] .stat-card { background: var(--bg-surface); border-color: var(--border-subtle); }
[data-theme="system"] .topbar__search { background: var(--bg-surface); border-color: var(--border-subtle); color: var(--text-primary); }
[data-theme="system"] .topbar__search::placeholder { color: var(--text-tertiary); }
[data-theme="system"] .topbar__btn { background: var(--bg-surface); border-color: var(--border-subtle); color: var(--text-secondary); }
[data-theme="system"] .topbar__btn:hover { background: var(--bg-raised); }
[data-theme="system"] .topbar__btn .notif-dot { border-color: var(--bg-surface); }
[data-theme="system"] .watchlist-table thead th { border-color: var(--border-subtle); }
[data-theme="system"] .watchlist-table td { border-color: var(--border-subtle); }
[data-theme="system"] .alert-item { background: var(--bg-raised); border-color: var(--border-subtle); }
[data-theme="system"] .btn--ghost { border-color: var(--border-subtle); color: var(--text-secondary); }
[data-theme="system"] .btn--ghost:hover { background: var(--bg-raised); }
[data-theme="system"] .chart-area line.grid { stroke: var(--border-subtle); }
[data-theme="system"] .chart-area text { fill: var(--text-tertiary); }
[data-theme="system"] .chart-area .chart-dot { stroke: #ffffff; }
[data-theme="system"] .stat-card__icon--positive { background: rgba(52,211,153,0.10); }
[data-theme="system"] .stat-card__icon--negative { background: rgba(248,113,113,0.10); }
[data-theme="system"] .change-pill--positive { background: rgba(52,211,153,0.10); }
[data-theme="system"] .change-pill--negative { background: rgba(248,113,113,0.10); }
[data-theme="system"] .ticker-icon--green { background: rgba(52,211,153,0.10); }
[data-theme="system"] .ticker-icon--blue { background: rgba(99,102,241,0.10); }
[data-theme="system"] .ticker-icon--purple { background: rgba(168,85,247,0.10); }
[data-theme="system"] .sparkline .spark-line--positive { stroke: #22996e; }
[data-theme="system"] .sparkline .spark-line--negative { stroke: #d44; }
[data-theme="system"] .heatmap-cell--strong-pos { background: rgba(52,211,153,0.12); }
[data-theme="system"] .heatmap-cell--pos { background: rgba(52,211,153,0.06); }
[data-theme="system"] .heatmap-cell--neutral { background: rgba(0,0,0,0.02); }
[data-theme="system"] .heatmap-cell--neg { background: rgba(248,113,113,0.06); }
[data-theme="system"] .heatmap-cell--strong-neg { background: rgba(248,113,113,0.12); }
}
/* Theme Toggle */
.theme-toggle { position: relative; }
.theme-toggle__btn {
width: 40px; height: 40px;
border-radius: var(--radius-sm);
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
display: flex; align-items: center; justify-content: center;
color: var(--text-secondary);
cursor: pointer;
transition: all 150ms;
}
.theme-toggle__btn:hover { background: var(--bg-raised); color: var(--text-primary); }
.theme-toggle__btn svg { width: 18px; height: 18px; }
.theme-dropdown {
position: absolute;
top: calc(100% + 8px);
right: 0;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
padding: 6px;
min-width: 160px;
box-shadow: var(--shadow-raised);
z-index: 200;
opacity: 0;
transform: translateY(-8px);
pointer-events: none;
transition: all 150ms ease-out;
}
.theme-dropdown--open {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
.theme-option {
display: flex; align-items: center; gap: 10px;
padding: 10px 14px;
border-radius: var(--radius-sm);
font-size: 0.8125rem;
font-weight: 500;
color: var(--text-secondary);
cursor: pointer;
transition: all 150ms;
border: none;
background: none;
width: 100%;
text-align: left;
font-family: 'Inter', sans-serif;
}
.theme-option:hover { background: var(--bg-raised); color: var(--text-primary); }
.theme-option--active { color: var(--accent-start); background: rgba(99,102,241,0.08); }
.theme-option--active:hover { background: rgba(99,102,241,0.14); }
.theme-option svg { width: 16px; height: 16px; flex-shrink: 0; opacity: 0.7; }
.theme-option--active svg { opacity: 1; color: var(--accent-start); }
/* Gradient definitions */
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; }
</style>
</head>
<body>
<div class="app">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar__logo">
<div class="sidebar__logo-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
<polyline points="22 7 13.5 15.5 8.5 10.5 2 17"/>
<polyline points="16 7 22 7 22 13"/>
</svg>
</div>
<span class="sidebar__logo-text">Invest Copilot</span>
</div>
<nav class="sidebar__nav">
<div class="sidebar__section-label">Main</div>
<a class="sidebar__link sidebar__link--active" href="#">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<rect x="3" y="3" width="7" height="7" rx="1"/>
<rect x="14" y="3" width="7" height="7" rx="1"/>
<rect x="3" y="14" width="7" height="7" rx="1"/>
<rect x="14" y="14" width="7" height="7" rx="1"/>
</svg>
Dashboard
</a>
<a class="sidebar__link" href="#">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
Research
</a>
<a class="sidebar__link" href="#">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/>
</svg>
Watchlists
</a>
<a class="sidebar__link" href="#">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/>
<polyline points="17 6 23 6 23 12"/>
</svg>
Strategies
</a>
<div class="sidebar__section-label">Analysis</div>
<a class="sidebar__link" href="#">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<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>
Screener
</a>
<a class="sidebar__link" href="#">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M2 12h4l3-9 4 18 3-9h6"/>
</svg>
Sectors
<span class="badge">3</span>
</a>
<a class="sidebar__link" href="#">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<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>
Alerts
</a>
</nav>
<div class="sidebar__bottom">
<div class="sidebar__user">
<div class="sidebar__avatar">LT</div>
<div class="sidebar__user-info">
<div class="sidebar__user-name">Lap Tran</div>
<div class="sidebar__user-role">Pro Plan</div>
</div>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="color:var(--text-tertiary)">
<circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/>
</svg>
</div>
</div>
</aside>
<!-- Main Content -->
<main class="main">
<!-- Top Bar -->
<div class="topbar">
<h1 class="topbar__title">Dashboard</h1>
<div class="topbar__actions">
<div class="topbar__search-wrap">
<svg 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>
<input class="topbar__search" type="text" placeholder="Search stocks, ETFs, sectors..." />
</div>
<div class="theme-toggle" id="themeToggle">
<button class="theme-toggle__btn" id="themeBtn" aria-label="Toggle theme">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" id="themeIcon">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
</button>
<div class="theme-dropdown" id="themeDropdown">
<button class="theme-option theme-option--active" data-value="system" id="themeOptSystem">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<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>
System
</button>
<button class="theme-option" data-value="dark" id="themeOptDark">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
Dark
</button>
<button class="theme-option" data-value="light" id="themeOptLight">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
</svg>
Light
</button>
</div>
</div>
<button class="topbar__btn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<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>
<span class="notif-dot"></span>
</button>
</div>
</div>
<!-- Dashboard Grid -->
<div class="dashboard">
<!-- Hero Card -->
<div class="card card--hero span-full">
<div class="hero">
<div class="hero__info">
<div class="hero__label">Portfolio Value</div>
<div class="hero__value">$147,832<span style="font-size:1.5rem;opacity:0.5">.42</span></div>
<div class="hero__change hero__change--positive">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 15 12 9 6 15"/></svg>
+$2,341.28 (1.61%) today
</div>
<div class="hero__meta">
<div class="hero__meta-item">
<span class="hero__meta-label">Day P&L</span>
<span class="hero__meta-value" style="color:var(--positive)">+$1,204.50</span>
</div>
<div class="hero__meta-item">
<span class="hero__meta-label">Positions</span>
<span class="hero__meta-value">12</span>
</div>
<div class="hero__meta-item">
<span class="hero__meta-label">Win Rate</span>
<span class="hero__meta-value" style="color:var(--positive)">73%</span>
</div>
<div class="hero__meta-item">
<span class="hero__meta-label">Active Alerts</span>
<span class="hero__meta-value" style="color:var(--warning)">3</span>
</div>
</div>
</div>
<div style="display:flex;gap:10px;">
<button class="btn btn--primary">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
New Trade
</button>
<button class="btn btn--ghost">Watchlist</button>
</div>
</div>
</div>
<!-- Chart -->
<div class="card span-full">
<div class="card__header">
<span class="card__title">Portfolio Performance</span>
<div class="btn-group">
<button class="btn btn--ghost btn--sm">1W</button>
<button class="btn btn--primary btn--sm">1M</button>
<button class="btn btn--ghost btn--sm">3M</button>
<button class="btn btn--ghost btn--sm">YTD</button>
<button class="btn btn--ghost btn--sm">1Y</button>
</div>
</div>
<div class="chart-area">
<svg viewBox="0 0 900 200" preserveAspectRatio="none">
<defs>
<linearGradient id="lineGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#6366f1"/>
<stop offset="100%" style="stop-color:#a855f7"/>
</linearGradient>
<linearGradient id="chartGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#6366f1;stop-opacity:0.4"/>
<stop offset="100%" style="stop-color:#6366f1;stop-opacity:0"/>
</linearGradient>
</defs>
<line class="grid" x1="0" y1="40" x2="900" y2="40"/>
<line class="grid" x1="0" y1="80" x2="900" y2="80"/>
<line class="grid" x1="0" y1="120" x2="900" y2="120"/>
<line class="grid" x1="0" y1="160" x2="900" y2="160"/>
<text x="12" y="36">155K</text>
<text x="12" y="76">145K</text>
<text x="12" y="116">135K</text>
<text x="12" y="156">125K</text>
<path class="area-fill" d="M0,130 L50,125 100,118 150,122 200,115 250,108 300,112 350,95 400,88 450,92 500,78 550,72 600,68 650,75 700,62 750,55 800,50 850,42 900,35 L900,200 L0,200 Z"/>
<path class="chart-line" d="M0,130 L50,125 100,118 150,122 200,115 250,108 300,112 350,95 400,88 450,92 500,78 550,72 600,68 650,75 700,62 750,55 800,50 850,42 900,35"/>
<circle class="chart-dot" cx="900" cy="35" r="5"/>
<text x="860" y="195">Sep 26</text>
<text x="12" y="195">Aug 26</text>
</svg>
</div>
</div>
<!-- Stat Cards -->
<div class="stat-cards span-full">
<div class="stat-card">
<div class="stat-card__top">
<div class="stat-card__icon stat-card__icon--positive">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/></svg>
</div>
<div class="stat-card__trend stat-card__trend--up">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 15 12 9 6 15"/></svg>
+12.4%
</div>
</div>
<div class="stat-card__value">$18,294</div>
<div class="stat-card__label">Total Returns (30d)</div>
</div>
<div class="stat-card">
<div class="stat-card__top">
<div class="stat-card__icon stat-card__icon--accent">
<svg 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-card__trend stat-card__trend--up">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 15 12 9 6 15"/></svg>
+3.2%
</div>
</div>
<div class="stat-card__value">1.42</div>
<div class="stat-card__label">Avg. Sharpe Ratio</div>
</div>
<div class="stat-card">
<div class="stat-card__top">
<div class="stat-card__icon stat-card__icon--positive">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
</div>
<div class="stat-card__trend stat-card__trend--down">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
-2
</div>
</div>
<div class="stat-card__value">8</div>
<div class="stat-card__label">Watchlist Items</div>
</div>
<div class="stat-card">
<div class="stat-card__top">
<div class="stat-card__icon stat-card__icon--negative">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
</div>
<div class="stat-card__trend stat-card__trend--up">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 15 12 9 6 15"/></svg>
+1
</div>
</div>
<div class="stat-card__value">3</div>
<div class="stat-card__label">Active Alerts</div>
</div>
</div>
<!-- Watchlist Table -->
<div class="card span-full">
<div class="card__header">
<span class="card__title">Watchlist</span>
<button class="card__action">View All →</button>
</div>
<table class="watchlist-table">
<thead>
<tr>
<th>Asset</th>
<th class="mono">Price</th>
<th>Change</th>
<th class="mono">Volume</th>
<th>Chart</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="ticker-cell">
<div class="ticker-icon ticker-icon--green">A</div>
<div>
<div class="ticker-name">Apple</div>
<div class="ticker-sub">AAPL · NASDAQ</div>
</div>
</div>
</td>
<td class="mono">$189.43</td>
<td>
<span class="change-pill change-pill--positive">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 15 12 9 6 15"/></svg>
+2.4%
</span>
</td>
<td class="mono" style="color:var(--text-secondary)">34.2M</td>
<td>
<div class="sparkline">
<svg viewBox="0 0 80 28">
<path class="spark-line spark-line--positive" d="M0,20 L10,18 20,16 30,14 40,12 50,10 60,8 70,6 80,4"/>
</svg>
</div>
</td>
</tr>
<tr>
<td>
<div class="ticker-cell">
<div class="ticker-icon ticker-icon--blue">M</div>
<div>
<div class="ticker-name">Microsoft</div>
<div class="ticker-sub">MSFT · NASDAQ</div>
</div>
</div>
</td>
<td class="mono">$412.88</td>
<td>
<span class="change-pill change-pill--positive">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 15 12 9 6 15"/></svg>
+1.1%
</span>
</td>
<td class="mono" style="color:var(--text-secondary)">18.7M</td>
<td>
<div class="sparkline">
<svg viewBox="0 0 80 28">
<path class="spark-line spark-line--positive" d="M0,22 L10,20 20,18 30,16 40,14 50,12 60,10 70,8 80,6"/>
</svg>
</div>
</td>
</tr>
<tr>
<td>
<div class="ticker-cell">
<div class="ticker-icon ticker-icon--purple">N</div>
<div>
<div class="ticker-name">NVIDIA</div>
<div class="ticker-sub">NVDA · NASDAQ</div>
</div>
</div>
</td>
<td class="mono">$121.88</td>
<td>
<span class="change-pill change-pill--negative">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
−0.3%
</span>
</td>
<td class="mono" style="color:var(--text-secondary)">312.4M</td>
<td>
<div class="sparkline">
<svg viewBox="0 0 80 28">
<path class="spark-line spark-line--negative" d="M0,8 L10,10 20,12 30,14 40,16 50,18 60,20 70,22 80,24"/>
</svg>
</div>
</td>
</tr>
<tr>
<td>
<div class="ticker-cell">
<div class="ticker-icon ticker-icon--blue">G</div>
<div>
<div class="ticker-name">Alphabet</div>
<div class="ticker-sub">GOOGL · NASDAQ</div>
</div>
</div>
</td>
<td class="mono">$141.26</td>
<td>
<span class="change-pill change-pill--negative">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
−0.8%
</span>
</td>
<td class="mono" style="color:var(--text-secondary)">22.1M</td>
<td>
<div class="sparkline">
<svg viewBox="0 0 80 28">
<path class="spark-line spark-line--negative" d="M0,6 L10,8 20,10 30,12 40,14 50,16 60,18 70,20 80,22"/>
</svg>
</div>
</td>
</tr>
<tr>
<td>
<div class="ticker-cell">
<div class="ticker-icon ticker-icon--green">M</div>
<div>
<div class="ticker-name">Meta</div>
<div class="ticker-sub">META · NASDAQ</div>
</div>
</div>
</td>
<td class="mono">$502.30</td>
<td>
<span class="change-pill change-pill--positive">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 15 12 9 6 15"/></svg>
+0.5%
</span>
</td>
<td class="mono" style="color:var(--text-secondary)">15.3M</td>
<td>
<div class="sparkline">
<svg viewBox="0 0 80 28">
<path class="spark-line spark-line--positive" d="M0,18 L10,16 20,15 30,13 40,12 50,10 60,9 70,7 80,5"/>
</svg>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Sector Heatmap -->
<div class="card">
<div class="card__header">
<span class="card__title">Sector Performance</span>
<button class="card__action">View All →</button>
</div>
<div class="heatmap">
<div class="heatmap-cell heatmap-cell--strong-pos">
<span class="label">XLK</span>
<span class="value">+8.2%</span>
</div>
<div class="heatmap-cell heatmap-cell--pos">
<span class="label">XLF</span>
<span class="value">+3.1%</span>
</div>
<div class="heatmap-cell heatmap-cell--pos">
<span class="label">XLY</span>
<span class="value">+2.8%</span>
</div>
<div class="heatmap-cell heatmap-cell--pos">
<span class="label">XLI</span>
<span class="value">+2.4%</span>
</div>
<div class="heatmap-cell heatmap-cell--pos">
<span class="label">XLV</span>
<span class="value">+1.5%</span>
</div>
<div class="heatmap-cell heatmap-cell--neutral">
<span class="label">XLP</span>
<span class="value">+0.3%</span>
</div>
<div class="heatmap-cell heatmap-cell--neutral">
<span class="label">XLU</span>
<span class="value">−0.2%</span>
</div>
<div class="heatmap-cell heatmap-cell--neg">
<span class="label">XLB</span>
<span class="value">−0.6%</span>
</div>
<div class="heatmap-cell heatmap-cell--neg">
<span class="label">XLE</span>
<span class="value">−1.8%</span>
</div>
<div class="heatmap-cell heatmap-cell--strong-neg">
<span class="label">XLR</span>
<span class="value">−3.4%</span>
</div>
</div>
</div>
<!-- Alerts -->
<div class="card">
<div class="card__header">
<span class="card__title">Recent Alerts</span>
<button class="card__action">View All →</button>
</div>
<div class="alert-list">
<div class="alert-item alert-item--unread">
<div class="alert-item__dot alert-item__dot--negative"></div>
<div class="alert-item__content">
<div class="alert-item__title">NVDA below 200-day MA</div>
<div class="alert-item__desc">Price crossed below key support level</div>
</div>
<span class="alert-item__time">14:27</span>
</div>
<div class="alert-item alert-item--unread">
<div class="alert-item__dot alert-item__dot--warning"></div>
<div class="alert-item__content">
<div class="alert-item__title">Sector rotation detected</div>
<div class="alert-item__desc">Tech → Energy shift signal</div>
</div>
<span class="alert-item__time">13:45</span>
</div>
<div class="alert-item">
<div class="alert-item__dot alert-item__dot--positive"></div>
<div class="alert-item__content">
<div class="alert-item__title">AAPL insider buy detected</div>
<div class="alert-item__desc">CEO purchased 5,000 shares</div>
</div>
<span class="alert-item__time">11:20</span>
</div>
<div class="alert-item">
<div class="alert-item__dot alert-item__dot--accent"></div>
<div class="alert-item__content">
<div class="alert-item__title">MSFT earnings approaching</div>
<div class="alert-item__desc">Q4 report in 3 days</div>
</div>
<span class="alert-item__time">09:00</span>
</div>
</div>
</div>
</div>
</main>
</div>
<script>
(function () {
const STORAGE_KEY = 'invest-copilot-theme';
const DEFAULT_THEME = 'system'; // system | dark | light
const html = document.documentElement;
const themeBtn = document.getElementById('themeBtn');
const themeDropdown = document.getElementById('themeDropdown');
const themeIcon = document.getElementById('themeIcon');
const options = document.querySelectorAll('.theme-option');
// Read persisted theme, fallback to system
let current = localStorage.getItem(STORAGE_KEY) || DEFAULT_THEME;
function applyTheme(mode) {
current = mode;
html.setAttribute('data-theme', mode);
localStorage.setItem(STORAGE_KEY, mode);
// Update active state on dropdown
options.forEach(function (opt) {
opt.classList.toggle('theme-option--active', opt.dataset.value === mode);
});
// Update icon in toggle button
if (mode === 'dark') {
themeIcon.innerHTML = '<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>';
} else if (mode === 'light') {
themeIcon.innerHTML = '<circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>';
} else {
// system — show sun if light mode detected, else moon
const prefersLight = window.matchMedia('(prefers-color-scheme: light)').matches;
if (prefersLight) {
themeIcon.innerHTML = '<circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>';
} else {
themeIcon.innerHTML = '<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>';
}
}
}
// Listen for system preference changes (when in system mode)
window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', function () {
if (current === 'system') applyTheme('system'); // re-apply to flip icon
});
// Toggle dropdown
themeBtn.addEventListener('click', function (e) {
e.stopPropagation();
themeDropdown.classList.toggle('theme-dropdown--open');
});
// Close dropdown on outside click
document.addEventListener('click', function () {
themeDropdown.classList.remove('theme-dropdown--open');
});
themeDropdown.addEventListener('click', function (e) {
e.stopPropagation();
});
// Theme option clicks
options.forEach(function (opt) {
opt.addEventListener('click', function () {
applyTheme(opt.dataset.value);
themeDropdown.classList.remove('theme-dropdown--open');
});
});
// Init
applyTheme(current);
})();
</script>
</body>
</html>