my personal site
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

http://127.0.0.1:3000/projects.html

+696 -118
+1
.vscode/settings.json
··· 1 1 { 2 + "livePreview.defaultPreviewPath": "/resume.html" 2 3 }
+60 -10
index.html
··· 26 26 <body> 27 27 <a href="#main-content" class="skip-link">Skip to main content</a> 28 28 <header> 29 - <nav class="container" role="navigation" aria-label="Main navigation"> 30 - <a href="index.html" class="brand">Jack Hannon</a> 31 - <div class="header-controls"> 32 - <ul class="menu" role="menubar"> 29 + <nav class="container" role="navigation" aria-label="Main navigation" style="display: flex; align-items: center; justify-content: space-between;"> 30 + <a href="index.html" class="brand" id="brand">Jack Hannon</a> 31 + <div class="header-controls" id="header-controls" style="margin-left: auto; display: flex; align-items: center;"> 32 + <button class="mobile-nav-toggle" aria-controls="header-controls" aria-expanded="false" aria-label="Open navigation menu"> 33 + <span class="hamburger-icon"></span> 34 + </button> 35 + <ul class="menu" role="menubar" id="main-menu"> 33 36 <li><a href="index.html" class="active" role="menuitem" tabindex="0">About</a></li> 34 37 <li><a href="projects.html" role="menuitem" tabindex="0">Projects</a></li> 35 38 <li><a href="resume.html" role="menuitem" tabindex="0">Resume</a></li> ··· 41 44 <div id="accessibility-menu" class="accessibility-menu" style="display:none;" role="menu" aria-label="Accessibility options"> 42 45 <div class="access-section"> 43 46 <div class="access-label">Theme</div> 44 - <label class="access-radio"><input type="radio" name="theme-mode" value="system" /> System</label> 45 - <label class="access-radio"><input type="radio" name="theme-mode" value="light" /> Light</label> 46 - <label class="access-radio"><input type="radio" name="theme-mode" value="dark" /> Dark</label> 47 + <div class="access-theme-btns"> 48 + <label class="access-radio"><input type="radio" name="theme-mode" value="system" /> System</label> 49 + <label class="access-radio"><input type="radio" name="theme-mode" value="light" /> Light</label> 50 + <label class="access-radio"><input type="radio" name="theme-mode" value="dark" /> Dark</label> 51 + </div> 47 52 </div> 48 53 <div class="access-section"> 49 54 <label class="access-toggle"><input type="checkbox" id="high-contrast-toggle" /> High Contrast</label> 50 55 </div> 51 56 <div class="access-section access-textsize"> 52 57 <div class="access-label">Text Size</div> 53 - <button type="button" id="text-decrease" class="textsize-round" aria-label="Decrease text size">–</button> 54 - <span id="text-reset" class="textsize-reset" tabindex="0" role="button" aria-label="Reset text size">Reset</span> 55 - <button type="button" id="text-increase" class="textsize-round" aria-label="Increase text size">+</button> 58 + <div class="access-textsize-btns"> 59 + <button type="button" id="text-decrease" class="textsize-round" aria-label="Decrease text size">–</button> 60 + <span id="text-reset" class="textsize-reset" tabindex="0" role="button" aria-label="Reset text size">Reset</span> 61 + <button type="button" id="text-increase" class="textsize-round" aria-label="Increase text size">+</button> 62 + </div> 56 63 </div> 57 64 </div> 58 65 </div> ··· 78 85 </div> 79 86 </section> 80 87 </main> 88 + <script> 89 + // Mobile nav toggle functionality (brand/menu swap) 90 + document.addEventListener('DOMContentLoaded', function() { 91 + const navToggle = document.querySelector('.mobile-nav-toggle'); 92 + const brand = document.getElementById('brand'); 93 + const menu = document.getElementById('main-menu'); 94 + const headerControls = document.getElementById('header-controls'); 95 + function isMobile() { 96 + return window.innerWidth <= 650; 97 + } 98 + function showMenuInBar(show) { 99 + if (isMobile()) { 100 + if (show) { 101 + brand.style.display = 'none'; 102 + menu.style.display = 'flex'; 103 + menu.classList.add('menu-in-bar'); 104 + } else { 105 + brand.style.display = ''; 106 + menu.style.display = ''; 107 + menu.classList.remove('menu-in-bar'); 108 + } 109 + } else { 110 + brand.style.display = ''; 111 + menu.style.display = ''; 112 + menu.classList.remove('menu-in-bar'); 113 + } 114 + } 115 + if (navToggle && brand && menu) { 116 + navToggle.addEventListener('click', function() { 117 + const expanded = navToggle.getAttribute('aria-expanded') === 'true'; 118 + navToggle.setAttribute('aria-expanded', !expanded); 119 + showMenuInBar(!expanded); 120 + }); 121 + window.addEventListener('resize', function() { 122 + // Reset on resize 123 + if (!isMobile()) { 124 + showMenuInBar(false); 125 + navToggle.setAttribute('aria-expanded', false); 126 + } 127 + }); 128 + } 129 + }); 130 + </script> 81 131 <script src="script.js"></script> 82 132 </body> 83 133 </html>
+60 -10
projects.html
··· 25 25 <body> 26 26 <a href="#main-content" class="skip-link">Skip to main content</a> 27 27 <header> 28 - <nav class="container" role="navigation" aria-label="Main navigation"> 29 - <a href="index.html" class="brand">Jack Hannon</a> 30 - <div class="header-controls"> 31 - <ul class="menu" role="menubar"> 28 + <nav class="container" role="navigation" aria-label="Main navigation" style="display: flex; align-items: center; justify-content: space-between;"> 29 + <a href="index.html" class="brand" id="brand">Jack Hannon</a> 30 + <div class="header-controls" id="header-controls" style="margin-left: auto; display: flex; align-items: center;"> 31 + <button class="mobile-nav-toggle" aria-controls="header-controls" aria-expanded="false" aria-label="Open navigation menu"> 32 + <span class="hamburger-icon"></span> 33 + </button> 34 + <ul class="menu" role="menubar" id="main-menu"> 32 35 <li><a href="index.html" role="menuitem" tabindex="0">About</a></li> 33 36 <li><a href="projects.html" class="active" role="menuitem" tabindex="0">Projects</a></li> 34 37 <li><a href="resume.html" role="menuitem" tabindex="0">Resume</a></li> ··· 40 43 <div id="accessibility-menu" class="accessibility-menu" style="display:none;" role="menu" aria-label="Accessibility options"> 41 44 <div class="access-section"> 42 45 <div class="access-label">Theme</div> 43 - <label class="access-radio"><input type="radio" name="theme-mode" value="system" /> System</label> 44 - <label class="access-radio"><input type="radio" name="theme-mode" value="light" /> Light</label> 45 - <label class="access-radio"><input type="radio" name="theme-mode" value="dark" /> Dark</label> 46 + <div class="access-theme-btns"> 47 + <label class="access-radio"><input type="radio" name="theme-mode" value="system" /> System</label> 48 + <label class="access-radio"><input type="radio" name="theme-mode" value="light" /> Light</label> 49 + <label class="access-radio"><input type="radio" name="theme-mode" value="dark" /> Dark</label> 50 + </div> 46 51 </div> 47 52 <div class="access-section"> 48 53 <label class="access-toggle"><input type="checkbox" id="high-contrast-toggle" /> High Contrast</label> 49 54 </div> 50 55 <div class="access-section access-textsize"> 51 56 <div class="access-label">Text Size</div> 52 - <button type="button" id="text-decrease" class="textsize-round" aria-label="Decrease text size">–</button> 53 - <span id="text-reset" class="textsize-reset" tabindex="0" role="button" aria-label="Reset text size">Reset</span> 54 - <button type="button" id="text-increase" class="textsize-round" aria-label="Increase text size">+</button> 57 + <div class="access-textsize-btns"> 58 + <button type="button" id="text-decrease" class="textsize-round" aria-label="Decrease text size">–</button> 59 + <span id="text-reset" class="textsize-reset" tabindex="0" role="button" aria-label="Reset text size">Reset</span> 60 + <button type="button" id="text-increase" class="textsize-round" aria-label="Increase text size">+</button> 61 + </div> 55 62 </div> 56 63 </div> 57 64 </div> ··· 91 98 </div> 92 99 </section> 93 100 </main> 101 + <script> 102 + // Mobile nav toggle functionality (brand/menu swap) 103 + document.addEventListener('DOMContentLoaded', function() { 104 + const navToggle = document.querySelector('.mobile-nav-toggle'); 105 + const brand = document.getElementById('brand'); 106 + const menu = document.getElementById('main-menu'); 107 + const headerControls = document.getElementById('header-controls'); 108 + function isMobile() { 109 + return window.innerWidth <= 600; 110 + } 111 + function showMenuInBar(show) { 112 + if (isMobile()) { 113 + if (show) { 114 + brand.style.display = 'none'; 115 + menu.style.display = 'flex'; 116 + menu.classList.add('menu-in-bar'); 117 + } else { 118 + brand.style.display = ''; 119 + menu.style.display = ''; 120 + menu.classList.remove('menu-in-bar'); 121 + } 122 + } else { 123 + brand.style.display = ''; 124 + menu.style.display = ''; 125 + menu.classList.remove('menu-in-bar'); 126 + } 127 + } 128 + if (navToggle && brand && menu) { 129 + navToggle.addEventListener('click', function() { 130 + const expanded = navToggle.getAttribute('aria-expanded') === 'true'; 131 + navToggle.setAttribute('aria-expanded', !expanded); 132 + showMenuInBar(!expanded); 133 + }); 134 + window.addEventListener('resize', function() { 135 + // Reset on resize 136 + if (!isMobile()) { 137 + showMenuInBar(false); 138 + navToggle.setAttribute('aria-expanded', false); 139 + } 140 + }); 141 + } 142 + }); 143 + </script> 94 144 <script src="script.js"></script> 95 145 </body> 96 146 </html>
+60 -10
resume.html
··· 25 25 <body> 26 26 <a href="#main-content" class="skip-link">Skip to main content</a> 27 27 <header> 28 - <nav class="container" role="navigation" aria-label="Main navigation"> 29 - <a href="index.html" class="brand">Jack Hannon</a> 30 - <div class="header-controls"> 31 - <ul class="menu" role="menubar"> 28 + <nav class="container" role="navigation" aria-label="Main navigation" style="display: flex; align-items: center; justify-content: space-between;"> 29 + <a href="index.html" class="brand" id="brand">Jack Hannon</a> 30 + <div class="header-controls" id="header-controls" style="margin-left: auto; display: flex; align-items: center;"> 31 + <button class="mobile-nav-toggle" aria-controls="header-controls" aria-expanded="false" aria-label="Open navigation menu"> 32 + <span class="hamburger-icon"></span> 33 + </button> 34 + <ul class="menu" role="menubar" id="main-menu"> 32 35 <li><a href="index.html" role="menuitem" tabindex="0">About</a></li> 33 36 <li><a href="projects.html" role="menuitem" tabindex="0">Projects</a></li> 34 37 <li><a href="resume.html" class="active" role="menuitem" tabindex="0">Resume</a></li> ··· 40 43 <div id="accessibility-menu" class="accessibility-menu" style="display:none;" role="menu" aria-label="Accessibility options"> 41 44 <div class="access-section"> 42 45 <div class="access-label">Theme</div> 43 - <label class="access-radio"><input type="radio" name="theme-mode" value="system" /> System</label> 44 - <label class="access-radio"><input type="radio" name="theme-mode" value="light" /> Light</label> 45 - <label class="access-radio"><input type="radio" name="theme-mode" value="dark" /> Dark</label> 46 + <div class="access-theme-btns"> 47 + <label class="access-radio"><input type="radio" name="theme-mode" value="system" /> System</label> 48 + <label class="access-radio"><input type="radio" name="theme-mode" value="light" /> Light</label> 49 + <label class="access-radio"><input type="radio" name="theme-mode" value="dark" /> Dark</label> 50 + </div> 46 51 </div> 47 52 <div class="access-section"> 48 53 <label class="access-toggle"><input type="checkbox" id="high-contrast-toggle" /> High Contrast</label> 49 54 </div> 50 55 <div class="access-section access-textsize"> 51 56 <div class="access-label">Text Size</div> 52 - <button type="button" id="text-decrease" class="textsize-round" aria-label="Decrease text size">–</button> 53 - <span id="text-reset" class="textsize-reset" tabindex="0" role="button" aria-label="Reset text size">Reset</span> 54 - <button type="button" id="text-increase" class="textsize-round" aria-label="Increase text size">+</button> 57 + <div class="access-textsize-btns"> 58 + <button type="button" id="text-decrease" class="textsize-round" aria-label="Decrease text size">–</button> 59 + <span id="text-reset" class="textsize-reset" tabindex="0" role="button" aria-label="Reset text size">Reset</span> 60 + <button type="button" id="text-increase" class="textsize-round" aria-label="Increase text size">+</button> 61 + </div> 55 62 </div> 56 63 </div> 57 64 </div> ··· 118 125 <li>Eagle Scout (Boy Scouts of America), July 2021</li> 119 126 </ul> 120 127 </main> 128 + <script> 129 + // Mobile nav toggle functionality (brand/menu swap) 130 + document.addEventListener('DOMContentLoaded', function() { 131 + const navToggle = document.querySelector('.mobile-nav-toggle'); 132 + const brand = document.getElementById('brand'); 133 + const menu = document.getElementById('main-menu'); 134 + const headerControls = document.getElementById('header-controls'); 135 + function isMobile() { 136 + return window.innerWidth <= 600; 137 + } 138 + function showMenuInBar(show) { 139 + if (isMobile()) { 140 + if (show) { 141 + brand.style.display = 'none'; 142 + menu.style.display = 'flex'; 143 + menu.classList.add('menu-in-bar'); 144 + } else { 145 + brand.style.display = ''; 146 + menu.style.display = ''; 147 + menu.classList.remove('menu-in-bar'); 148 + } 149 + } else { 150 + brand.style.display = ''; 151 + menu.style.display = ''; 152 + menu.classList.remove('menu-in-bar'); 153 + } 154 + } 155 + if (navToggle && brand && menu) { 156 + navToggle.addEventListener('click', function() { 157 + const expanded = navToggle.getAttribute('aria-expanded') === 'true'; 158 + navToggle.setAttribute('aria-expanded', !expanded); 159 + showMenuInBar(!expanded); 160 + }); 161 + window.addEventListener('resize', function() { 162 + // Reset on resize 163 + if (!isMobile()) { 164 + showMenuInBar(false); 165 + navToggle.setAttribute('aria-expanded', false); 166 + } 167 + }); 168 + } 169 + }); 170 + </script> 121 171 <script src="script.js"></script> 122 172 </body> 123 173 </html>
+113 -28
script.js
··· 32 32 const textDecreaseBtn = menu ? menu.querySelector('#text-decrease') : null; 33 33 const textResetBtn = menu ? menu.querySelector('#text-reset') : null; 34 34 35 - // Utility functions 36 35 function setTheme(mode) { 37 36 root.classList.remove('dark-theme', 'light-theme'); 38 37 if (mode === 'dark') { ··· 45 44 // System: remove explicit theme, use OS preference 46 45 localStorage.setItem('theme', 'system'); 47 46 } 47 + // Update selected state for mobile button UI 48 + themeRadioLabels.forEach(label => { 49 + const input = label.querySelector('input[type="radio"]'); 50 + if (input) { 51 + if (input.value === mode) { 52 + label.classList.add('selected'); 53 + } else { 54 + label.classList.remove('selected'); 55 + } 56 + } 57 + }); 48 58 } 49 59 50 60 // Utility to announce status to screen readers ··· 93 103 } 94 104 95 105 function getFontSize() { 96 - const val = parseInt(getComputedStyle(root).getPropertyValue('--base-font-size'), 10); 106 + const val = parseInt(localStorage.getItem('fontSize'), 10); 97 107 return isNaN(val) ? 16 : val; 98 108 } 99 109 100 110 function openMenu() { 111 + if (!menu) return; 101 112 menu.style.display = 'flex'; 102 113 accessibilityBtn.setAttribute('aria-expanded', 'true'); 103 114 menuOpen = true; ··· 108 119 }, 0); 109 120 } 110 121 function closeMenu() { 122 + if (!menu) return; 111 123 menu.style.display = 'none'; 112 124 accessibilityBtn.setAttribute('aria-expanded', 'false'); 113 125 menuOpen = false; ··· 130 142 }); 131 143 } 132 144 133 - // Theme radio logic 134 - allThemeRadios.forEach(radio => { 135 - radio.addEventListener('change', function() { 136 - if (this.checked) setTheme(this.value); 145 + // Theme selection logic 146 + function isMobile() { 147 + return window.matchMedia && window.matchMedia('(max-width: 650px)').matches; 148 + } 149 + 150 + if (isMobile()) { 151 + // On mobile, treat .access-radio as buttons 152 + themeRadioLabels.forEach(label => { 153 + label.addEventListener('click', function(e) { 154 + e.preventDefault(); 155 + const input = label.querySelector('input[type="radio"]'); 156 + if (input && !input.disabled) { 157 + setTheme(input.value); 158 + } 159 + }); 160 + label.setAttribute('tabindex', '0'); 161 + label.addEventListener('keydown', function(e) { 162 + if (e.key === 'Enter' || e.key === ' ') { 163 + e.preventDefault(); 164 + const input = label.querySelector('input[type="radio"]'); 165 + if (input && !input.disabled) { 166 + setTheme(input.value); 167 + } 168 + } 169 + }); 137 170 }); 138 - }); 171 + } else { 172 + // Desktop: radio logic 173 + allThemeRadios.forEach(radio => { 174 + radio.addEventListener('change', function() { 175 + if (this.checked) setTheme(this.value); 176 + }); 177 + }); 178 + } 139 179 140 180 // High contrast toggle logic 141 181 highContrastToggle && highContrastToggle.addEventListener('change', function() { ··· 156 196 textResetBtn && textResetBtn.addEventListener('click', function() { 157 197 setFontSize(16); 158 198 }); 199 + textResetBtn && textResetBtn.addEventListener('keydown', function(e) { 200 + if (e.key === 'Enter' || e.key === ' ') { 201 + e.preventDefault(); 202 + setFontSize(16); 203 + } 204 + }); 159 205 160 206 // Close menu on outside click or Escape 161 207 document.addEventListener('click', function(e) { 162 - if (menuOpen && !menu.contains(e.target) && e.target !== accessibilityBtn) { 208 + if (menuOpen && menu && !menu.contains(e.target) && e.target !== accessibilityBtn) { 163 209 closeMenu(); 164 210 } 165 211 }); ··· 169 215 accessibilityBtn && accessibilityBtn.focus(); 170 216 } 171 217 // Keyboard navigation: arrow keys 172 - if (menuOpen && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) { 173 - const focusables = Array.from(menu.querySelectorAll('input, button')); 218 + if (menuOpen && menu && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) { 219 + const focusables = Array.from(menu.querySelectorAll('input, button, [role="button"], .access-radio')); 174 220 const idx = focusables.indexOf(document.activeElement); 175 221 let nextIdx = idx; 176 222 if (e.key === 'ArrowDown') nextIdx = (idx + 1) % focusables.length; ··· 181 227 }); 182 228 183 229 // On load: apply saved theme, high contrast, and font size 184 - (function() { 230 + window.addEventListener('DOMContentLoaded', function() { 231 + // Theme 232 + const savedTheme = localStorage.getItem('theme') || 'system'; 233 + setTheme(savedTheme); 234 + allThemeRadios.forEach(radio => { radio.checked = (radio.value === savedTheme); }); 235 + // High contrast 185 236 const highContrast = localStorage.getItem('highContrast') === '1'; 186 - setHighContrast(highContrast); 237 + if (highContrast) setHighContrast(true); 187 238 if (highContrastToggle) highContrastToggle.checked = highContrast; 188 - if (!highContrast) { 189 - const savedTheme = localStorage.getItem('theme'); 190 - if (savedTheme === 'dark' || savedTheme === 'light') { 191 - setTheme(savedTheme); 192 - allThemeRadios.forEach(radio => { radio.checked = (radio.value === savedTheme); }); 193 - } else { 194 - setTheme('system'); 195 - allThemeRadios.forEach(radio => { radio.checked = (radio.value === 'system'); }); 196 - } 197 - } 198 - const savedFontSize = parseInt(localStorage.getItem('fontSize'), 10); 199 - if (savedFontSize && savedFontSize !== 16) { 200 - setFontSize(savedFontSize); 201 - } 202 - })(); 203 - })(); 239 + // Font size 240 + setFontSize(getFontSize()); 241 + }); 242 + })(); 243 + 244 + // Dynamically load header.html into #header-include 245 + function loadHeader() { 246 + const headerDiv = document.getElementById('header-include'); 247 + if (headerDiv) { 248 + fetch('header.html') 249 + .then(response => response.text()) 250 + .then(html => { 251 + headerDiv.insertAdjacentHTML('beforeend', html); 252 + // Set active tab in header navigation 253 + const navLinks = headerDiv.querySelectorAll('.menu a'); 254 + const page = window.location.pathname.split('/').pop() || 'index.html'; 255 + navLinks.forEach(link => { 256 + // Normalize both for comparison 257 + const linkHref = link.getAttribute('href'); 258 + if ( 259 + (page === '' && linkHref === 'index.html') || 260 + (page === linkHref) || 261 + (page === 'index.html' && linkHref === 'index.html') 262 + ) { 263 + link.classList.add('active'); 264 + } else { 265 + link.classList.remove('active'); 266 + } 267 + }); 268 + // Run scripts that depend on the loaded header 269 + initializeAccessibility(); 270 + initializeMobileNav(); 271 + }); 272 + } 273 + } 274 + document.addEventListener('DOMContentLoaded', loadHeader); 275 + 276 + // Mobile navigation toggle 277 + function initializeMobileNav() { 278 + const toggleBtn = document.getElementById('mobile-nav-toggle'); 279 + const navContent = document.getElementById('header-controls'); 280 + 281 + if (toggleBtn && navContent) { 282 + toggleBtn.addEventListener('click', () => { 283 + const isExpanded = toggleBtn.getAttribute('aria-expanded') === 'true'; 284 + toggleBtn.setAttribute('aria-expanded', !isExpanded); 285 + navContent.classList.toggle('mobile-nav-open'); 286 + }); 287 + } 288 + }
+402 -60
style.css
··· 110 110 .header-controls { 111 111 display: flex; 112 112 align-items: center; 113 - gap: 1.2rem; 113 + gap: 0.5rem; 114 114 } 115 115 116 116 /* Add a divider for the right-side controls */ 117 117 .header-divider { 118 - width: 1.5px; 118 + width: 0; 119 119 height: 2.2rem; 120 - background: #e0e0e7; 120 + border-left: var(--nav-border); 121 121 margin: 0 0.3rem 0 1.1rem; 122 122 align-self: center; 123 123 border-radius: 2px; 124 + background: none; 125 + transition: border-color 0.2s; 124 126 } 125 127 .dark-theme .header-divider { 126 - background: #333a40; 128 + border-left: var(--nav-border); 129 + } 130 + .high-contrast .header-divider { 131 + border-left: 2px solid #ffff00 !important; 132 + background: none !important; 127 133 } 128 134 129 135 /* Accessibility icon (Aa) styling, no button look */ ··· 196 202 display: flex; 197 203 align-items: center; 198 204 height: 100%; 205 + white-space: nowrap; 199 206 } 200 207 .menu { 201 208 list-style: none; ··· 291 298 transform: translate(-50%, -50%); 292 299 } 293 300 301 + /* Mobile Nav Toggle */ 302 + .mobile-nav-toggle { 303 + display: none; /* Hidden on desktop */ 304 + background: none; 305 + border: none; 306 + cursor: pointer; 307 + padding: 0.5rem; 308 + z-index: 1002; /* Ensure above nav */ 309 + } 310 + 311 + .hamburger-icon { 312 + display: block; 313 + width: 24px; 314 + height: 2px; 315 + background-color: var(--text-color); 316 + position: relative; 317 + transition: background-color 0.2s ease-in-out; 318 + } 319 + 320 + .hamburger-icon::before, 321 + .hamburger-icon::after { 322 + content: ''; 323 + position: absolute; 324 + left: 0; 325 + width: 100%; 326 + height: 2px; 327 + background-color: var(--text-color); 328 + transition: transform 0.2s ease-in-out, top 0.2s ease-in-out; 329 + } 330 + 331 + .hamburger-icon::before { 332 + top: -8px; 333 + } 334 + 335 + .hamburger-icon::after { 336 + top: 8px; 337 + } 338 + 339 + /* "X" icon when nav is open */ 340 + .mobile-nav-toggle[aria-expanded="true"] .hamburger-icon { 341 + background-color: transparent; /* Middle bar disappears */ 342 + } 343 + 344 + .mobile-nav-toggle[aria-expanded="true"] .hamburger-icon::before { 345 + transform: rotate(45deg); 346 + top: 0; 347 + } 348 + 349 + .mobile-nav-toggle[aria-expanded="true"] .hamburger-icon::after { 350 + transform: rotate(-45deg); 351 + top: 0; 352 + } 353 + 294 354 /* Responsive adjustments */ 295 - @media (max-width: 600px) { 355 + @media (max-width: 650px) { 296 356 header nav { 297 - flex-direction: column; 298 - align-items: stretch; 299 - height: auto; 300 - padding: 0.5rem 0.3rem; 357 + flex-direction: row; 358 + flex-wrap: nowrap; /* Changed from wrap */ 359 + align-items: center; 360 + justify-content: space-between; 361 + height: 3.6rem; /* Set fixed height */ 362 + padding: 0.5rem 1rem; 301 363 gap: 0.5rem; 302 364 margin-top: 0.7rem; 303 - justify-content: flex-start; 365 + position: relative; /* For menu positioning */ 304 366 } 305 367 .brand { 306 368 margin-right: 0; 307 - margin-bottom: 0.5rem; 308 - justify-content: center; 369 + margin-bottom: 0; /* Removed bottom margin */ 370 + justify-content: flex-start; 309 371 } 310 372 .header-controls { 311 - flex-direction: column; 312 - align-items: stretch; 313 - gap: 0.3rem; 373 + display: flex !important; 374 + position: static !important; 375 + flex-direction: row; 376 + align-items: center; 377 + gap: 0.5rem; 378 + background: none; 379 + box-shadow: none; 380 + border: none; 381 + padding: 0; 382 + } 383 + .header-controls.mobile-nav-open { 384 + display: flex; 314 385 } 315 386 .menu { 387 + display: none; 316 388 flex-direction: column; 317 389 gap: 0.3rem; 318 390 align-items: stretch; ··· 331 403 height: 2rem; 332 404 font-size: 1rem; 333 405 } 406 + .mobile-nav-toggle { 407 + display: block; 408 + } 409 + .header-divider { 410 + display: none !important; 411 + } 412 + } 413 + 414 + @media (min-width: 601px) { 415 + .header-controls { 416 + display: flex !important; 417 + position: static; 418 + max-height: none; 419 + opacity: 1; 420 + flex-direction: row; 421 + align-items: center; 422 + gap: 1.2rem; 423 + background: none; 424 + box-shadow: none; 425 + border: none; 426 + padding: 0; 427 + } 428 + .header-controls { 429 + padding-right: 1.2rem; 430 + } 431 + .brand { 432 + padding-left: 1.2rem; 433 + } 434 + .menu { 435 + display: flex; 436 + flex-direction: row; 437 + gap: 1.2rem; 438 + align-items: center; 439 + width: auto; 440 + background: none; 441 + padding: 0; 442 + margin: 0; 443 + border: none; 444 + box-shadow: none; 445 + } 446 + .menu li { 447 + flex: none; 448 + text-align: left; 449 + list-style: none; 450 + margin: 0; 451 + padding: 0; 452 + } 453 + .menu a { 454 + display: inline-block; 455 + width: auto; 456 + padding: 0.3em 0.8em; 457 + font-size: 1em; 458 + border-radius: 0.7em; 459 + background: none; 460 + border: none; 461 + margin: 0; 462 + text-align: center; 463 + transition: background 0.15s, color 0.15s; 464 + } 465 + .mobile-nav-toggle { 466 + display: none; 467 + } 334 468 } 335 469 336 470 /* Intro (About section) */ ··· 427 561 textarea { resize: vertical; } 428 562 429 563 /* Enhanced mobile responsiveness */ 430 - @media (max-width: 600px) { 564 + @media (max-width: 650px) { 431 565 html, body { 432 566 font-size: 15px; 433 567 padding: 0; ··· 435 569 .container { 436 570 padding: 0.5rem; 437 571 max-width: 100%; 438 - } 439 - header nav { 440 - flex-direction: column; 441 - align-items: stretch; 442 - height: auto; 443 - padding: 0.5rem 0.3rem; 444 - gap: 0.5rem; 445 - margin-top: 0.7rem; 446 - } 447 - .brand { 448 - font-size: 1em; 449 - margin-right: 0; 450 - margin-bottom: 0.5rem; 451 - justify-content: center; 452 - } 453 - .header-controls { 454 - flex-direction: column; 455 - align-items: stretch; 456 - gap: 0.3rem; 457 - } 458 - .menu { 459 - flex-direction: column; 460 - gap: 0.3rem; 461 - align-items: stretch; 462 - } 463 - .menu a { 464 - font-size: 1em; 465 - padding: 0.3em 0.5em; 466 - margin-top: 0; 467 - justify-content: flex-start; 468 - } 469 - #theme-toggle { 470 - margin-left: 0; 471 - align-self: flex-end; 472 - width: 2rem; 473 - height: 2rem; 474 - font-size: 1rem; 475 572 } 476 573 .intro { 477 574 flex-direction: column; ··· 734 831 } 735 832 736 833 /* Responsive: accessibility menu */ 737 - @media (max-width: 600px) { 834 + @media (max-width: 650px) { 738 835 .accessibility-menu { 836 + left: 0.5rem; 739 837 right: 0.5rem; 740 838 top: 3.2rem; 741 - min-width: 140px; 742 - padding: 0.3rem 0.3rem; 839 + min-width: unset; 840 + max-width: calc(100vw - 1rem); 841 + width: auto; 842 + border-radius: 1.2rem; 843 + padding: 0.7rem 0.7rem; 844 + font-size: 1.05em; 845 + box-sizing: border-box; 846 + overflow-x: hidden; 847 + overflow-y: auto; 743 848 } 744 849 .access-section { 745 - gap: 0.2rem; 850 + gap: 0.3rem; 851 + } 852 + .access-theme-btns { 853 + display: flex; 854 + flex-direction: row; 855 + gap: 0.5em; 856 + justify-content: center; 857 + align-items: center; 858 + margin-top: 0.3em; 859 + margin-bottom: 0.2em; 860 + } 861 + .access-radio { 862 + display: inline-flex; 863 + align-items: center; 864 + justify-content: center; 865 + background: var(--btn-bg); 866 + color: var(--btn-text); 867 + border: 1.5px solid #c0c0c0; 868 + border-radius: 999px; 869 + font-size: 1em; 870 + font-weight: 600; 871 + padding: 0.35em 1.1em; 872 + margin: 0; 873 + cursor: pointer; 874 + transition: background 0.15s, color 0.15s, border 0.15s; 875 + text-align: center; 876 + min-width: 0; 877 + min-height: 2.2em; 878 + box-sizing: border-box; 879 + flex: 1 1 0; 880 + outline: none; 881 + } 882 + .access-radio.selected, .access-radio:active, .access-radio:focus { 883 + background: var(--btn-active-bg); 884 + color: var(--btn-active-text); 885 + border-color: #222; 886 + outline: none; 887 + } 888 + .access-radio input[type="radio"] { 889 + display: none; 746 890 } 747 891 .access-textsize button { 748 - font-size: 0.95em; 749 - padding: 0.15em 0.5em; 892 + font-size: 1em; 893 + padding: 0.2em 0.7em; 894 + } 895 + } 896 + 897 + /* Consistent button style for .access-radio on all screens */ 898 + .access-radio { 899 + background: var(--nav-bg); 900 + color: var(--text-color); 901 + border: var(--nav-border); 902 + border-radius: 1.5rem; 903 + box-shadow: var(--nav-shadow); 904 + padding: 0.5em 1.5em; 905 + font-weight: 600; 906 + font-size: 1em; 907 + transition: background 0.15s, color 0.15s, box-shadow 0.15s, border 0.15s; 908 + text-decoration: none; 909 + display: inline-block; 910 + outline: none; 911 + margin: 0 0.2em 0.2em 0; 912 + cursor: pointer; 913 + min-width: 0; 914 + min-height: 2.2em; 915 + box-sizing: border-box; 916 + align-items: center; 917 + justify-content: center; 918 + } 919 + .access-radio.selected, .access-radio:active, .access-radio:focus { 920 + background: var(--nav-active-bg); 921 + color: var(--link-hover-color); 922 + box-shadow: 0 4px 16px rgba(0,0,0,0.10); 923 + border-color: #444; 924 + outline: none; 925 + } 926 + .access-radio input[type="radio"] { 927 + display: none; 928 + } 929 + @media (max-width: 650px) { 930 + .access-theme-btns { 931 + display: flex; 932 + flex-direction: row; 933 + gap: 0.5em; 934 + justify-content: center; 935 + align-items: center; 936 + margin-top: 0.3em; 937 + margin-bottom: 0.2em; 938 + } 939 + .access-radio { 940 + width: 100%; 941 + min-width: 0; 942 + padding: 0.5em 0.7em; 943 + font-size: 1em; 944 + border-radius: 1.5rem; 945 + margin: 0 0.2em 0.2em 0; 946 + box-shadow: var(--nav-shadow); 750 947 } 751 948 } 752 949 ··· 938 1135 } 939 1136 940 1137 /* Responsive adjustments for smaller screens */ 941 - @media (max-width: 600px) { 1138 + @media (max-width: 650px) { 942 1139 .project-media .carousel { 943 1140 gap: 0.5rem; 944 1141 } ··· 954 1151 .app-store-link img { 955 1152 width: 140px; 956 1153 } 1154 + } 1155 + 1156 + @media (max-width: 650px) { 1157 + header nav { 1158 + display: flex; 1159 + flex-direction: row; 1160 + align-items: center; 1161 + justify-content: space-between; 1162 + min-height: 3.6rem; 1163 + height: 3.6rem; 1164 + position: relative; 1165 + padding-right: 0 !important; 1166 + } 1167 + .brand { 1168 + flex: 1 1 auto; 1169 + text-align: left; 1170 + padding-left: 0.7em; 1171 + } 1172 + .mobile-nav-toggle { 1173 + display: block; 1174 + position: relative; 1175 + z-index: 1002; 1176 + margin-right: 0.5rem; 1177 + margin-left: 0; 1178 + order: 0; 1179 + } 1180 + .header-controls { 1181 + display: flex !important; 1182 + flex: 2 1 auto; 1183 + align-items: center; 1184 + justify-content: flex-end; 1185 + position: static; 1186 + background: none; 1187 + box-shadow: none; 1188 + border: none; 1189 + padding: 0 1rem 0 0; 1190 + gap: 0; 1191 + margin-right: 0 !important; 1192 + } 1193 + .menu { 1194 + display: none; 1195 + } 1196 + .menu.menu-in-bar { 1197 + display: flex !important; 1198 + flex-direction: row !important; 1199 + justify-content: flex-start; 1200 + align-items: center; 1201 + gap: 0; 1202 + width: 100%; 1203 + background: none; 1204 + box-shadow: none; 1205 + border: none; 1206 + margin: 0; 1207 + padding: 0; 1208 + z-index: auto; 1209 + order: 1; 1210 + height: 3.6rem; 1211 + } 1212 + .menu.menu-in-bar li { 1213 + margin: 0; 1214 + padding: 0; 1215 + height: 100%; 1216 + display: flex; 1217 + align-items: center; 1218 + } 1219 + .menu.menu-in-bar a { 1220 + font-size: 1em; 1221 + padding: 0.2em 0.7em; 1222 + margin: 0; 1223 + opacity: 1; 1224 + justify-content: flex-start; 1225 + line-height: 1.2; 1226 + min-height: 2.8rem; 1227 + display: flex; 1228 + align-items: center; 1229 + height: 100%; 1230 + } 1231 + .brand[style*="display: none"] { 1232 + display: none !important; 1233 + } 1234 + .menu.menu-in-bar ~ .header-divider, 1235 + .menu.menu-in-bar ~ #accessibility-btn { 1236 + display: none !important; 1237 + } 1238 + .mobile-nav-toggle { 1239 + order: 2; 1240 + margin-left: 1.2rem; 1241 + margin-right: 0; 1242 + } 1243 + } 1244 + 1245 + /* Add consistent horizontal padding to header bar */ 1246 + header { 1247 + padding-left: 1.2rem; 1248 + padding-right: 1.2rem; 1249 + } 1250 + @media (max-width: 650px) { 1251 + header { 1252 + padding-left: 0.5rem; 1253 + padding-right: 0.5rem; 1254 + } 1255 + } 1256 + 1257 + header nav, 1258 + header nav.container { 1259 + padding-left: 0 !important; 1260 + padding-right: 0 !important; 1261 + } 1262 + @media (max-width: 650px) { 1263 + header nav, 1264 + header nav.container { 1265 + padding-left: 0 !important; 1266 + padding-right: 0 !important; 1267 + } 1268 + } 1269 + 1270 + .access-section.access-textsize { 1271 + display: flex; 1272 + justify-content: space-between; 1273 + align-items: center; 1274 + gap: 1em; 1275 + } 1276 + .access-section.access-textsize .access-label { 1277 + margin-bottom: 0; 1278 + flex: 1 1 auto; 1279 + text-align: left; 1280 + } 1281 + .access-textsize-btns { 1282 + display: flex; 1283 + gap: 0.3em; 1284 + flex: 0 0 auto; 1285 + align-items: center; 1286 + justify-content: flex-end; 1287 + } 1288 + .brand, .profile-name { 1289 + color: #fff !important; 1290 + text-shadow: 0 1px 8px rgba(0,0,0,0.10); 1291 + } 1292 + 1293 + .dark-theme .brand, .dark-theme .profile-name { 1294 + color: #fff !important; 1295 + } 1296 + 1297 + :root:not(.light-theme):not(.dark-theme) .brand, :root:not(.light-theme):not(.dark-theme) .profile-name { 1298 + color: #fff !important; 957 1299 }