From 36f7d0ed8b7756401225d917eafe55489856798b Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 12 Feb 2026 17:28:25 -0500 Subject: [PATCH] updated the website with new features and improvements --- 404.html | 70 +++++++++++++++++++ index.html | 14 +++- links.html | 14 +++- main.js | 122 ++++++++++++++++++++++++++------ styles.css | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 383 insertions(+), 39 deletions(-) create mode 100644 404.html diff --git a/404.html b/404.html new file mode 100644 index 0000000..532b22b --- /dev/null +++ b/404.html @@ -0,0 +1,70 @@ + + + + + + 404 · pvrz + + + + + + + + + + + + + + +
+ + + +
+
+
+

Lost in the feed

+

404

+

This page slipped the map.

+

Double-check the link, or head back to the main hub.

+ +
+ +
+ + + + diff --git a/index.html b/index.html index 6e78d55..9ffda56 100644 --- a/index.html +++ b/index.html @@ -34,9 +34,17 @@ - +
+ + +
diff --git a/links.html b/links.html index 6a78448..8a35934 100644 --- a/links.html +++ b/links.html @@ -34,9 +34,17 @@ - +
+ + +
diff --git a/main.js b/main.js index 3eac7d0..d7aabde 100644 --- a/main.js +++ b/main.js @@ -13,37 +13,115 @@ if (wordmark) { } const THEME_KEY = 'theme'; -const themeToggle = document.querySelector('.theme-toggle'); +const themeMenu = document.querySelector('.theme-menu'); +const themeToggle = themeMenu ? themeMenu.querySelector('.theme-toggle') : null; +const themeDropdown = themeMenu ? themeMenu.querySelector('.theme-dropdown') : null; +const themeOptions = themeMenu ? themeMenu.querySelectorAll('[data-theme-choice]') : []; +const themeQuery = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null; -function setTheme(theme) { - document.documentElement.setAttribute('data-theme', theme); - if (themeToggle) { - themeToggle.setAttribute('aria-pressed', theme === 'dark' ? 'true' : 'false'); - themeToggle.innerHTML = theme === 'dark' - ? '' - : ''; +function getEffectiveTheme(mode) { + if (mode === 'auto') { + return themeQuery && themeQuery.matches ? 'dark' : 'light'; + } + return mode; +} + +function updateToggle(mode, effectiveTheme) { + if (!themeToggle) { + return; + } + + let iconClass = 'fa-circle-half-stroke'; + if (mode === 'dark') { + iconClass = 'fa-sun'; + } else if (mode === 'light') { + iconClass = 'fa-moon'; + } + + themeToggle.innerHTML = ` + + + `; + themeToggle.setAttribute('aria-label', `Theme: ${mode.charAt(0).toUpperCase() + mode.slice(1)}`); + themeToggle.setAttribute('data-effective-theme', effectiveTheme); +} + +function updateActiveOption(mode) { + themeOptions.forEach((option) => { + const isActive = option.dataset.themeChoice === mode; + option.classList.toggle('is-active', isActive); + option.setAttribute('aria-checked', isActive ? 'true' : 'false'); + }); +} + +function setThemeMode(mode, persist = true) { + const effectiveTheme = getEffectiveTheme(mode); + document.documentElement.setAttribute('data-theme', effectiveTheme); + updateToggle(mode, effectiveTheme); + updateActiveOption(mode); + if (persist) { + localStorage.setItem(THEME_KEY, mode); } } function initTheme() { - const savedTheme = localStorage.getItem(THEME_KEY); - if (savedTheme) { - setTheme(savedTheme); - return; - } - - const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; - setTheme(prefersDark ? 'dark' : 'light'); + const savedMode = localStorage.getItem(THEME_KEY); + const mode = savedMode || 'auto'; + setThemeMode(mode, Boolean(savedMode)); } -if (themeToggle) { +function openThemeMenu() { + if (!themeMenu || !themeToggle) { + return; + } + themeMenu.classList.add('is-open'); + themeToggle.setAttribute('aria-expanded', 'true'); +} + +function closeThemeMenu() { + if (!themeMenu || !themeToggle) { + return; + } + themeMenu.classList.remove('is-open'); + themeToggle.setAttribute('aria-expanded', 'false'); +} + +if (themeMenu && themeToggle && themeDropdown) { initTheme(); - themeToggle.addEventListener('click', () => { - const isDark = document.documentElement.getAttribute('data-theme') === 'dark'; - const nextTheme = isDark ? 'light' : 'dark'; - setTheme(nextTheme); - localStorage.setItem(THEME_KEY, nextTheme); + + themeToggle.addEventListener('click', (event) => { + event.stopPropagation(); + themeMenu.classList.contains('is-open') ? closeThemeMenu() : openThemeMenu(); + }); + + themeOptions.forEach((option) => { + option.addEventListener('click', () => { + const mode = option.dataset.themeChoice || 'auto'; + setThemeMode(mode); + closeThemeMenu(); + }); + }); + + document.addEventListener('click', (event) => { + if (!themeMenu.contains(event.target)) { + closeThemeMenu(); + } + }); + + document.addEventListener('keydown', (event) => { + if (event.key === 'Escape') { + closeThemeMenu(); + } }); } else { initTheme(); } + +if (themeQuery) { + themeQuery.addEventListener('change', () => { + const mode = localStorage.getItem(THEME_KEY) || 'auto'; + if (mode === 'auto') { + setThemeMode('auto', false); + } + }); +} diff --git a/styles.css b/styles.css index 3bf86f2..abfe581 100644 --- a/styles.css +++ b/styles.css @@ -1,45 +1,36 @@ :root { - /* Backgrounds */ --bg: #F7F4EE; --bg-alt: #F0EBE3; --surface: #FFFCF8; --surface-alt: #F5F0E8; - /* Text */ --text-primary: #1C1714; --text-secondary: #6B5F56; --text-muted: #A09488; - /* Accent — Claude's warm coral/orange */ --accent: #C96442; --accent-hover: #B5512E; --accent-light: rgba(201, 100, 66, 0.10); --accent-glow: rgba(201, 100, 66, 0.18); - /* Borders */ --border: #E8E2D8; --border-dark: #D4CEC5; - /* Nav */ --nav-bg: rgba(247, 244, 238, 0.85); - /* Shadows */ --shadow-sm: 0 1px 3px rgba(28,23,20,0.07), 0 1px 2px rgba(28,23,20,0.05); --shadow-md: 0 4px 12px rgba(28,23,20,0.08), 0 2px 4px rgba(28,23,20,0.05); --shadow-lg: 0 16px 40px rgba(28,23,20,0.10), 0 4px 12px rgba(28,23,20,0.06); --shadow-card: 0 2px 8px rgba(28,23,20,0.06), 0 1px 2px rgba(28,23,20,0.04); - /* Typography */ --font-serif: 'Instrument Serif', Georgia, serif; --font-sans: 'DM Sans', system-ui, sans-serif; - /* Layout */ --nav-h: 64px; --radius: 14px; --radius-sm: 9px; --radius-pill: 100px; - /* Transitions */ --ease: cubic-bezier(0.22, 1, 0.36, 1); --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); } @@ -151,13 +142,15 @@ img { display: block; max-width: 100%; } display: inline-flex; align-items: center; justify-content: center; - width: 38px; + gap: 0.35rem; + width: auto; height: 38px; - border-radius: 50%; + border-radius: var(--radius-pill); border: 1.5px solid var(--border); background: var(--surface); color: var(--text-secondary); cursor: pointer; + padding: 0 0.7rem; transition: all 0.18s var(--ease); box-shadow: var(--shadow-sm); } @@ -168,6 +161,62 @@ img { display: block; max-width: 100%; } } .theme-toggle:active { transform: translateY(1px); } +.theme-caret { + font-size: 0.6rem; + opacity: 0.7; +} + +.theme-menu { + position: relative; +} + +.theme-dropdown { + position: absolute; + top: calc(100% + 0.55rem); + right: 0; + min-width: 160px; + padding: 0.35rem; + border-radius: 12px; + border: 1.5px solid var(--border); + background: var(--surface); + box-shadow: var(--shadow-md); + display: none; + z-index: 10; +} + +.theme-menu.is-open .theme-dropdown { + display: block; + animation: dropdown-in 0.2s var(--ease) both; +} + +.theme-option { + width: 100%; + border: none; + background: transparent; + color: var(--text-secondary); + font-size: 0.85rem; + text-align: left; + padding: 0.55rem 0.7rem; + border-radius: 8px; + cursor: pointer; + transition: all 0.18s var(--ease); +} + +.theme-option:hover { + color: var(--text-primary); + background: var(--bg-alt); +} + +.theme-option.is-active { + color: var(--text-primary); + background: var(--accent-light); +} + +@keyframes dropdown-in { + from { opacity: 0; transform: translateY(-6px) scale(0.98); } + to { opacity: 1; transform: translateY(0) scale(1); } +} + .btn { display: inline-flex; align-items: center; @@ -499,6 +548,131 @@ body.inner-page { transform: translate(0, 0); } +body.not-found { + background: radial-gradient(1200px 600px at 20% -10%, rgba(201,100,66,0.12), transparent 60%), + radial-gradient(900px 500px at 90% 0%, rgba(201,100,66,0.08), transparent 55%), + var(--bg); +} + +.notfound { + flex: 1; + display: grid; + place-items: center; + padding: calc(var(--nav-h) + clamp(2rem, 6vw, 4rem)) clamp(1.25rem, 4vw, 3rem) clamp(2.5rem, 6vw, 4rem); + position: relative; + overflow: hidden; +} + +.notfound-sheen { + position: absolute; + inset: 0; + background: linear-gradient(120deg, transparent 0%, rgba(255,255,255,0.18) 45%, transparent 70%); + opacity: 0.7; + pointer-events: none; + animation: sheen-sweep 2.6s var(--ease) 1 both; +} + +.notfound-card { + position: relative; + z-index: 2; + width: min(720px, 92vw); + padding: clamp(2rem, 5vw, 3.25rem); + border-radius: clamp(18px, 2.5vw, 28px); + border: 1px solid var(--border); + background: linear-gradient(145deg, var(--surface), var(--surface-alt)); + box-shadow: var(--shadow-lg); + text-align: center; + animation: fade-in 0.7s var(--ease) both; +} + +.notfound-label { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.22em; + color: var(--text-muted); + margin-bottom: 0.75rem; +} + +.notfound-code { + font-family: var(--font-serif); + font-style: italic; + font-size: clamp(4.5rem, 18vw, 9rem); + font-weight: 400; + letter-spacing: -0.04em; + line-height: 0.9; + margin-bottom: 0.75rem; + background: linear-gradient(140deg, var(--text-primary), var(--accent)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.notfound-title { + font-size: clamp(1.4rem, 3.5vw, 2rem); + font-weight: 500; + color: var(--text-primary); + margin-bottom: 0.4rem; +} + +.notfound-copy { + color: var(--text-secondary); + max-width: 44ch; + margin: 0 auto 1.6rem; +} + +.notfound-actions { + display: flex; + align-items: center; + justify-content: center; + gap: 0.8rem; + flex-wrap: wrap; +} + +.notfound-orbit { + position: absolute; + width: min(720px, 90vw); + height: min(720px, 90vw); + border-radius: 50%; + inset: 50% auto auto 50%; + transform: translate(-50%, -50%); + z-index: 1; + opacity: 0.8; +} + +.notfound-orbit span { + position: absolute; + inset: 0; + border-radius: 50%; + border: 1px dashed rgba(201,100,66,0.25); + animation: orbit-spin 18s linear infinite; +} + +.notfound-orbit span:nth-child(2) { + inset: 12%; + border-style: solid; + border-color: rgba(201,100,66,0.18); + animation-duration: 26s; +} + +.notfound-orbit span:nth-child(3) { + inset: 24%; + border-style: dotted; + border-color: rgba(201,100,66,0.22); + animation-duration: 34s; + animation-direction: reverse; +} + +@keyframes orbit-spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +@keyframes sheen-sweep { + 0% { transform: translateX(-40%); } + 70% { transform: translateX(0%); } + 100% { transform: translateX(0%); } +} + @media (max-width: 640px) { .nav { padding: 0 1.25rem; } @@ -509,10 +683,16 @@ body.inner-page { .page-main { padding: calc(var(--nav-h) + 1.5rem) 1.25rem 2.25rem; } .link-grid { grid-template-columns: 1fr; } + .notfound-card { padding: 2rem 1.5rem; } + .notfound-actions { gap: 0.6rem; } + .footer { flex-direction: column; gap: 0.75rem; text-align: center; } } @media (max-width: 400px) { .hero-buttons { flex-direction: column; width: 100%; } .btn { width: 100%; } + + .notfound-actions { width: 100%; } + .notfound-actions .btn { width: 100%; } }