helldivers-theme 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,205 @@
1
+ // _sass/_layout.scss
2
+
3
+ .terminal-wrapper {
4
+ width: 100%;
5
+ max-width: 1200px;
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: 25px;
9
+ position: relative;
10
+ z-index: 10;
11
+ }
12
+
13
+ .terminal-header {
14
+ display: flex;
15
+ justify-content: space-between;
16
+ align-items: center;
17
+ padding: 20px 30px;
18
+
19
+ // Glass Header
20
+ background: rgba(2, 4, 10, 0.8);
21
+ backdrop-filter: blur(10px);
22
+ border-bottom: 2px solid $hd-gold;
23
+ box-shadow: 0 10px 30px rgba(0,0,0,0.5);
24
+
25
+ // Angled corners for sci-fi look
26
+ clip-path: polygon(0 0, 100% 0, 100% 75%, 98% 100%, 2% 100%, 0 75%);
27
+
28
+ h1 {
29
+ font-family: $font-head;
30
+ font-size: 3.5rem;
31
+ color: $hd-white;
32
+ text-transform: uppercase;
33
+ margin: 0;
34
+ text-shadow: 0 0 10px rgba($hd-gold, 0.6);
35
+ letter-spacing: 2px;
36
+
37
+ span {
38
+ color: $hd-gold;
39
+ }
40
+ }
41
+
42
+ .subtitle {
43
+ color: $hd-cyan;
44
+ font-size: 0.9rem;
45
+ letter-spacing: 3px;
46
+ text-shadow: 0 0 5px $hd-cyan;
47
+ display: block;
48
+ margin-top: 5px;
49
+ }
50
+ }
51
+
52
+ .main-nav {
53
+ display: flex;
54
+ gap: 25px;
55
+ }
56
+
57
+ .nav-link {
58
+ text-decoration: none;
59
+ color: $hd-white;
60
+ font-family: $font-head;
61
+ font-size: 1.4rem;
62
+ letter-spacing: 2px;
63
+ transition: all 0.3s ease;
64
+ text-transform: uppercase;
65
+ position: relative;
66
+
67
+ &:visited { color: $hd-white; }
68
+
69
+ &:hover {
70
+ color: $hd-cyan;
71
+ text-shadow: 0 0 12px $hd-cyan;
72
+ transform: translateY(-2px);
73
+ }
74
+
75
+ &.active {
76
+ color: $hd-gold;
77
+ text-shadow: 0 0 15px $hd-gold;
78
+
79
+ // Little underline indicator
80
+ &::after {
81
+ content: '';
82
+ position: absolute;
83
+ bottom: -5px;
84
+ left: 0;
85
+ width: 100%;
86
+ height: 2px;
87
+ background: $hd-gold;
88
+ box-shadow: 0 0 10px $hd-gold;
89
+ }
90
+ }
91
+ }
92
+
93
+ // Existing Red Pulse
94
+ .live-dot {
95
+ display: inline-block;
96
+ width: 8px;
97
+ height: 8px;
98
+ background-color: $hd-red; // Default to Red (Offline)
99
+ border-radius: 50%;
100
+ margin-left: 6px;
101
+ box-shadow: 0 0 5px $hd-red;
102
+ animation: pulse-red 1.5s infinite;
103
+ transition: all 0.5s ease; // Smooth color change
104
+ flex-shrink: 0; // CRITICAL: Prevents the dot from squishing
105
+ margin: 0; // Remove default margins, use gap in parent instead
106
+ }
107
+
108
+ // New Green Pulse (Added via JS when live)
109
+ .live-dot.is-live {
110
+ background-color: #0f0; // Bright Green
111
+ box-shadow: 0 0 8px #0f0;
112
+ animation: pulse-green 1.5s infinite;
113
+ }
114
+
115
+ @keyframes pulse-red {
116
+ 0% { opacity: 1; transform: scale(1); }
117
+ 50% { opacity: 0.5; transform: scale(1.2); }
118
+ 100% { opacity: 1; transform: scale(1); }
119
+ }
120
+
121
+ @keyframes pulse-green {
122
+ 0% { opacity: 1; transform: scale(1); box-shadow: 0 0 5px #0f0; }
123
+ 50% { opacity: 0.6; transform: scale(1.3); box-shadow: 0 0 15px #0f0; }
124
+ 100% { opacity: 1; transform: scale(1); box-shadow: 0 0 5px #0f0; }
125
+ }
126
+
127
+ // THE MAIN HOLOGRAPHIC CARD
128
+
129
+ // _sass/_layout.scss
130
+
131
+ .terminal-card {
132
+ display: grid;
133
+ // DEFAULT: Sidebar (280px) + Content (Rest)
134
+ // This applies to the Home Page automatically because it has an <aside>
135
+ grid-template-columns: 280px 1fr;
136
+ gap: 25px;
137
+
138
+ // Glassmorphism & Borders
139
+ background: $glass-bg;
140
+ backdrop-filter: blur(12px);
141
+ -webkit-backdrop-filter: blur(12px);
142
+ border: 1px solid $glass-border;
143
+ border-top: 1px solid rgba($hd-gold, 0.6);
144
+ border-left: 1px solid rgba($hd-cyan, 0.3);
145
+ padding: 30px;
146
+ border-radius: 4px;
147
+ box-shadow: 0 20px 50px rgba(0,0,0,0.6), inset 0 0 30px rgba(0, 240, 255, 0.05);
148
+ position: relative;
149
+ min-height: 600px;
150
+
151
+ // Decorative Corners
152
+ &::before, &::after {
153
+ content: '';
154
+ position: absolute;
155
+ width: 20px;
156
+ height: 20px;
157
+ border: 2px solid $hd-cyan;
158
+ transition: all 0.3s ease;
159
+ z-index: 10;
160
+ }
161
+ &::before { top: -2px; left: -2px; border-right: none; border-bottom: none; }
162
+ &::after { bottom: -2px; right: -2px; border-left: none; border-top: none; }
163
+
164
+ &:hover {
165
+ box-shadow: 0 20px 60px rgba(0,0,0,0.7), inset 0 0 40px rgba(0, 240, 255, 0.1);
166
+ &::before, &::after {
167
+ width: 30px;
168
+ height: 30px;
169
+ border-color: $hd-gold;
170
+ box-shadow: 0 0 10px $hd-gold;
171
+ }
172
+ }
173
+
174
+ // --- EXCEPTION: ENLIST & INTEL PAGES ---
175
+ // If the direct child is our specific centered containers, force single column
176
+ &:has(> .enlist-container),
177
+ &:has(> .intel-container),
178
+ &:has(> .comms-container),
179
+ &:has(> .watch-container) {
180
+ grid-template-columns: 1fr; // Remove sidebar column
181
+ justify-items: center; // Center content
182
+ }
183
+ }
184
+
185
+ // Force these containers to span full width and center themselves
186
+ .enlist-container, .intel-container, .comms-container, .watch-container {
187
+ grid-column: 1 / -1; // Span across all columns
188
+ width: 100%;
189
+ max-width: 1200px; // Cap width so it doesn't stretch too far
190
+ margin: 0 auto; // Center horizontally
191
+ display: flex;
192
+ flex-direction: column;
193
+ align-items: center;
194
+ }
195
+
196
+ // Mobile Responsiveness
197
+ @media (max-width: 900px) {
198
+ .terminal-card {
199
+ grid-template-columns: 1fr; // Stack sidebar on top for mobile
200
+ }
201
+
202
+ .enlist-container, .intel-container {
203
+ max-width: 100%;
204
+ }
205
+ }
@@ -0,0 +1,49 @@
1
+ // _sass/_sidebar.scss
2
+
3
+ .sidebar {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 20px;
7
+ }
8
+
9
+ .panel-box {
10
+ background: rgba(0, 0, 0, 0.4);
11
+ border: 1px solid rgba($hd-cyan, 0.2);
12
+ padding: 15px;
13
+ position: relative;
14
+ backdrop-filter: blur(5px);
15
+
16
+ // Small corner accents
17
+ &::before {
18
+ content: '';
19
+ position: absolute;
20
+ top: 0; left: 0;
21
+ width: 10px; height: 10px;
22
+ border-top: 2px solid $hd-gold;
23
+ border-left: 2px solid $hd-gold;
24
+ }
25
+
26
+ h3 {
27
+ font-family: $font-head;
28
+ color: $hd-cyan;
29
+ font-size: 1.2rem;
30
+ margin-bottom: 10px;
31
+ border-bottom: 1px solid rgba($hd-cyan, 0.3);
32
+ padding-bottom: 5px;
33
+ text-transform: uppercase;
34
+ letter-spacing: 1px;
35
+ text-shadow: 0 0 5px rgba($hd-cyan, 0.4);
36
+ }
37
+ }
38
+
39
+ .alert-marquee {
40
+ background: rgba($hd-red, 0.2); // Transparent red
41
+ border: 1px solid $hd-red;
42
+ color: #ffaaaa;
43
+ // ... keep animation code same ...
44
+
45
+ .marquee-content span::before {
46
+ color: $hd-red;
47
+ text-shadow: 0 0 5px $hd-red;
48
+ }
49
+ }
@@ -0,0 +1,19 @@
1
+ // _sass/_variables.scss
2
+
3
+ // Core Colors
4
+ $hd-blue-deep: #02040a; // Almost black space
5
+ $hd-blue-space: #0b1021; // Deep navy
6
+ $hd-gold: #ffd700; // Liberty Gold
7
+ $hd-red: #ff2a2a; // Alert Crimson
8
+ $hd-cyan: #00f0ff; // Holographic Tech Blue
9
+ $hd-white: #e6f2ff; // Slightly blue-tinted white
10
+
11
+ // Fonts
12
+ $font-head: 'Bebas Neue', sans-serif;
13
+ $font-mono: 'Share Tech Mono', monospace;
14
+
15
+ // Effects
16
+ $glass-bg: rgba(11, 16, 33, 0.65); // Transparent dark blue
17
+ $glass-border: rgba(255, 215, 0, 0.3); // Faint gold border
18
+ $glow-gold: 0 0 15px rgba(255, 215, 0, 0.4);
19
+ $glow-cyan: 0 0 15px rgba(0, 240, 255, 0.4);
data/about.md ADDED
@@ -0,0 +1,107 @@
1
+ ---
2
+ layout: default
3
+ title: Intel
4
+ permalink: /about/
5
+ ---
6
+
7
+ <div class="intel-container">
8
+
9
+ <!-- HEADER & QUOTE -->
10
+ <div class="section-header">
11
+ <h2>// SQUAD DOSSIER: {{ site.data.squad.squad_name | upcase }}</h2>
12
+ <div class="line-accent"></div>
13
+ <p class="squad-motto" style="margin-top: 15px; color: var(--hd-cyan); font-style: italic; font-size: 1.1rem;">
14
+ {{ site.data.squad.motto }} <span style="color: #888;">{{ site.data.squad.commander_quote }}</span>
15
+ </p>
16
+ </div>
17
+
18
+ <!-- SMALL GAP AFTER QUOTE -->
19
+ <div class="intel-spacer"></div>
20
+
21
+ <!-- TOP ROW: STATS (Left) + LOADOUTS (Right) -->
22
+ <div class="intel-dashboard-row">
23
+
24
+ <!-- STATS PANEL (Left Side) -->
25
+ <div class="stats-panel-compact">
26
+ <h3>// OPERATIONAL STATS</h3>
27
+ <ul class="stat-list-compact">
28
+ {% for stat in site.data.squad.stats %}
29
+ <li>
30
+ <span class="stat-label">{{ stat.label }}:</span>
31
+ <span class="stat-value" style="{% if stat.color %}color: {{ stat.color }};{% endif %}">
32
+ {{ stat.value }}
33
+ </span>
34
+ </li>
35
+ {% endfor %}
36
+ </ul>
37
+ </div>
38
+
39
+ <!-- LOADOUT TABS (Right Side - Takes remaining space) -->
40
+ <div class="panel-box intel-panel loadout-panel-compact">
41
+ <h3>// TACTICAL LOADOUT DATABASE</h3>
42
+
43
+ <!-- Tab Buttons -->
44
+ <div class="loadout-tabs">
45
+ {% for loadout in site.data.squad.loadouts %}
46
+ <button class="tab-btn {% if forloop.first %}active{% endif %}"
47
+ onclick="openLoadout(event, 'loadout-{{ forloop.index }}')"
48
+ data-color="{{ loadout.theme_color }}">
49
+ {{ loadout.name }}
50
+ </button>
51
+ {% endfor %}
52
+ </div>
53
+
54
+ <!-- Tab Content Areas -->
55
+ <div class="loadout-content-area">
56
+ {% for loadout in site.data.squad.loadouts %}
57
+ <div id="loadout-{{ forloop.index }}"
58
+ class="loadout-tab-content {% if forloop.first %}active{% endif %}"
59
+ data-theme="{{ loadout.theme_color }}">
60
+
61
+ <div class="loadout-header" style="border-bottom-color: var(--hd-{{ loadout.theme_color }}); color: var(--hd-{{ loadout.theme_color }});">
62
+ {{ loadout.name | upcase }}
63
+ </div>
64
+ <ul class="loadout-items">
65
+ {% for item in loadout.items %}
66
+ <li>{{ item }}</li>
67
+ {% endfor %}
68
+ </ul>
69
+ </div>
70
+ {% endfor %}
71
+ </div>
72
+ </div>
73
+ </div>
74
+
75
+ <!-- LARGE GAP BEFORE STORY -->
76
+ <div class="intel-spacer-large"></div>
77
+
78
+ <!-- BOTTOM ROW: ORIGIN STORY (Full Width) -->
79
+ <div class="panel-box intel-panel story-panel-wide">
80
+ <h3>// ORIGIN STORY</h3>
81
+ <div class="story-content">
82
+ {{ site.data.squad.story }}
83
+ </div>
84
+ </div>
85
+
86
+ </div>
87
+
88
+ <!-- JAVASCRIPT FOR TABS -->
89
+ <script>
90
+ function openLoadout(evt, loadoutName) {
91
+ var i, tabcontent, tablinks;
92
+ tabcontent = document.getElementsByClassName("loadout-tab-content");
93
+ for (i = 0; i < tabcontent.length; i++) {
94
+ tabcontent[i].style.display = "none";
95
+ tabcontent[i].classList.remove("active");
96
+ }
97
+ tablinks = document.getElementsByClassName("tab-btn");
98
+ for (i = 0; i < tablinks.length; i++) {
99
+ tablinks[i].className = tablinks[i].className.replace(" active", "");
100
+ }
101
+ document.getElementById(loadoutName).style.display = "block";
102
+ setTimeout(() => {
103
+ document.getElementById(loadoutName).classList.add("active");
104
+ }, 10);
105
+ evt.currentTarget.className += " active";
106
+ }
107
+ </script>
@@ -0,0 +1,8 @@
1
+ ---
2
+ ---
3
+
4
+ @import "variables";
5
+ @import "base";
6
+ @import "layout";
7
+ @import "components";
8
+ @import "sidebar";
Binary file
data/assets/js/main.js ADDED
@@ -0,0 +1,57 @@
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+
3
+ // --- 1. CONFIGURATION ---
4
+ // Official Helldivers 2 Steam News Feed URL
5
+ const RSS_URL = 'https://store.steampowered.com/feeds/news/app/553850/';
6
+ const FEED_CONTAINER = document.getElementById('rss-feed');
7
+
8
+ // --- 2. FETCH UPDATES FUNCTION ---
9
+ async function loadGameUpdates() {
10
+ try {
11
+ // We use rss2json.com to convert the XML feed to JSON and bypass CORS
12
+ const apiUrl = `https://api.rss2json.com/v1/api.json?rss_url=${encodeURIComponent(RSS_URL)}`;
13
+
14
+ const response = await fetch(apiUrl);
15
+
16
+ if (!response.ok) throw new Error('Network response was not ok');
17
+
18
+ const data = await response.json();
19
+
20
+ // Clear the loading text
21
+ FEED_CONTAINER.innerHTML = '';
22
+
23
+ // Create a list for the items
24
+ const list = document.createElement('ul');
25
+ list.className = 'news-list';
26
+
27
+ // Loop through the top 5 news items
28
+ data.items.slice(0, 5).forEach(item => {
29
+ const li = document.createElement('li');
30
+
31
+ // Format the date nicely
32
+ const dateObj = new Date(item.pubDate);
33
+ const dateStr = dateObj.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
34
+
35
+ li.innerHTML = `
36
+ <span class="news-date">[${dateStr}]</span>
37
+ <a href="${item.link}" target="_blank" class="news-link">${item.title}</a>
38
+ `;
39
+ list.appendChild(li);
40
+ });
41
+
42
+ FEED_CONTAINER.appendChild(list);
43
+
44
+ } catch (error) {
45
+ console.error('Failed to load updates:', error);
46
+ FEED_CONTAINER.innerHTML = `
47
+ <div class="error-msg">
48
+ ⚠️ COMM LINK INTERRUPTED<br>
49
+ <small>Unable to reach Super Earth servers.</small>
50
+ </div>
51
+ `;
52
+ }
53
+ }
54
+
55
+ // --- 3. INITIALIZE ---
56
+ loadGameUpdates();
57
+ });
@@ -0,0 +1,119 @@
1
+ // assets/js/bg-particles.js
2
+
3
+ const canvas = document.createElement('canvas');
4
+ const ctx = canvas.getContext('2d');
5
+ document.body.appendChild(canvas);
6
+
7
+ // Style the canvas to sit behind everything
8
+ canvas.style.position = 'fixed';
9
+ canvas.style.top = '0';
10
+ canvas.style.left = '0';
11
+ canvas.style.width = '100%';
12
+ canvas.style.height = '100%';
13
+ canvas.style.zIndex = '0'; // Behind the content
14
+ canvas.style.pointerEvents = 'none'; // Click through it
15
+ canvas.style.opacity = '0.4'; // Subtle effect
16
+
17
+ let width, height;
18
+ let particles = [];
19
+
20
+ // Configuration
21
+ const particleCount = 150; // Fewer particles needed for Canvas to look full
22
+ const connectionDistance = 100;
23
+ const baseHue = 50; // Cyan/Blue (Helldivers Tech vibe)
24
+
25
+ // Resize handling
26
+ function resize() {
27
+ width = window.innerWidth;
28
+ height = window.innerHeight;
29
+ canvas.width = width;
30
+ canvas.height = height;
31
+ }
32
+ window.addEventListener('resize', resize);
33
+ resize();
34
+
35
+ // Particle Class
36
+ class Particle {
37
+ constructor() {
38
+ this.reset();
39
+ }
40
+
41
+ reset() {
42
+ this.x = Math.random() * width;
43
+ this.y = Math.random() * height;
44
+ this.vx = (Math.random() - 0.5) * 0.5; // Slow drift velocity
45
+ this.vy = (Math.random() - 0.5) * 0.5;
46
+ this.size = Math.random() * 2 + 1;
47
+ // Vary hue slightly for depth
48
+ this.hue = baseHue + (Math.random() * 40 - 20);
49
+ this.life = Math.random() * 100;
50
+ this.maxLife = 100 + Math.random() * 100;
51
+ }
52
+
53
+ update() {
54
+ this.x += this.vx;
55
+ this.y += this.vy;
56
+ this.life++;
57
+
58
+ // Wrap around screen
59
+ if (this.x < 0) this.x = width;
60
+ if (this.x > width) this.x = 0;
61
+ if (this.y < 0) this.y = height;
62
+ if (this.y > height) this.y = 0;
63
+
64
+ // Fade in/out cycle
65
+ if (this.life > this.maxLife) {
66
+ this.reset();
67
+ this.life = 0;
68
+ }
69
+ }
70
+
71
+ draw() {
72
+ const opacity = Math.sin((this.life / this.maxLife) * Math.PI); // Smooth fade
73
+ ctx.fillStyle = `hsla(${this.hue}, 100%, 60%, ${opacity})`;
74
+ ctx.beginPath();
75
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
76
+ ctx.fill();
77
+ }
78
+ }
79
+
80
+ // Initialize
81
+ for (let i = 0; i < particleCount; i++) {
82
+ particles.push(new Particle());
83
+ }
84
+
85
+ // Animation Loop
86
+ function animate() {
87
+ ctx.clearRect(0, 0, width, height);
88
+
89
+ // Draw particles
90
+ particles.forEach(p => {
91
+ p.update();
92
+ p.draw();
93
+ });
94
+
95
+ // Optional: Draw subtle connections (lines) between close particles
96
+ // Only do this if performance allows (comment out if too slow)
97
+ /*
98
+ ctx.strokeStyle = `hsla(${baseHue}, 100%, 50%, 0.1)`;
99
+ ctx.lineWidth = 0.5;
100
+ for (let i = 0; i < particles.length; i++) {
101
+ for (let j = i + 1; j < particles.length; j++) {
102
+ const dx = particles[i].x - particles[j].x;
103
+ const dy = particles[i].y - particles[j].y;
104
+ const dist = Math.sqrt(dx * dx + dy * dy);
105
+
106
+ if (dist < connectionDistance) {
107
+ ctx.beginPath();
108
+ ctx.moveTo(particles[i].x, particles[i].y);
109
+ ctx.lineTo(particles[j].x, particles[j].y);
110
+ ctx.stroke();
111
+ }
112
+ }
113
+ }
114
+ */
115
+
116
+ requestAnimationFrame(animate);
117
+ }
118
+
119
+ animate();
@@ -0,0 +1,110 @@
1
+ // assets/js/profile-modal.js
2
+
3
+ document.addEventListener('DOMContentLoaded', () => {
4
+ const dataElement = document.getElementById('leader-data-json');
5
+
6
+ if (!dataElement) {
7
+ console.error("❌ Error: Hidden data div #leader-data-json not found!");
8
+ return;
9
+ }
10
+
11
+ let squadData = [];
12
+
13
+ try {
14
+ // Parse the JSON from the hidden div
15
+ squadData = JSON.parse(dataElement.textContent.trim());
16
+ console.log("✅ Leader data loaded:", squadData);
17
+ } catch (error) {
18
+ console.error("❌ CRITICAL ERROR: Failed to parse leader data.");
19
+ console.error("Raw content causing error:", dataElement.textContent);
20
+ console.error("Error details:", error);
21
+ alert("Error loading profile data. Check browser console (F12).");
22
+ return;
23
+ }
24
+
25
+ // Define Open Function
26
+ window.openLeaderProfile = function(name) {
27
+ console.log("🔍 Searching for:", name);
28
+ const data = squadData.find(l => l.name === name);
29
+
30
+ if (!data) {
31
+ console.error("❌ Leader not found in data:", name);
32
+ return;
33
+ }
34
+
35
+ console.log("✅ Found data:", data);
36
+
37
+ // --- 1. Populate Basic Fields ---
38
+ document.getElementById('modal-name').innerText = data.name;
39
+ document.getElementById('modal-role').innerText = data.role;
40
+ document.getElementById('modal-img').src = data.image;
41
+ document.getElementById('modal-primary').innerText = data.primary || "Unknown";
42
+ document.getElementById('modal-stratagem').innerText = data.stratagem || "Unknown";
43
+ document.getElementById('modal-missions').innerText = data.missions || "0";
44
+ document.getElementById('modal-kd').innerText = data.kd || "0.0";
45
+ document.getElementById('modal-specialty').innerText = data.specialty || "None";
46
+
47
+ // --- 2. Populate Bio (NEW) ---
48
+ const bioElement = document.getElementById('modal-bio');
49
+ if (bioElement) {
50
+ bioElement.innerText = data.bio || "No service record available.";
51
+ }
52
+
53
+ // --- 3. Populate Medals (NEW) ---
54
+ const medalsContainer = document.getElementById('modal-medals');
55
+ if (medalsContainer && data.medals) {
56
+ medalsContainer.innerHTML = ''; // Clear old content
57
+ data.medals.forEach(medal => {
58
+ const badge = document.createElement('span');
59
+ badge.className = 'medal-badge';
60
+ badge.innerText = medal;
61
+ medalsContainer.appendChild(badge);
62
+ });
63
+ } else if (medalsContainer) {
64
+ medalsContainer.innerHTML = '<span style="color:#666; font-style:italic;">No decorations recorded.</span>';
65
+ }
66
+
67
+ // --- 4. Show Modal Animation ---
68
+ const modal = document.getElementById('leader-modal');
69
+ modal.style.display = 'flex';
70
+
71
+ // Force browser reflow to ensure transition triggers
72
+ void modal.offsetWidth;
73
+
74
+ modal.style.opacity = '1';
75
+ modal.querySelector('.modal-terminal').style.transform = 'scale(1)';
76
+
77
+ // Prevent background scrolling
78
+ document.body.style.overflow = 'hidden';
79
+ };
80
+
81
+ // Define Close Function
82
+ window.closeLeaderProfile = function(event) {
83
+ // Only close if clicking the overlay background or the X button
84
+ if (event.target.id === 'leader-modal' || event.target.classList.contains('modal-close')) {
85
+ const modal = document.getElementById('leader-modal');
86
+
87
+ // Fade out
88
+ modal.style.opacity = '0';
89
+ modal.querySelector('.modal-terminal').style.transform = 'scale(0.9)';
90
+
91
+ // Wait for transition to finish before hiding display
92
+ setTimeout(() => {
93
+ modal.style.display = 'none';
94
+ document.body.style.overflow = 'auto'; // Restore scrolling
95
+ console.log("✅ Modal closed, clicks restored.");
96
+ }, 300); // Must match CSS transition time
97
+ }
98
+ };
99
+
100
+ // Escape Key Listener
101
+ document.addEventListener('keydown', (e) => {
102
+ if (e.key === "Escape") {
103
+ const modal = document.getElementById('leader-modal');
104
+ if (modal.style.display === 'flex') {
105
+ // Simulate a click on the overlay to trigger close logic
106
+ window.closeLeaderProfile({ target: { id: 'leader-modal' } });
107
+ }
108
+ }
109
+ });
110
+ });
data/assets/main.scss ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ ---
3
+ @import "variables";
4
+ @import "base";
5
+ @import "layout";
6
+ @import "components";
7
+ @import "sidebar";