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

1223 lines
54 KiB
HTML
Raw Normal View History

2026-05-30 11:28:59 -04:00
<!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>