feat: dropdown nav with submenus + black ops colour theme redesign

- Navigation: 6 top-level items (BASE, TRANSMISSIONS, ARMOURY, INTEL, SAFEHOUSE, COMMS) with dropdown children
- nav.js: renders nested dropdown submenus, mobile tap-to-toggle support
- Theme: tactical green (#00ff41) accent, deep black (#0a0a0a) bg, amber (#c9a227) secondary
- 176 colour replacements across 4 CSS + 3 JS files
- Mobile: responsive dropdowns with slide animation
- Updated navigation.json with full nested structure
This commit is contained in:
jae 2026-04-01 03:16:34 +00:00
parent c3455b34cd
commit 139849d632
9 changed files with 365 additions and 216 deletions

View file

@ -1,17 +1,54 @@
[
{
"label": "HOME",
"url": "/",
"order": 1
"label": "BASE", "url": "/", "order": 0,
"children": [
{"label": "OPERATOR PROFILE", "url": "/#operator"},
{"label": "DOSSIER", "url": "/#dossier"},
{"label": "DIAGNOSTICS", "url": "/#diagnostics"},
{"label": "SITREP", "url": "/#sitrep"},
{"label": "RADAR", "url": "/#radar"}
]
},
{
"label": "BLOG",
"url": "/blog",
"order": 2
"label": "TRANSMISSIONS", "url": "/blog", "order": 1,
"children": [
{"label": "DISPATCHES", "url": "/blog"},
{"label": "SYNAPTIC LOG", "url": "/blog?tag=thoughts"},
{"label": "DIRECTIVES", "url": "/blog?tag=tasks"}
]
},
{
"label": "ADMIN",
"url": "/admin",
"order": 3
"label": "ARMOURY", "url": "/blog?tag=dev", "order": 2,
"children": [
{"label": "LOADOUT", "url": "/blog?tag=tools"},
{"label": "FIELD MANUALS", "url": "/blog?tag=guides"},
{"label": "BLUEPRINTS", "url": "/blog?tag=configs"},
{"label": "CONTRABAND", "url": "/blog?tag=discoveries"}
]
},
{
"label": "INTEL", "url": "/blog?tag=intel", "order": 3,
"children": [
{"label": "REDACTED", "url": "/blog?tag=classified"},
{"label": "CASE FILES", "url": "/blog?tag=truecrime"},
{"label": "BLACK OPS", "url": "/blog?tag=conspiracy"},
{"label": "EXHIBITS", "url": "/blog?tag=evidence"},
{"label": "WIRETAPS", "url": "/blog?tag=intercepts"}
]
},
{
"label": "SAFEHOUSE", "url": "/#links", "order": 4,
"children": [
{"label": "ASSET LINKS", "url": "/#links"},
{"label": "DEAD DROPS", "url": "/#deaddrops"}
]
},
{
"label": "COMMS", "url": "/#contact", "order": 5,
"children": [
{"label": "OPEN CHANNELS", "url": "/#contact"},
{"label": "ENCRYPTED LINE", "url": "mailto:jaeswift@jaeswift.xyz"},
{"label": "BACKUP RELAY", "url": "/#backup-relay"}
]
}
]
]

View file

