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%; }
}