@ -3,21 +3,21 @@
Full cyberpunk admin theme
=================================================== */
:root {
--bg: #0a0e17;
--bg2: #0f1420;
--bg3: #151b2b;
--accent: #00ffc8;
--accent-dim: rgba(0, 255, 200, 0.15);
--secondary: #ff006e;
--secondary-dim: rgba(255, 0, 110, 0.15);
--text: #c8d6e5;
--text-dim: rgba(200, 214, 229, 0.5);
--danger: #ff4757;
--warn: #ffa502;
--success: #00ffc8;
--info: #00d4ff;
--border: rgba(0, 255, 200, 0.08);
--border-strong: rgba(0, 255, 200, 0.2);
--bg: #111111;
--bg2: #141414;
--bg3: #1a1a1a;
--accent: #00ff41;
--accent-dim: rgba(0, 255, 65, 0.15);
--secondary: #ff2d2d;
--secondary-dim: rgba(255, 45, 45, 0.15);
--text: #c0c0c0;
--text-dim: rgba(192, 192, 192, 0.5);
--danger: #ff2d2d;
--warn: #c9a227;
--success: #00ff41;
--info: #c9a227;
--border: rgba(0, 255, 65, 0.08);
--border-strong: rgba(0, 255, 65, 0.2);
--sidebar-w: 220px;
--topbar-h: 50px;
--font-mono: 'JetBrains Mono', monospace;
@ -45,11 +45,11 @@ body {
background: var(--bg);
}
::-webkit-scrollbar-thumb {
background: rgba(0, 255, 200, 0.15);
background: rgba(0, 255, 65, 0.15);
border: 1px solid var(--border);
}
::-webkit-scrollbar-thumb:hover {
background: rgba(0, 255, 200, 0.25);
background: rgba(0, 255, 65, 0.25);
}
/* ─── Login Screen ─── */
@ -1078,11 +1078,11 @@ body {
display: inline-block;
font-size: 0.55rem;
padding: 0.1rem 0.5rem;
border: 1px solid rgba(0, 255, 200, 0.2);
border: 1px solid rgba(0, 255, 65, 0.2);
color: var(--accent);
letter-spacing: 1px;
text-transform: uppercase;
background: rgba(0, 255, 200, 0.05);
background: rgba(0, 255, 65, 0.05);
}
.order-badge {
@ -1096,7 +1096,7 @@ body {
height: 24px;
border: 1px solid var(--border);
color: var(--accent);
background: rgba(0, 255, 200, 0.05);
background: rgba(0, 255, 65, 0.05);
flex-shrink: 0;
}
@ -1584,7 +1584,7 @@ body {
grid-column: 1 / -1;
max-width: 800px;
border-color: var(--accent);
background: rgba(0, 255, 200, 0.03);
background: rgba(0, 255, 65, 0.03);
box-shadow: 0 0 15px rgba(0,255,200,0.06);
}
@ -1728,8 +1728,8 @@ body {
0deg,
transparent,
transparent 2px,
rgba(0, 255, 200, 0.008) 2px,
rgba(0, 255, 200, 0.008) 4px
rgba(0, 255, 65, 0.008) 2px,
rgba(0, 255, 65, 0.008) 4px
);
}
@ -2008,7 +2008,7 @@ body {
padding: 1.2rem 1rem;
font-size: 1.1rem;
font-weight: 700;
color: #00ffc8;
color: #00ff41;
letter-spacing: 2px;
text-transform: uppercase;
border-bottom: 1px solid rgba(0,255,200,0.08);
@ -2023,7 +2023,7 @@ body {
right: 0.8rem;
background: none;
border: 1px solid rgba(0,255,200,0.2);
color: #00ffc8;
color: #00ff41;
font-size: 1.2rem;
cursor: pointer;
padding: 0.2rem 0.5rem;
@ -2119,7 +2119,7 @@ body {
text-align: center;
margin-bottom: 1.5rem;
font-size: 1.8rem;
color: #00ffc8;
color: #00ff41;
text-shadow: 0 0 20px rgba(0,255,200,0.3);
letter-spacing: 4px;
font-weight: 700;
@ -2156,7 +2156,7 @@ body {
}
.hud-range {
width: 100%;
accent-color: #00ffc8;
accent-color: #00ff41;
height: 4px;
cursor: pointer;
}
@ -2164,7 +2164,7 @@ body {
display: inline-block;
min-width: 24px;
text-align: center;
color: #00ffc8;
color: #00ff41;
font-weight: 700;
font-size: 0.9rem;
font-family: "JetBrains Mono", monospace;
@ -2230,7 +2230,7 @@ body {
.toolbar-btn {
background: rgba(0,255,200,0.06);
border: 1px solid rgba(0,255,200,0.12);
color: #00ffc8;
color: #00ff41;
padding: 0.3rem 0.6rem;
font-size: 0.7rem;
cursor: pointer;
@ -2240,7 +2240,7 @@ body {
}
.toolbar-btn:hover {
background: rgba(0,255,200,0.15);
border-color: #00ffc8;
border-color: #00ff41;
}
.toolbar-sep {
width: 1px;
@ -2340,13 +2340,13 @@ body {
}
.toggle-slider.active::after {
left: 22px;
background: #00ffc8;
background: #00ff41;
}
/* ─── HOMEPAGE ─── */
.homepage-section-name {
font-size: 0.7rem;
color: #00ffc8;
color: #00ff41;
text-transform: uppercase;
letter-spacing: 2px;
font-weight: 600;
@ -2428,7 +2428,7 @@ body {
.backup-card-title {
font-size: 0.8rem;
font-weight: 700;
color: #00ffc8;
color: #00ff41;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 0.3rem;
@ -2470,7 +2470,7 @@ body {
.threat-feed .threat-empty {
padding: 1rem;
text-align: center;
color: #00ffc8;
color: #00ff41;
font-size: 0.75rem;
opacity: 0.6;
}

View file

@ -49,7 +49,7 @@
font-size: 0.6rem;
letter-spacing: 2px;
padding: 0.5rem 1.2rem;
background: rgba(0, 255, 200, 0.03);
background: rgba(0, 255, 65, 0.03);
border: 1px solid var(--border);
color: var(--text-secondary);
cursor: pointer;
@ -59,10 +59,10 @@
.filter-btn:hover,
.filter-btn.active {
background: rgba(0, 255, 200, 0.08);
background: rgba(0, 255, 65, 0.08);
border-color: var(--accent);
color: var(--accent);
box-shadow: 0 0 15px rgba(0, 255, 200, 0.1);
box-shadow: 0 0 15px rgba(0, 255, 65, 0.1);
}
/* Blog Section */
@ -142,8 +142,8 @@
}
.post-card:hover {
border-color: rgba(0, 255, 200, 0.3);
box-shadow: 0 0 30px rgba(0, 255, 200, 0.05), inset 0 0 30px rgba(0, 255, 200, 0.02);
border-color: rgba(0, 255, 65, 0.3);
box-shadow: 0 0 30px rgba(0, 255, 65, 0.05), inset 0 0 30px rgba(0, 255, 65, 0.02);
transform: translateY(-2px);
}
@ -175,9 +175,9 @@
font-weight: 700;
}
.threat-LOW { background: rgba(0, 255, 200, 0.1); color: var(--accent); border: 1px solid rgba(0, 255, 200, 0.2); }
.threat-MED { background: rgba(255, 165, 2, 0.1); color: #ffa502; border: 1px solid rgba(255, 165, 2, 0.2); }
.threat-HIGH { background: rgba(255, 71, 87, 0.1); color: #ff4757; border: 1px solid rgba(255, 71, 87, 0.2); }
.threat-LOW { background: rgba(0, 255, 65, 0.1); color: var(--accent); border: 1px solid rgba(0, 255, 65, 0.2); }
.threat-MED { background: rgba(255, 165, 2, 0.1); color: #c9a227; border: 1px solid rgba(255, 165, 2, 0.2); }
.threat-HIGH { background: rgba(255, 71, 87, 0.1); color: #ff2d2d; border: 1px solid rgba(255, 71, 87, 0.2); }
.threat-CRITICAL { background: rgba(255, 0, 0, 0.15); color: #ff0040; border: 1px solid rgba(255, 0, 0, 0.3); animation: criticalPulse 2s ease infinite; }
@keyframes criticalPulse {
@ -229,7 +229,7 @@
.post-card-stats {
padding: 1rem;
border-left: 1px solid var(--border);
background: rgba(0, 255, 200, 0.015);
background: rgba(0, 255, 65, 0.015);
display: flex;
flex-direction: column;
gap: 0.5rem;
@ -270,8 +270,8 @@
.stat-pip {
flex: 1;
height: 8px;
background: rgba(0, 255, 200, 0.06);
border: 1px solid rgba(0, 255, 200, 0.1);
background: rgba(0, 255, 65, 0.06);
border: 1px solid rgba(0, 255, 65, 0.1);
transition: all 0.5s ease;
}
@ -282,15 +282,15 @@
}
.stat-pip.filled.warn {
background: #ffa502;
background: #c9a227;
box-shadow: 0 0 6px rgba(255, 165, 2, 0.4);
border-color: #ffa502;
border-color: #c9a227;
}
.stat-pip.filled.danger {
background: #ff4757;
background: #ff2d2d;
box-shadow: 0 0 6px rgba(255, 71, 87, 0.4);
border-color: #ff4757;
border-color: #ff2d2d;
}
.stat-bpm {
@ -389,7 +389,7 @@
.post-body ul li::marker { color: var(--accent); }
.post-body code {
background: rgba(0, 255, 200, 0.05);
background: rgba(0, 255, 65, 0.05);
border: 1px solid var(--border);
padding: 0.15rem 0.4rem;
font-size: 0.8rem;
@ -414,7 +414,7 @@
letter-spacing: 2px;
color: var(--text-secondary);
padding: 0.3rem 0.6rem;
background: rgba(0, 255, 200, 0.03);
background: rgba(0, 255, 65, 0.03);
border-left: 1px solid var(--border);
border-bottom: 1px solid var(--border);
}
@ -470,7 +470,7 @@
color: var(--text-secondary);
padding: 0.6rem 0.8rem;
border-bottom: 1px solid var(--border);
background: rgba(0, 255, 200, 0.02);
background: rgba(0, 255, 65, 0.02);
}
.sidebar-body {
@ -501,8 +501,8 @@
.sidebar-pip {
width: 16px;
height: 10px;
background: rgba(0, 255, 200, 0.06);
border: 1px solid rgba(0, 255, 200, 0.1);
background: rgba(0, 255, 65, 0.06);
border: 1px solid rgba(0, 255, 65, 0.1);
}
.sidebar-pip.filled {

View file

@ -6,7 +6,7 @@
.post-header {
padding: 10rem 2rem 3rem;
position: relative;
border-bottom: 1px solid rgba(0, 255, 200, 0.1);
border-bottom: 1px solid rgba(0, 255, 65, 0.1);
}
.post-header-content {
@ -16,7 +16,7 @@
.post-back {
display: inline-block;
color: var(--accent, #00ffc8);
color: var(--accent, #00ff41);
font-family: 'JetBrains Mono', monospace;
font-size: 0.75rem;
letter-spacing: 1px;
@ -40,7 +40,7 @@
.post-header .post-date {
font-family: 'JetBrains Mono', monospace;
font-size: 0.8rem;
color: rgba(0, 255, 200, 0.6);
color: rgba(0, 255, 65, 0.6);
letter-spacing: 1px;
}
@ -69,8 +69,8 @@
font-family: 'JetBrains Mono', monospace;
font-size: 0.65rem;
padding: 0.25rem 0.6rem;
border: 1px solid rgba(0, 255, 200, 0.2);
color: var(--accent, #00ffc8);
border: 1px solid rgba(0, 255, 65, 0.2);
color: var(--accent, #00ff41);
letter-spacing: 1px;
text-transform: uppercase;
}
@ -84,17 +84,17 @@
font-weight: 600;
}
.threat-low {
color: #00ffc8;
border: 1px solid rgba(0, 255, 200, 0.3);
text-shadow: 0 0 8px rgba(0, 255, 200, 0.3);
color: #00ff41;
border: 1px solid rgba(0, 255, 65, 0.3);
text-shadow: 0 0 8px rgba(0, 255, 65, 0.3);
}
.threat-medium, .threat-med {
color: #ffa502;
color: #c9a227;
border: 1px solid rgba(255, 165, 2, 0.3);
text-shadow: 0 0 8px rgba(255, 165, 2, 0.3);
}
.threat-high {
color: #ff4757;
color: #ff2d2d;
border: 1px solid rgba(255, 71, 87, 0.3);
text-shadow: 0 0 8px rgba(255, 71, 87, 0.3);
}
@ -148,9 +148,9 @@
.post-rendered h2 {
font-size: 1.2rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid rgba(0, 255, 200, 0.1);
border-bottom: 1px solid rgba(0, 255, 65, 0.1);
}
.post-rendered h3 { font-size: 1rem; color: var(--accent, #00ffc8); }
.post-rendered h3 { font-size: 1rem; color: var(--accent, #00ff41); }
.post-rendered h4 { font-size: 0.9rem; }
.post-rendered p {
@ -163,18 +163,18 @@
}
.post-rendered em {
color: rgba(0, 255, 200, 0.7);
color: rgba(0, 255, 65, 0.7);
font-style: italic;
}
.post-rendered a {
color: var(--accent, #00ffc8);
color: var(--accent, #00ff41);
text-decoration: none;
border-bottom: 1px solid rgba(0, 255, 200, 0.3);
border-bottom: 1px solid rgba(0, 255, 65, 0.3);
transition: border-color 0.3s;
}
.post-rendered a:hover {
border-color: var(--accent, #00ffc8);
border-color: var(--accent, #00ff41);
}
.post-rendered ul {
@ -191,22 +191,22 @@
content: '▸';
position: absolute;
left: 0;
color: var(--accent, #00ffc8);
color: var(--accent, #00ff41);
font-size: 0.8rem;
}
.post-rendered hr {
border: none;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(0, 255, 200, 0.2), transparent);
background: linear-gradient(90deg, transparent, rgba(0, 255, 65, 0.2), transparent);
margin: 2rem 0;
}
/* ─── Code Blocks ─── */
.post-rendered pre {
background: rgba(0, 0, 0, 0.5);
border: 1px solid rgba(0, 255, 200, 0.1);
border-left: 3px solid var(--accent, #00ffc8);
border: 1px solid rgba(0, 255, 65, 0.1);
border-left: 3px solid var(--accent, #00ff41);
border-radius: 0;
padding: 1.2rem 1.5rem;
margin: 1.5rem 0;
@ -222,15 +222,15 @@
font-family: 'JetBrains Mono', monospace;
font-size: 0.6rem;
padding: 0.2rem 0.6rem;
background: rgba(0, 255, 200, 0.08);
color: rgba(0, 255, 200, 0.4);
background: rgba(0, 255, 65, 0.08);
color: rgba(0, 255, 65, 0.4);
letter-spacing: 1px;
}
.post-rendered pre code {
font-family: 'Share Tech Mono', 'JetBrains Mono', monospace;
font-size: 0.82rem;
color: var(--accent, #00ffc8);
color: var(--accent, #00ff41);
line-height: 1.6;
background: none;
padding: 0;
@ -239,10 +239,10 @@
.post-rendered code.inline-code {
font-family: 'Share Tech Mono', monospace;
font-size: 0.82rem;
background: rgba(0, 255, 200, 0.06);
border: 1px solid rgba(0, 255, 200, 0.1);
background: rgba(0, 255, 65, 0.06);
border: 1px solid rgba(0, 255, 65, 0.1);
padding: 0.15rem 0.4rem;
color: var(--accent, #00ffc8);
color: var(--accent, #00ff41);
}
/* ─── Sidebar ─── */
@ -257,8 +257,8 @@
.post-stats-panel,
.post-meta-panel,
.post-nav-panel {
background: rgba(0, 255, 200, 0.02);
border: 1px solid rgba(0, 255, 200, 0.08);
background: rgba(0, 255, 65, 0.02);
border: 1px solid rgba(0, 255, 65, 0.08);
}
/* ─── Stat Rows (sidebar) ─── */
@ -306,24 +306,24 @@
transition: all 0.3s;
}
.pip.filled {
background: var(--accent, #00ffc8);
border-color: var(--accent, #00ffc8);
box-shadow: 0 0 6px rgba(0, 255, 200, 0.3);
background: var(--accent, #00ff41);
border-color: var(--accent, #00ff41);
box-shadow: 0 0 6px rgba(0, 255, 65, 0.3);
}
.pip.filled.warn {
background: #ffa502;
border-color: #ffa502;
background: #c9a227;
border-color: #c9a227;
box-shadow: 0 0 6px rgba(255, 165, 2, 0.3);
}
.pip.filled.danger {
background: #ff4757;
border-color: #ff4757;
background: #ff2d2d;
border-color: #ff2d2d;
box-shadow: 0 0 6px rgba(255, 71, 87, 0.3);
}
.stat-divider {
height: 1px;
background: rgba(0, 255, 200, 0.08);
background: rgba(0, 255, 65, 0.08);
margin: 0.5rem 0;
}
@ -341,7 +341,7 @@
font-family: 'Orbitron', sans-serif;
font-size: 2rem;
font-weight: 700;
color: #ff4757;
color: #ff2d2d;
text-shadow: 0 0 15px rgba(255, 71, 87, 0.4);
transition: transform 0.1s;
}
@ -376,7 +376,7 @@
justify-content: space-between;
align-items: center;
padding: 0.4rem 0;
border-bottom: 1px solid rgba(0, 255, 200, 0.04);
border-bottom: 1px solid rgba(0, 255, 65, 0.04);
}
.meta-row:last-child {
border-bottom: none;
@ -397,14 +397,14 @@
}
.meta-val.mono {
font-size: 0.65rem;
color: rgba(0, 255, 200, 0.5);
color: rgba(0, 255, 65, 0.5);
}
/* ─── Navigation Panel ─── */
.post-nav-link {
display: block;
padding: 0.8rem 0;
border-bottom: 1px solid rgba(0, 255, 200, 0.06);
border-bottom: 1px solid rgba(0, 255, 65, 0.06);
text-decoration: none;
transition: all 0.3s;
}
@ -412,14 +412,14 @@
border-bottom: none;
}
.post-nav-link:hover {
background: rgba(0, 255, 200, 0.03);
background: rgba(0, 255, 65, 0.03);
padding-left: 0.5rem;
}
.nav-dir {
font-family: 'JetBrains Mono', monospace;
font-size: 0.6rem;
color: var(--accent, #00ffc8);
color: var(--accent, #00ff41);
letter-spacing: 1px;
display: block;
margin-bottom: 0.2rem;
@ -445,7 +445,7 @@
align-items: center;
gap: 1rem;
padding: 4rem 0;
color: rgba(0, 255, 200, 0.5);
color: rgba(0, 255, 65, 0.5);
font-family: 'JetBrains Mono', monospace;
font-size: 0.8rem;
letter-spacing: 2px;
@ -456,7 +456,7 @@
padding: 4rem 2rem;
font-family: 'Orbitron', sans-serif;
font-size: 1.1rem;
color: #ff4757;
color: #ff2d2d;
text-shadow: 0 0 20px rgba(255, 71, 87, 0.3);
}

View file

@ -1,27 +1,27 @@
/* ===================================================
JAESWIFT.XYZ Sci-Fi Dashboard Theme
Palette: #0a0a0f (bg), #00ffc8 (accent), #0d1117 (panels)
Palette: #0a0a0a (bg), #00ff41 (accent-green), #c9a227 (accent-amber), #161616 (panels) BLACK OPS THEME
=================================================== */
/* --- Reset & Base --- */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg-primary: #060608;
--bg-secondary: #0a0e14;
--bg-panel: #0c1018;
--bg-panel-hover: #101820;
--accent: #00ffc8;
--accent-dim: #00ffc840;
--accent-glow: #00ffc880;
--accent-subtle: #00ffc815;
--text-primary: #c8d6e5;
--text-secondary: #5a6a7a;
--text-muted: #2a3a4a;
--border: #1a2a3a;
--border-accent: #00ffc830;
--danger: #ff3a5c;
--warning: #ffaa00;
--bg-primary: #0a0a0a;
--bg-secondary: #111111;
--bg-panel: #161616;
--bg-panel-hover: #1e1e1e;
--accent: #00ff41;
--accent-dim: #00ff4140;
--accent-glow: #00ff4180;
--accent-subtle: #00ff4115;
--text-primary: #c0c0c0;
--text-secondary: #707070;
--text-muted: #3a3a3a;
--border: #2a2a2a;
--border-accent: #00ff4130;
--danger: #ff2d2d;
--warning: #c9a227;
--font-mono: 'JetBrains Mono', 'Share Tech Mono', 'Courier New', monospace;
--font-display: 'Orbitron', sans-serif;
--nav-height: 60px;
@ -63,8 +63,8 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
0deg,
transparent,
transparent 2px,
rgba(0, 255, 200, 0.008) 2px,
rgba(0, 255, 200, 0.008) 4px
rgba(0, 255, 65, 0.008) 2px,
rgba(0, 255, 65, 0.008) 4px
);
}
@ -97,7 +97,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
top: 0; left: 0;
width: 100%;
height: var(--nav-height);
background: rgba(6, 6, 8, 0.92);
background: rgba(10, 10, 10, 0.92);
backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border);
z-index: 1000;
@ -105,9 +105,9 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
}
.nav-main.scrolled {
background: rgba(6, 6, 8, 0.98);
background: rgba(10, 10, 10, 0.98);
border-bottom-color: var(--accent-dim);
box-shadow: 0 4px 30px rgba(0, 255, 200, 0.05);
box-shadow: 0 4px 30px rgba(0, 255, 65, 0.05);
}
.nav-container {
@ -200,7 +200,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
top: 100%;
left: 0;
min-width: 200px;
background: rgba(10, 14, 20, 0.98);
background: rgba(14, 14, 14, 0.98);
border: 1px solid var(--border);
border-top: 2px solid var(--accent);
list-style: none;
@ -244,6 +244,27 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
transition: opacity 0.2s ease;
}
.dropdown li a.active {
color: var(--accent);
background: var(--accent-subtle);
}
/* Dropdown Arrow */
.dropdown-arrow {
font-size: 0.55rem;
margin-left: 0.35rem;
opacity: 0.5;
transition: transform 0.3s ease, opacity 0.3s ease;
display: inline-block;
}
.nav-item:hover > .nav-link .dropdown-arrow,
.nav-item.dropdown-open > .nav-link .dropdown-arrow {
opacity: 1;
transform: rotate(180deg);
}
.dropdown li a:hover::before { opacity: 1; }
/* Nav Status */
@ -321,7 +342,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
============================ */
.panel {
border: 1px solid var(--border);
background: rgba(10, 14, 20, 0.6);
background: rgba(16, 16, 16, 0.6);
position: relative;
overflow: hidden;
}
@ -352,7 +373,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
align-items: center;
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--border);
background: rgba(0, 255, 200, 0.02);
background: rgba(0, 255, 65, 0.02);
}
.panel-title {
@ -523,7 +544,6 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
@keyframes glitch-2 {
0%, 92%, 100% { opacity: 0; transform: none; }
93% { opacity: 0.6; transform: translate(2px, 2px); clip-path: inset(10% 0 70% 0); }
95% { opacity: 0.6; transform: translate(-1px, -2px); clip-path: inset(40% 0 20% 0); }
97% { opacity: 0; }
}
@ -548,19 +568,6 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
/* ============================
SERVER METRICS PANEL
============================ */
.hud-metrics {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.metric-row {
display: grid;
grid-template-columns: 80px 1fr 45px;
align-items: center;
gap: 0.75rem;
}
.metric-label {
font-family: var(--font-mono);
font-size: 0.55rem;
@ -570,15 +577,15 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
.metric-bar {
height: 6px;
background: rgba(0, 255, 200, 0.08);
border: 1px solid rgba(0, 255, 200, 0.15);
background: rgba(0, 255, 65, 0.08);
border: 1px solid rgba(0, 255, 65, 0.15);
position: relative;
overflow: hidden;
}
.metric-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent), rgba(0, 255, 200, 0.4));
background: linear-gradient(90deg, var(--accent), rgba(0, 255, 65, 0.4));
box-shadow: 0 0 8px var(--accent-glow);
transition: width 1.5s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
@ -618,7 +625,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
.hud-mini-panel {
border: 1px solid var(--border);
background: rgba(10, 14, 20, 0.6);
background: rgba(16, 16, 16, 0.6);
position: relative;
}
@ -687,8 +694,8 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
.container-bar {
height: 4px;
background: rgba(0, 255, 200, 0.08);
border: 1px solid rgba(0, 255, 200, 0.1);
background: rgba(0, 255, 65, 0.08);
border: 1px solid rgba(0, 255, 65, 0.1);
margin-bottom: 0.4rem;
}
@ -764,7 +771,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
align-items: center;
padding: 0.6rem 1.5rem;
border-bottom: 1px solid var(--border);
background: rgba(10, 14, 20, 0.95);
background: rgba(16, 16, 16, 0.95);
backdrop-filter: blur(10px);
gap: 1rem;
}
@ -839,8 +846,8 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
.hud-scan-bar {
width: 60px;
height: 4px;
background: rgba(0, 255, 200, 0.08);
border: 1px solid rgba(0, 255, 200, 0.15);
background: rgba(0, 255, 65, 0.08);
border: 1px solid rgba(0, 255, 65, 0.15);
overflow: hidden;
}
@ -959,7 +966,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
align-items: center;
padding: 1rem 1.25rem;
border-bottom: 1px solid var(--border);
background: rgba(0, 255, 200, 0.02);
background: rgba(0, 255, 65, 0.02);
}
.panel-title {
@ -1077,7 +1084,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
.blog-card:hover {
border-color: var(--accent-dim);
transform: translateY(-4px);
box-shadow: 0 8px 30px rgba(0, 255, 200, 0.08);
box-shadow: 0 8px 30px rgba(0, 255, 65, 0.08);
}
.blog-card:hover::after { transform: scaleX(1); }
@ -1170,7 +1177,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
.dev-card:hover {
border-color: var(--accent-dim);
transform: translateY(-4px);
box-shadow: 0 8px 30px rgba(0, 255, 200, 0.06);
box-shadow: 0 8px 30px rgba(0, 255, 65, 0.06);
}
.dev-card:hover::before { height: 100%; }
@ -1339,7 +1346,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
.form-input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 1px var(--accent-dim), 0 0 20px rgba(0, 255, 200, 0.05);
box-shadow: 0 0 0 1px var(--accent-dim), 0 0 20px rgba(0, 255, 65, 0.05);
}
.form-input::placeholder { color: var(--text-muted); }
@ -1429,7 +1436,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
.footer {
padding: 2rem;
border-top: 1px solid var(--border);
background: rgba(6, 6, 8, 0.8);
background: rgba(10, 10, 10, 0.8);
}
.footer-container {
@ -1523,7 +1530,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
left: 0;
width: 100%;
flex-direction: column;
background: rgba(6, 6, 8, 0.98);
background: rgba(10, 10, 10, 0.98);
backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border);
padding: 1rem 0;
@ -1531,9 +1538,11 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
max-height: calc(100vh - var(--nav-height));
overflow-y: auto;
}
.nav-menu.active {
.nav-menu.open {
transform: translateY(0);
opacity: 1;
visibility: visible;
@ -1551,16 +1560,35 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
position: static;
border: none;
border-top: none;
background: rgba(0, 0, 0, 0.3);
background: rgba(20, 20, 20, 0.95);
max-height: 0;
overflow: hidden;
opacity: 1;
visibility: visible;
transform: none;
transition: max-height 0.3s ease;
transition: max-height 0.4s ease;
box-shadow: none;
backdrop-filter: none;
}
.nav-item.active .dropdown { max-height: 300px; }
.nav-item.dropdown-open > .dropdown {
max-height: 500px;
}
.dropdown li a {
padding-left: 3rem;
font-size: 0.65rem;
}
.dropdown li a::before {
content: '── ';
opacity: 0.3;
color: var(--accent);
}
.dropdown li a:hover::before {
opacity: 1;
}
/* HUD grid stacks on mobile */
.hero-hud-grid {
@ -1616,15 +1644,15 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
padding: 3rem;
font-family: 'JetBrains Mono', monospace;
font-size: 0.8rem;
color: rgba(0, 255, 200, 0.4);
color: rgba(0, 255, 65, 0.4);
letter-spacing: 2px;
}
.loading-spinner {
width: 16px;
height: 16px;
border: 2px solid rgba(0, 255, 200, 0.1);
border-top-color: #00ffc8;
border: 2px solid rgba(0, 255, 65, 0.1);
border-top-color: #00ff41;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@ -1634,8 +1662,8 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
}
.blog-view-all-link:hover {
background: rgba(0, 255, 200, 0.08) !important;
box-shadow: 0 0 15px rgba(0, 255, 200, 0.15);
background: rgba(0, 255, 65, 0.08) !important;
box-shadow: 0 0 15px rgba(0, 255, 65, 0.15);
}
/* ─── Weather Display ─── */
@ -1654,12 +1682,12 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
font-size: 2rem;
font-weight: 700;
color: #fff;
text-shadow: 0 0 15px rgba(0, 255, 200, 0.3);
text-shadow: 0 0 15px rgba(0, 255, 65, 0.3);
}
.weather-cond {
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
color: rgba(0, 255, 200, 0.6);
color: rgba(0, 255, 65, 0.6);
letter-spacing: 1px;
}
.weather-details {
@ -1670,10 +1698,10 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
.weather-detail {
font-family: 'JetBrains Mono', monospace;
font-size: 0.6rem;
color: rgba(200, 214, 229, 0.4);
color: rgba(192, 192, 192, 0.4);
letter-spacing: 1px;
padding: 0.2rem 0.5rem;
border: 1px solid rgba(0, 255, 200, 0.06);
border: 1px solid rgba(0, 255, 65, 0.06);
}
/* ─── Now Playing Display ─── */
@ -1694,13 +1722,13 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
.np-artist {
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
color: var(--accent, #00ffc8);
color: var(--accent, #00ff41);
letter-spacing: 1px;
}
.np-album {
font-family: 'JetBrains Mono', monospace;
font-size: 0.6rem;
color: rgba(200, 214, 229, 0.35);
color: rgba(192, 192, 192, 0.35);
letter-spacing: 0.5px;
margin-bottom: 0.3rem;
}
@ -1713,10 +1741,10 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
}
.np-bar {
width: 4px;
background: var(--accent, #00ffc8);
background: var(--accent, #00ff41);
border-radius: 1px;
animation: npBounce 0.8s ease-in-out infinite alternate;
box-shadow: 0 0 4px rgba(0, 255, 200, 0.3);
box-shadow: 0 0 4px rgba(0, 255, 65, 0.3);
}
.np-bar:nth-child(1) { height: 40%; animation-delay: 0s; }
.np-bar:nth-child(2) { height: 70%; animation-delay: 0.1s; }
@ -1732,7 +1760,7 @@ a:hover { color: #fff; text-shadow: 0 0 10px var(--accent-glow); }
}
#npStatus {
color: var(--accent, #00ffc8);
color: var(--accent, #00ff41);
animation: statusPulse 2s ease-in-out infinite;
}
@keyframes statusPulse {

View file

@ -12,8 +12,8 @@ const AdminApp = {
navData: [],
linksData: [],
themeDefaults: {
accent: '#00ffc8',
bg: '#0a0e17',
accent: '#00ff41',
bg: '#111111',
text: '#e0e0e0',
scanlines: true,
particles: true,
@ -283,7 +283,7 @@ const AdminApp = {
if (type === 'success') {
div.style.background = 'rgba(0,255,200,0.15)';
div.style.borderColor = '#00ffc8';
div.style.borderColor = '#00ff41';
} else if (type === 'error') {
div.style.background = 'rgba(255,50,50,0.15)';
div.style.borderColor = '#ff3232';
@ -358,7 +358,7 @@ const AdminApp = {
if (grid) {
grid.innerHTML = arr.map(svc => {
const isUp = svc.status === 'up' || svc.status === 'online' || svc.status === true;
const dotColor = isUp ? '#00ffc8' : '#ff3232';
const dotColor = isUp ? '#00ff41' : '#ff3232';
const statusText = isUp ? 'ONLINE' : 'OFFLINE';
return `<div class="service-card">
<span class="service-dot" style="display:inline-block;width:8px;height:8px;border-radius:50%;background:${dotColor};box-shadow:0 0 8px ${dotColor};margin-right:8px;"></span>
@ -385,7 +385,7 @@ const AdminApp = {
} else {
container.innerHTML = arr.map(t => {
const severity = (t.severity || t.level || 'low').toLowerCase();
const sevColor = severity === 'critical' ? '#ff0040' : severity === 'high' ? '#ff6600' : severity === 'medium' ? '#ffaa00' : '#00ffc8';
const sevColor = severity === 'critical' ? '#ff0040' : severity === 'high' ? '#ff6600' : severity === 'medium' ? '#c9a227' : '#00ff41';
return `<div class="threat-item" style="border-left:3px solid ${sevColor};padding:8px 12px;margin-bottom:6px;background:rgba(255,255,255,0.03);">
<div style="display:flex;justify-content:space-between;align-items:center;">
<strong style="color:${sevColor};">${this.escapeHtml(t.cve || t.id || t.name || 'Unknown')}</strong>
@ -743,7 +743,7 @@ const AdminApp = {
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" style="max-width:100%;">');
// Links
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" style="color:#00ffc8;">$1</a>');
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" style="color:#00ff41;">$1</a>');
// Unordered lists
html = html.replace(/^- (.+)$/gm, '<li>$1</li>');
@ -805,7 +805,7 @@ const AdminApp = {
<div style="font-weight:600;color:#e0e0e0;">${this.escapeHtml(t.artist || 'Unknown')} ${this.escapeHtml(t.title || 'Untitled')}</div>
<div style="font-size:12px;color:#555;">${this.escapeHtml(t.album || '')} ${t.genre ? '· ' + this.escapeHtml(t.genre) : ''}</div>
</div>
${t.url ? `<a href="${this.escapeHtml(t.url)}" target="_blank" style="color:#00ffc8;margin-right:12px;font-size:12px;">▶</a>` : ''}
${t.url ? `<a href="${this.escapeHtml(t.url)}" target="_blank" style="color:#00ff41;margin-right:12px;font-size:12px;">▶</a>` : ''}
<button class="btn-sm btn-delete" onclick="AdminApp.deleteTrack(${i})" title="Delete"></button>
</div>`;
}).join('');
@ -1107,7 +1107,7 @@ const AdminApp = {
container.innerHTML = this.navData.map((item, i) => {
return `<div class="nav-list-item" style="display:flex;align-items:center;padding:10px;border-bottom:1px solid rgba(255,255,255,0.05);">
<span style="color:#00ffc8;font-size:12px;margin-right:12px;min-width:30px;">#${item.order || i}</span>
<span style="color:#00ff41;font-size:12px;margin-right:12px;min-width:30px;">#${item.order || i}</span>
<div style="flex:1;">
<div style="font-weight:600;color:#e0e0e0;">${this.escapeHtml(item.label || 'Unnamed')}</div>
<div style="font-size:12px;color:#555;">${this.escapeHtml(item.url || '')}</div>
@ -1191,7 +1191,7 @@ const AdminApp = {
<span style="font-size:18px;margin-right:12px;">${this.escapeHtml(link.icon || '🔗')}</span>
<div style="flex:1;">
<div style="font-weight:600;color:#e0e0e0;">${this.escapeHtml(link.name || 'Unnamed')}</div>
<div style="font-size:12px;color:#555;">${this.escapeHtml(link.url || '')} ${link.category ? '<span style="color:#00ffc8;">· ' + this.escapeHtml(link.category) + '</span>' : ''}</div>
<div style="font-size:12px;color:#555;">${this.escapeHtml(link.url || '')} ${link.category ? '<span style="color:#00ff41;">· ' + this.escapeHtml(link.category) + '</span>' : ''}</div>
</div>
<button class="btn-sm btn-delete" onclick="AdminApp.deleteLink(${i})" title="Delete"></button>
</div>`;

View file

@ -248,7 +248,7 @@
function drawParticle(p) {
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(0, 255, 200, ${p.opacity})`;
ctx.fillStyle = `rgba(0, 255, 65, ${p.opacity})`;
ctx.fill();
}
@ -264,7 +264,7 @@
ctx.beginPath();
ctx.moveTo(particles[i].x, particles[i].y);
ctx.lineTo(particles[j].x, particles[j].y);
ctx.strokeStyle = `rgba(0, 255, 200, ${opacity})`;
ctx.strokeStyle = `rgba(0, 255, 65, ${opacity})`;
ctx.lineWidth = 0.5;
ctx.stroke();
}
@ -281,7 +281,7 @@
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(mouseX, mouseY);
ctx.strokeStyle = `rgba(0, 255, 200, ${opacity})`;
ctx.strokeStyle = `rgba(0, 255, 65, ${opacity})`;
ctx.lineWidth = 0.8;
ctx.stroke();
}
@ -501,13 +501,13 @@
form.reset();
} else {
submitText.textContent = '✗ FAILED';
btn.style.borderColor = '#ff006e';
btn.style.color = '#ff006e';
btn.style.borderColor = '#ff2d2d';
btn.style.color = '#ff2d2d';
}
} catch (err) {
submitText.textContent = '✗ ERROR';
btn.style.borderColor = '#ff006e';
btn.style.color = '#ff006e';
btn.style.borderColor = '#ff2d2d';
btn.style.color = '#ff2d2d';
}
setTimeout(() => {
@ -562,7 +562,7 @@
const y = e.clientY - rect.top;
card.style.setProperty('--mouse-x', `${x}px`);
card.style.setProperty('--mouse-y', `${y}px`);
card.style.background = `radial-gradient(circle 200px at ${x}px ${y}px, rgba(0, 255, 200, 0.04), var(--bg-panel))`;
card.style.background = `radial-gradient(circle 200px at ${x}px ${y}px, rgba(0, 255, 65, 0.04), var(--bg-panel))`;
});
card.addEventListener('mouseleave', () => {
@ -670,7 +670,7 @@
if (dlData.length > maxPoints) dlData.shift();
if (ulData.length > maxPoints) ulData.shift();
drawLine(dlData, '#00ffc8', 0.9);
drawLine(dlData, '#00ff41', 0.9);
drawLine(ulData, '#00a8ff', 0.6);
updateStats();
@ -700,13 +700,13 @@
// Colour shift for high values
if (val > 85) {
barEl.style.background = 'linear-gradient(90deg, #ff4757, rgba(255,71,87,0.4))';
barEl.style.background = 'linear-gradient(90deg, #ff2d2d, rgba(255,71,87,0.4))';
barEl.style.boxShadow = '0 0 8px rgba(255,71,87,0.4)';
valEl.style.color = '#ff4757';
valEl.style.color = '#ff2d2d';
} else if (val > 70) {
barEl.style.background = 'linear-gradient(90deg, #ffa502, rgba(255,165,2,0.4))';
barEl.style.background = 'linear-gradient(90deg, #c9a227, rgba(255,165,2,0.4))';
barEl.style.boxShadow = '0 0 8px rgba(255,165,2,0.3)';
valEl.style.color = '#ffa502';
valEl.style.color = '#c9a227';
} else {
barEl.style.background = '';
barEl.style.boxShadow = '';
@ -837,13 +837,13 @@
barEl.style.width = m.value + '%';
valEl.textContent = Math.round(m.value) + '%';
if (m.value > 85) {
barEl.style.background = 'linear-gradient(90deg, #ff4757, rgba(255,71,87,0.4))';
barEl.style.background = 'linear-gradient(90deg, #ff2d2d, rgba(255,71,87,0.4))';
barEl.style.boxShadow = '0 0 8px rgba(255,71,87,0.4)';
valEl.style.color = '#ff4757';
valEl.style.color = '#ff2d2d';
} else if (m.value > 70) {
barEl.style.background = 'linear-gradient(90deg, #ffa502, rgba(255,165,2,0.4))';
barEl.style.background = 'linear-gradient(90deg, #c9a227, rgba(255,165,2,0.4))';
barEl.style.boxShadow = '0 0 8px rgba(255,165,2,0.3)';
valEl.style.color = '#ffa502';
valEl.style.color = '#c9a227';
} else {
barEl.style.background = '';
barEl.style.boxShadow = '';
@ -960,8 +960,8 @@
});
console.log('%c[JAESWIFT] %cSystems Online',
'color: #00ffc8; font-weight: bold; font-size: 14px;',
'color: #c8d6e5; font-size: 12px;');
'color: #00ff41; font-weight: bold; font-size: 14px;',
'color: #c0c0c0; font-size: 12px;');
});
})();

View file

@ -1,5 +1,5 @@
/* ─── Dynamic Navigation ─── */
/* Fetches nav items from /api/navigation and renders them */
/* ─── Dynamic Navigation with Dropdown Submenus ─── */
/* Fetches nav items from /api/navigation and renders dropdowns */
(function() {
'use strict';
@ -17,39 +17,117 @@
// Determine current page for active state
const path = window.location.pathname;
const search = window.location.search;
const fullPath = path + search;
// Build nav HTML
let html = '';
items.forEach(item => {
const url = item.url || '/';
const label = (item.label || '').toUpperCase();
const children = item.children || [];
const hasChildren = children.length > 0;
// Determine if this link is active
let isActive = false;
if (url === '/' && (path === '/' || path === '/index.html')) {
isActive = true;
} else if (url !== '/' && path.startsWith(url)) {
} else if (url !== '/' && path.startsWith(url.split('?')[0])) {
isActive = true;
}
const activeClass = isActive ? ' active' : '';
const hasDropdownClass = hasChildren ? ' has-dropdown' : '';
// Check if it's an anchor link (homepage sections)
const isAnchor = url.startsWith('#') || url.startsWith('/#');
const href = isAnchor && path !== '/' ? '/' + url.replace(/^\//, '') : url;
html += `<li class="nav-item">`;
html += `<a href="${href}" class="nav-link${activeClass}">${label}</a>`;
html += `<li class="nav-item${hasDropdownClass}">`;
html += `<a href="${href}" class="nav-link${activeClass}">${label}`;
if (hasChildren) {
html += `<span class="dropdown-arrow">▾</span>`;
}
html += `</a>`;
// Render dropdown submenu
if (hasChildren) {
html += `<ul class="dropdown">`;
children.forEach(child => {
const childUrl = child.url || '#';
const childLabel = (child.label || '').toUpperCase();
// Check child active state
let childActive = false;
if (childUrl === fullPath) {
childActive = true;
} else if (childUrl.startsWith('/#') && path === '/') {
childActive = false; // anchor links - don't mark active
}
const childActiveClass = childActive ? ' active' : '';
// Handle anchor links from non-homepage
const childIsAnchor = childUrl.startsWith('#') || childUrl.startsWith('/#');
const childHref = childIsAnchor && path !== '/' ? '/' + childUrl.replace(/^\//, '') : childUrl;
html += `<li><a href="${childHref}" class="dropdown-link${childActiveClass}">${childLabel}</a></li>`;
});
html += `</ul>`;
}
html += `</li>\n`;
});
navMenu.innerHTML = html;
// Bind mobile dropdown toggles after rendering
initMobileDropdowns();
} catch (err) {
console.warn('Nav load failed, keeping existing:', err);
// Don't clear - keep any hardcoded fallback
}
}
// Mobile: tap parent to toggle dropdown
function initMobileDropdowns() {
const isMobile = () => window.innerWidth <= 768;
document.querySelectorAll('.nav-item.has-dropdown > .nav-link').forEach(link => {
link.addEventListener('click', function(e) {
if (!isMobile()) return; // desktop uses hover
const parent = this.parentElement;
const dropdown = parent.querySelector('.dropdown');
if (!dropdown) return;
// If dropdown is closed, prevent navigation and open it
if (!parent.classList.contains('dropdown-open')) {
e.preventDefault();
// Close all other dropdowns
document.querySelectorAll('.nav-item.dropdown-open').forEach(item => {
if (item !== parent) item.classList.remove('dropdown-open');
});
parent.classList.add('dropdown-open');
} else {
// Dropdown already open — allow navigation or close
// If tapped again on same parent link, close dropdown
e.preventDefault();
parent.classList.remove('dropdown-open');
}
});
});
// Close dropdowns when tapping outside
document.addEventListener('click', function(e) {
if (!isMobile()) return;
if (!e.target.closest('.nav-item.has-dropdown')) {
document.querySelectorAll('.nav-item.dropdown-open').forEach(item => {
item.classList.remove('dropdown-open');
});
}
});
}
// Nav toggle (hamburger menu)
function initNavToggle() {
const toggle = document.getElementById('navToggle');
@ -58,6 +136,12 @@
toggle.addEventListener('click', () => {
menu.classList.toggle('open');
toggle.classList.toggle('active');
// Close any open dropdowns when closing menu
if (!menu.classList.contains('open')) {
document.querySelectorAll('.nav-item.dropdown-open').forEach(item => {
item.classList.remove('dropdown-open');
});
}
});
}
}

View file

@ -109,8 +109,8 @@
'locked-in': '◉', 'zen': '☯'
};
const moodColors = {
'focused': '#00ffc8', 'creative': '#a855f7', 'productive': '#22d3ee',
'tired': '#6b7280', 'wired': '#f59e0b', 'chaotic': '#ff4757',
'focused': '#00ff41', 'creative': '#a855f7', 'productive': '#22d3ee',
'tired': '#6b7280', 'wired': '#f59e0b', 'chaotic': '#ff2d2d',
'locked-in': '#00ff88', 'zen': '#818cf8'
};
@ -121,7 +121,7 @@
const moodStr = (post.mood || 'focused').toLowerCase();
const moodIcon = moodIcons[moodStr] || '◈';
const moodColor = moodColors[moodStr] || '#00ffc8';
const moodColor = moodColors[moodStr] || '#00ff41';
const hr = post.heart_rate || post.bpm || 72;
const coffee = '☕'.repeat(Math.min(post.coffee || 0, 10)) +