Update theme variable usage and add ThemeToggle component

- Refactor component styles to use CSS custom properties for colors and backgrounds.
- Replace hard‑coded Tailwind classes with theme variables across BlogCard, BlogFilters, Footer, GridOverlay, Navigation, PostNavigation, ReadingProgress, RelatedPosts, TableOfContents, ThemeToggle, sections, layouts, pages, and global.css.
- Add ThemeToggle component for user‑controlled theme switching.
- Update global styles to define new theme variables.
- Ensure all components respect theme changes and maintain accessibility.

Hubert The Eunuch
This commit is contained in:
Nicholai 2025-12-18 17:08:52 -07:00
parent dc215c89f4
commit 2ca66ccc6d
19 changed files with 698 additions and 405 deletions

View File

@ -36,12 +36,12 @@ const isFeatured = variant === 'featured';
--- ---
<article class:list={[ <article class:list={[
'group relative border border-white/10 bg-white/[0.02] hover:border-brand-accent/40 transition-all duration-500 overflow-hidden', 'group relative border border-[var(--theme-border-primary)] bg-[var(--theme-hover-bg)] hover:border-brand-accent/40 transition-all duration-500 overflow-hidden',
isFeatured ? 'lg:grid lg:grid-cols-2' : '', isFeatured ? 'lg:grid lg:grid-cols-2' : '',
className className
]}> ]}>
<!-- Accent indicator strip --> <!-- Accent indicator strip -->
<div class="absolute top-0 left-0 w-1 h-full bg-slate-700 opacity-50 group-hover:bg-brand-accent group-hover:opacity-100 transition-all duration-500"></div> <div class="absolute top-0 left-0 w-1 h-full bg-[var(--theme-text-subtle)] opacity-50 group-hover:bg-brand-accent group-hover:opacity-100 transition-all duration-500"></div>
<!-- Image section --> <!-- Image section -->
<a href={href} class:list={[ <a href={href} class:list={[
@ -57,13 +57,13 @@ const isFeatured = variant === 'featured';
class="w-full h-full object-cover transition-transform duration-[1.2s] ease-out group-hover:scale-105" class="w-full h-full object-cover transition-transform duration-[1.2s] ease-out group-hover:scale-105"
/> />
)} )}
<div class="absolute inset-0 bg-brand-dark/40 group-hover:bg-brand-dark/20 transition-colors duration-500"></div> <div class="absolute inset-0 bg-[var(--theme-card-overlay)] group-hover:opacity-50 transition-opacity duration-500"></div>
<div class="absolute inset-0 bg-gradient-to-t from-brand-dark/60 to-transparent"></div> <div class="absolute inset-0 bg-gradient-to-t from-[var(--theme-card-gradient)] to-transparent"></div>
<!-- Category badge overlay --> <!-- Category badge overlay -->
{category && ( {category && (
<div class="absolute top-4 left-4"> <div class="absolute top-4 left-4">
<span class="px-3 py-1.5 text-[10px] font-mono font-bold uppercase tracking-widest bg-brand-dark/80 border border-white/20 text-white backdrop-blur-sm"> <span class="px-3 py-1.5 text-[10px] font-mono font-bold uppercase tracking-widest bg-[var(--theme-bg-primary)]/80 border border-[var(--theme-border-strong)] text-[var(--theme-text-primary)] backdrop-blur-sm">
{category} {category}
</span> </span>
</div> </div>
@ -80,8 +80,8 @@ const isFeatured = variant === 'featured';
<span class="text-[10px] font-mono text-brand-accent uppercase tracking-widest"> <span class="text-[10px] font-mono text-brand-accent uppercase tracking-widest">
<FormattedDate date={pubDate} /> <FormattedDate date={pubDate} />
</span> </span>
<span class="h-px flex-grow max-w-8 bg-white/20"></span> <span class="h-px flex-grow max-w-8 bg-[var(--theme-border-strong)]"></span>
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-widest"> <span class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest">
{readTime} {readTime}
</span> </span>
</div> </div>
@ -89,7 +89,7 @@ const isFeatured = variant === 'featured';
<!-- Title --> <!-- Title -->
<a href={href}> <a href={href}>
<h3 class:list={[ <h3 class:list={[
'font-bold text-white uppercase tracking-tight mb-3 group-hover:text-brand-accent transition-colors duration-300 leading-tight', 'font-bold text-[var(--theme-text-primary)] uppercase tracking-tight mb-3 group-hover:text-brand-accent transition-colors duration-300 leading-tight',
isFeatured ? 'text-3xl lg:text-4xl' : isCompact ? 'text-lg' : 'text-xl lg:text-2xl' isFeatured ? 'text-3xl lg:text-4xl' : isCompact ? 'text-lg' : 'text-xl lg:text-2xl'
]}> ]}>
{title} {title}
@ -98,7 +98,7 @@ const isFeatured = variant === 'featured';
<!-- Description --> <!-- Description -->
<p class:list={[ <p class:list={[
'text-slate-400 font-light leading-relaxed', 'text-[var(--theme-text-secondary)] font-light leading-relaxed',
isFeatured ? 'text-base lg:text-lg line-clamp-3 mb-8' : isCompact ? 'text-sm line-clamp-2 mb-4' : 'text-sm line-clamp-2 mb-6' isFeatured ? 'text-base lg:text-lg line-clamp-3 mb-8' : isCompact ? 'text-sm line-clamp-2 mb-4' : 'text-sm line-clamp-2 mb-6'
]}> ]}>
{description} {description}
@ -108,7 +108,7 @@ const isFeatured = variant === 'featured';
{tags && tags.length > 0 && !isCompact && ( {tags && tags.length > 0 && !isCompact && (
<div class="flex flex-wrap gap-2 mb-6"> <div class="flex flex-wrap gap-2 mb-6">
{tags.slice(0, 4).map((tag) => ( {tags.slice(0, 4).map((tag) => (
<span class="px-2 py-1 text-[10px] font-mono uppercase border border-white/10 text-slate-500 group-hover:border-white/20 transition-colors"> <span class="px-2 py-1 text-[10px] font-mono uppercase border border-[var(--theme-border-primary)] text-[var(--theme-text-muted)] group-hover:border-[var(--theme-border-strong)] transition-colors">
{tag} {tag}
</span> </span>
))} ))}
@ -118,14 +118,14 @@ const isFeatured = variant === 'featured';
<!-- Read link --> <!-- Read link -->
<div class:list={[ <div class:list={[
'flex items-center', 'flex items-center',
isFeatured ? 'mt-auto pt-6 border-t border-white/10' : 'mt-auto' isFeatured ? 'mt-auto pt-6 border-t border-[var(--theme-border-primary)]' : 'mt-auto'
]}> ]}>
<a <a
href={href} href={href}
class="inline-flex items-center gap-3 text-xs font-bold uppercase tracking-widest text-slate-500 group-hover:text-white transition-all duration-300" class="inline-flex items-center gap-3 text-xs font-bold uppercase tracking-widest text-[var(--theme-text-muted)] group-hover:text-[var(--theme-text-primary)] transition-all duration-300"
> >
Read Article Read Article
<span class="block w-6 h-[1px] bg-slate-600 group-hover:bg-brand-accent group-hover:w-10 transition-all duration-300"></span> <span class="block w-6 h-[1px] bg-[var(--theme-text-subtle)] group-hover:bg-brand-accent group-hover:w-10 transition-all duration-300"></span>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="12" width="12"
@ -145,4 +145,3 @@ const isFeatured = variant === 'featured';
</div> </div>
</div> </div>
</article> </article>

View File

@ -12,13 +12,13 @@ const { categories, class: className = '' } = Astro.props;
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-6 mb-10"> <div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-6 mb-10">
<!-- Category chips --> <!-- Category chips -->
<div class="flex flex-wrap items-center gap-1"> <div class="flex flex-wrap items-center gap-1">
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-widest mr-4"> <span class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest mr-4">
/// SECTOR SELECT /// SECTOR SELECT
</span> </span>
<button <button
type="button" type="button"
data-category="all" data-category="all"
class="filter-chip active px-4 py-2 text-[10px] font-mono font-bold uppercase tracking-widest border-b-2 border-brand-accent text-white bg-white/5 transition-all duration-300 hover:bg-white/10" class="filter-chip active px-4 py-2 text-[10px] font-mono font-bold uppercase tracking-widest border-b-2 border-brand-accent text-[var(--theme-text-primary)] bg-[var(--theme-hover-bg-strong)] transition-all duration-300 hover:bg-[var(--theme-hover-bg-strong)]"
> >
All All
</button> </button>
@ -26,7 +26,7 @@ const { categories, class: className = '' } = Astro.props;
<button <button
type="button" type="button"
data-category={category} data-category={category}
class="filter-chip px-4 py-2 text-[10px] font-mono font-bold uppercase tracking-widest border-b-2 border-transparent text-slate-500 hover:text-white hover:border-brand-accent/50 hover:bg-white/5 transition-all duration-300" class="filter-chip px-4 py-2 text-[10px] font-mono font-bold uppercase tracking-widest border-b-2 border-transparent text-[var(--theme-text-muted)] hover:text-[var(--theme-text-primary)] hover:border-brand-accent/50 hover:bg-[var(--theme-hover-bg-strong)] transition-all duration-300"
> >
{category} {category}
</button> </button>
@ -42,12 +42,12 @@ const { categories, class: className = '' } = Astro.props;
type="text" type="text"
id="blog-search" id="blog-search"
placeholder="SEARCH_DATABASE..." placeholder="SEARCH_DATABASE..."
class="w-full pl-6 pr-4 py-2 text-sm font-mono bg-transparent border-b border-slate-700 text-white placeholder:text-slate-600 focus:border-brand-accent focus:outline-none transition-colors duration-300 uppercase" class="w-full pl-6 pr-4 py-2 text-sm font-mono bg-transparent border-b border-[var(--theme-text-subtle)] text-[var(--theme-text-primary)] placeholder:text-[var(--theme-text-subtle)] focus:border-brand-accent focus:outline-none transition-colors duration-300 uppercase"
/> />
<button <button
type="button" type="button"
id="clear-search" id="clear-search"
class="absolute right-0 top-1/2 -translate-y-1/2 text-slate-500 hover:text-brand-accent transition-colors hidden" class="absolute right-0 top-1/2 -translate-y-1/2 text-[var(--theme-text-muted)] hover:text-brand-accent transition-colors hidden"
> >
<span class="font-mono text-xs">[CLR]</span> <span class="font-mono text-xs">[CLR]</span>
</button> </button>
@ -55,11 +55,11 @@ const { categories, class: className = '' } = Astro.props;
</div> </div>
<!-- Results count --> <!-- Results count -->
<div class="flex items-center gap-4 pb-6 border-b border-white/10 mb-8"> <div class="flex items-center gap-4 pb-6 border-b border-[var(--theme-border-primary)] mb-8">
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-widest"> <span class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest">
<span id="results-count">0</span> ARTICLES <span id="results-count">0</span> ARTICLES
</span> </span>
<span class="h-px flex-grow bg-white/5"></span> <span class="h-px flex-grow bg-[var(--theme-border-secondary)]"></span>
</div> </div>
</div> </div>
@ -122,13 +122,13 @@ const { categories, class: className = '' } = Astro.props;
// Update active state // Update active state
// Reset all to inactive state // Reset all to inactive state
filterChips.forEach((c) => { filterChips.forEach((c) => {
c.classList.remove('active', 'border-brand-accent', 'text-white', 'bg-white/5'); c.classList.remove('active', 'border-brand-accent', 'text-[var(--theme-text-primary)]', 'bg-[var(--theme-hover-bg-strong)]');
c.classList.add('border-transparent', 'text-slate-500'); c.classList.add('border-transparent', 'text-[var(--theme-text-muted)]');
}); });
// Set clicked to active state // Set clicked to active state
chipEl.classList.add('active', 'border-brand-accent', 'text-white', 'bg-white/5'); chipEl.classList.add('active', 'border-brand-accent', 'text-[var(--theme-text-primary)]', 'bg-[var(--theme-hover-bg-strong)]');
chipEl.classList.remove('border-transparent', 'text-slate-500'); chipEl.classList.remove('border-transparent', 'text-[var(--theme-text-muted)]');
filterPosts(); filterPosts();
}); });
@ -173,8 +173,7 @@ const { categories, class: className = '' } = Astro.props;
<style> <style>
.filter-chip.active { .filter-chip.active {
border-color: var(--color-brand-accent); border-color: var(--color-brand-accent);
color: white; color: var(--theme-text-primary);
background-color: rgba(255, 77, 0, 0.05); background-color: var(--theme-hover-bg-strong);
} }
</style> </style>

View File

@ -2,10 +2,10 @@
const today = new Date(); const today = new Date();
--- ---
<footer class="container mx-auto px-6 lg:px-12 py-32 lg:py-40 relative overflow-hidden border-t border-white/5"> <footer class="container mx-auto px-6 lg:px-12 py-32 lg:py-40 relative overflow-hidden border-t border-[var(--theme-border-secondary)]">
<div class="grid grid-cols-1 md:grid-cols-2 gap-16 lg:gap-24 items-end relative z-10"> <div class="grid grid-cols-1 md:grid-cols-2 gap-16 lg:gap-24 items-end relative z-10">
<div class="animate-on-scroll slide-right"> <div class="animate-on-scroll slide-right">
<h2 class="text-5xl md:text-7xl lg:text-8xl font-bold uppercase leading-[0.95] tracking-tighter mb-10 text-white group cursor-pointer"> <h2 class="text-5xl md:text-7xl lg:text-8xl font-bold uppercase leading-[0.95] tracking-tighter mb-10 text-[var(--theme-text-primary)] group cursor-pointer">
Let's<br> Let's<br>
<span class="text-stroke group-hover:text-brand-accent transition-all duration-500 ease-out">Build</span><br> <span class="text-stroke group-hover:text-brand-accent transition-all duration-500 ease-out">Build</span><br>
Reality. Reality.
@ -18,28 +18,28 @@ const today = new Date();
<div class="md:text-right animate-on-scroll slide-left stagger-2"> <div class="md:text-right animate-on-scroll slide-left stagger-2">
<div class="mb-14"> <div class="mb-14">
<p class="text-xs font-bold uppercase text-slate-500 mb-6 tracking-widest">Social Uplink</p> <p class="text-xs font-bold uppercase text-[var(--theme-text-muted)] mb-6 tracking-widest">Social Uplink</p>
<ul class="space-y-3"> <ul class="space-y-3">
<li> <li>
<a href="https://nicholai.work" class="text-white hover:text-brand-accent text-lg font-mono transition-colors duration-300 inline-block"> <a href="https://nicholai.work" class="text-[var(--theme-text-primary)] hover:text-brand-accent text-lg font-mono transition-colors duration-300 inline-block">
nicholai.work nicholai.work
</a> </a>
</li> </li>
<li> <li>
<a href="https://instagram.com/nicholai.exe/" class="text-white hover:text-brand-accent text-lg font-mono transition-colors duration-300 inline-block"> <a href="https://instagram.com/nicholai.exe/" class="text-[var(--theme-text-primary)] hover:text-brand-accent text-lg font-mono transition-colors duration-300 inline-block">
@nicholai.exe @nicholai.exe
</a> </a>
</li> </li>
<li> <li>
<a href="https://www.linkedin.com/in/nicholai-vogel-7a6b85112/" class="text-white hover:text-brand-accent text-lg font-mono transition-colors duration-300 inline-block"> <a href="https://www.linkedin.com/in/nicholai-vogel-7a6b85112/" class="text-[var(--theme-text-primary)] hover:text-brand-accent text-lg font-mono transition-colors duration-300 inline-block">
LinkedIn LinkedIn
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
<div class="flex md:justify-end items-end gap-3 text-[10px] text-slate-600 font-mono uppercase tracking-wide"> <div class="flex md:justify-end items-end gap-3 text-[10px] text-[var(--theme-text-muted)] font-mono uppercase tracking-wide">
<span>&copy; {today.getFullYear()} Nicholai Vogel</span> <span>&copy; {today.getFullYear()} Nicholai Vogel</span>
<span class="text-slate-700">/</span> <span class="text-[var(--theme-text-subtle)]">/</span>
<span>V7 SYSTEM</span> <span>V7 SYSTEM</span>
</div> </div>
</div> </div>
@ -47,6 +47,6 @@ const today = new Date();
<!-- Decorative huge text bg --> <!-- Decorative huge text bg -->
<div class="absolute -bottom-8 lg:-bottom-12 left-1/2 -translate-x-1/2 w-full text-center pointer-events-none select-none"> <div class="absolute -bottom-8 lg:-bottom-12 left-1/2 -translate-x-1/2 w-full text-center pointer-events-none select-none">
<span class="text-[12rem] md:text-[18rem] lg:text-[22rem] font-extrabold text-white/[0.02] uppercase leading-none whitespace-nowrap tracking-tighter">VOGEL</span> <span class="text-[12rem] md:text-[18rem] lg:text-[22rem] font-extrabold text-[var(--theme-text-primary)] opacity-[var(--theme-decorative-opacity)] uppercase leading-none whitespace-nowrap tracking-tighter transition-opacity duration-300">VOGEL</span>
</div> </div>
</footer> </footer>

View File

@ -5,17 +5,16 @@
<!-- 12 Column Guide (Visual Only - Low Opacity) --> <!-- 12 Column Guide (Visual Only - Low Opacity) -->
<div <div
class="fixed inset-0 container mx-auto px-6 lg:px-12 grid grid-cols-4 md:grid-cols-12 gap-4 pointer-events-none z-0 opacity-10 border-x border-white/5"> class="fixed inset-0 container mx-auto px-6 lg:px-12 grid grid-cols-4 md:grid-cols-12 gap-4 pointer-events-none z-0 opacity-10 border-x border-[var(--theme-border-secondary)]">
<div class="border-r border-white/5 h-full hidden md:block"></div> <div class="border-r border-[var(--theme-border-secondary)] h-full hidden md:block"></div>
<div class="border-r border-white/5 h-full hidden md:block"></div> <div class="border-r border-[var(--theme-border-secondary)] h-full hidden md:block"></div>
<div class="border-r border-white/5 h-full hidden md:block"></div> <div class="border-r border-[var(--theme-border-secondary)] h-full hidden md:block"></div>
<div class="border-r border-white/5 h-full hidden md:block"></div> <div class="border-r border-[var(--theme-border-secondary)] h-full hidden md:block"></div>
<div class="border-r border-white/5 h-full hidden md:block"></div> <div class="border-r border-[var(--theme-border-secondary)] h-full hidden md:block"></div>
<div class="border-r border-white/5 h-full hidden md:block"></div> <div class="border-r border-[var(--theme-border-secondary)] h-full hidden md:block"></div>
<div class="border-r border-white/5 h-full hidden md:block"></div> <div class="border-r border-[var(--theme-border-secondary)] h-full hidden md:block"></div>
<div class="border-r border-white/5 h-full hidden md:block"></div> <div class="border-r border-[var(--theme-border-secondary)] h-full hidden md:block"></div>
<div class="border-r border-white/5 h-full hidden md:block"></div> <div class="border-r border-[var(--theme-border-secondary)] h-full hidden md:block"></div>
<div class="border-r border-white/5 h-full hidden md:block"></div> <div class="border-r border-[var(--theme-border-secondary)] h-full hidden md:block"></div>
<div class="border-r border-white/5 h-full hidden md:block"></div> <div class="border-r border-[var(--theme-border-secondary)] h-full hidden md:block"></div>
</div> </div>

View File

@ -1,21 +1,23 @@
--- ---
--- import ThemeToggle from './ThemeToggle.astro';
---
--- ---
<nav class="fixed top-0 left-0 w-full z-50 px-6 lg:px-12 py-6 lg:py-8 flex justify-between items-center backdrop-blur-md bg-brand-dark/80 border-b border-white/5"> <nav class="fixed top-0 left-0 w-full z-50 px-6 lg:px-12 py-6 lg:py-8 flex justify-between items-center backdrop-blur-md bg-[var(--theme-overlay)] border-b border-[var(--theme-border-secondary)]">
<!-- Left side - can be empty or have subtle branding --> <!-- Left side - branding and theme toggle -->
<div class="hidden md:block"> <div class="flex items-center gap-6">
<a href="/" class="text-[10px] font-mono text-slate-600 tracking-widest uppercase hover:text-brand-accent transition-colors duration-300">NV / 2026</a> <a href="/" class="text-[10px] font-mono text-[var(--theme-text-muted)] tracking-widest uppercase hover:text-brand-accent transition-colors duration-300">NV / 2026</a>
<div class="hidden md:block">
<ThemeToggle />
</div>
</div> </div>
<!-- Right side navigation --> <!-- Right side navigation -->
<div class="flex items-center gap-8 lg:gap-12 ml-auto"> <div class="flex items-center gap-6 lg:gap-10 ml-auto">
<div class="hidden md:flex items-center gap-10 lg:gap-12"> <div class="hidden md:flex items-center gap-10 lg:gap-12">
<a href="/" <a href="/"
class:list={[ class:list={[
"relative text-xs font-semibold uppercase tracking-[0.15em] transition-all duration-300 py-2 group", "relative text-xs font-semibold uppercase tracking-[0.15em] transition-all duration-300 py-2 group",
Astro.url.pathname === '/' ? "text-white" : "text-slate-500 hover:text-white" Astro.url.pathname === '/' ? "text-[var(--theme-text-primary)]" : "text-[var(--theme-text-muted)] hover:text-[var(--theme-text-primary)]"
]}> ]}>
<span class="relative z-10">Home</span> <span class="relative z-10">Home</span>
<span class:list={[ <span class:list={[
@ -26,7 +28,7 @@
<a href="/blog" <a href="/blog"
class:list={[ class:list={[
"relative text-xs font-semibold uppercase tracking-[0.15em] transition-all duration-300 py-2 group", "relative text-xs font-semibold uppercase tracking-[0.15em] transition-all duration-300 py-2 group",
Astro.url.pathname.startsWith('/blog') ? "text-white" : "text-slate-500 hover:text-white" Astro.url.pathname.startsWith('/blog') ? "text-[var(--theme-text-primary)]" : "text-[var(--theme-text-muted)] hover:text-[var(--theme-text-primary)]"
]}> ]}>
<span class="relative z-10">Blog</span> <span class="relative z-10">Blog</span>
<span class:list={[ <span class:list={[
@ -41,34 +43,36 @@
"hidden md:block border px-5 lg:px-6 py-2.5 lg:py-3 text-xs font-bold uppercase tracking-[0.15em] transition-all duration-300", "hidden md:block border px-5 lg:px-6 py-2.5 lg:py-3 text-xs font-bold uppercase tracking-[0.15em] transition-all duration-300",
Astro.url.pathname.startsWith('/contact') Astro.url.pathname.startsWith('/contact')
? "border-brand-accent bg-brand-accent text-brand-dark" ? "border-brand-accent bg-brand-accent text-brand-dark"
: "border-slate-600 text-white hover:border-brand-accent hover:bg-brand-accent hover:text-brand-dark" : "border-[var(--theme-border-strong)] text-[var(--theme-text-primary)] hover:border-brand-accent hover:bg-brand-accent hover:text-brand-dark"
]}> ]}>
Let's Talk Let's Talk
</a> </a>
</div> </div>
<!-- Mobile menu button --> <!-- Mobile menu button -->
<button <div class="md:hidden flex items-center">
id="mobile-menu-toggle" <button
class="md:hidden p-2 text-slate-400 hover:text-white transition-colors z-[60]" id="mobile-menu-toggle"
aria-label="Toggle menu" class="p-2 text-[var(--theme-text-muted)] hover:text-[var(--theme-text-primary)] transition-colors z-[60]"
aria-expanded="false" aria-label="Toggle menu"
> aria-expanded="false"
<!-- Hamburger icon --> >
<svg id="menu-icon-open" class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <!-- Hamburger icon -->
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 6h16M4 12h16M4 18h16"></path> <svg id="menu-icon-open" class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
</svg> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 6h16M4 12h16M4 18h16"></path>
<!-- Close icon (hidden by default) --> </svg>
<svg id="menu-icon-close" class="w-6 h-6 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <!-- Close icon (hidden by default) -->
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6 18L18 6M6 6l12 12"></path> <svg id="menu-icon-close" class="w-6 h-6 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
</svg> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6 18L18 6M6 6l12 12"></path>
</button> </svg>
</button>
</div>
</nav> </nav>
<!-- Mobile Menu Overlay --> <!-- Mobile Menu Overlay -->
<div <div
id="mobile-menu" id="mobile-menu"
class="fixed inset-0 z-40 bg-brand-dark/98 backdrop-blur-xl transform translate-x-full transition-transform duration-300 ease-out md:hidden" class="fixed inset-0 z-40 bg-[var(--theme-overlay-heavy)] backdrop-blur-xl transform translate-x-full transition-transform duration-300 ease-out md:hidden"
> >
<!-- Menu Content --> <!-- Menu Content -->
<div class="flex flex-col justify-center items-center h-full px-8"> <div class="flex flex-col justify-center items-center h-full px-8">
@ -76,19 +80,19 @@
<nav class="flex flex-col items-center gap-8 mb-12"> <nav class="flex flex-col items-center gap-8 mb-12">
<a <a
href="/" href="/"
class="mobile-nav-link text-3xl font-bold uppercase tracking-wider text-white hover:text-brand-accent transition-colors duration-300" class="mobile-nav-link text-3xl font-bold uppercase tracking-wider text-[var(--theme-text-primary)] hover:text-brand-accent transition-colors duration-300"
> >
Home Home
</a> </a>
<a <a
href="/blog" href="/blog"
class="mobile-nav-link text-3xl font-bold uppercase tracking-wider text-white hover:text-brand-accent transition-colors duration-300" class="mobile-nav-link text-3xl font-bold uppercase tracking-wider text-[var(--theme-text-primary)] hover:text-brand-accent transition-colors duration-300"
> >
Blog Blog
</a> </a>
<a <a
href="/contact" href="/contact"
class="mobile-nav-link text-3xl font-bold uppercase tracking-wider text-white hover:text-brand-accent transition-colors duration-300" class="mobile-nav-link text-3xl font-bold uppercase tracking-wider text-[var(--theme-text-primary)] hover:text-brand-accent transition-colors duration-300"
> >
Contact Contact
</a> </a>
@ -97,16 +101,21 @@
<!-- CTA Button --> <!-- CTA Button -->
<a <a
href="/contact" href="/contact"
class="border border-brand-accent px-8 py-4 text-sm font-bold uppercase tracking-[0.2em] text-brand-accent hover:bg-brand-accent hover:text-brand-dark transition-all duration-300" class="border border-brand-accent px-8 py-4 text-sm font-bold uppercase tracking-[0.2em] text-brand-accent hover:bg-brand-accent hover:text-brand-dark transition-all duration-300 mb-8"
> >
Let's Talk Let's Talk
</a> </a>
<!-- Decorative Elements --> <!-- Decorative Elements -->
<div class="absolute bottom-12 left-8 right-8"> <div class="absolute bottom-12 left-8 right-8 flex justify-between items-center">
<div class="flex justify-between items-center text-[10px] font-mono text-slate-600 uppercase tracking-widest"> <div class="flex flex-col gap-2">
<span>NV / 2026</span> <div class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest">
<span>Menu</span> NV / 2026
</div>
<ThemeToggle />
</div>
<div class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest self-end">
Menu
</div> </div>
</div> </div>
</div> </div>

View File

@ -17,12 +17,12 @@ const { prevPost, nextPost } = Astro.props;
--- ---
{(prevPost || nextPost) && ( {(prevPost || nextPost) && (
<nav class="post-navigation mt-20 pt-12 border-t border-white/10" aria-label="Post navigation"> <nav class="post-navigation mt-20 pt-12 border-t border-[var(--theme-border-primary)]" aria-label="Post navigation">
<div class="flex items-center gap-4 mb-8"> <div class="flex items-center gap-4 mb-8">
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-widest font-bold"> <span class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest font-bold">
/// NEXT_IN_SEQUENCE /// NEXT_IN_SEQUENCE
</span> </span>
<span class="h-px flex-grow bg-white/10"></span> <span class="h-px flex-grow bg-[var(--theme-border-primary)]"></span>
</div> </div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
@ -30,24 +30,24 @@ const { prevPost, nextPost } = Astro.props;
{prevPost ? ( {prevPost ? (
<a <a
href={prevPost.href} href={prevPost.href}
class="group relative flex items-center gap-6 p-6 border border-white/10 bg-white/[0.02] hover:border-brand-accent/40 hover:bg-white/[0.04] transition-all duration-500 overflow-hidden" class="group relative flex items-center gap-6 p-6 border border-[var(--theme-border-primary)] bg-[var(--theme-hover-bg)] hover:border-brand-accent/40 hover:bg-[var(--theme-hover-bg-strong)] transition-all duration-500 overflow-hidden"
> >
<div class="absolute top-0 left-0 w-[2px] h-full bg-slate-700 opacity-50 group-hover:bg-brand-accent group-hover:opacity-100 transition-all duration-500"></div> <div class="absolute top-0 left-0 w-[2px] h-full bg-[var(--theme-text-subtle)] opacity-50 group-hover:bg-brand-accent group-hover:opacity-100 transition-all duration-500"></div>
<div class="absolute inset-0 bg-brand-accent/5 translate-x-[-100%] group-hover:translate-x-0 transition-transform duration-500 pointer-events-none"></div> <div class="absolute inset-0 bg-brand-accent/5 translate-x-[-100%] group-hover:translate-x-0 transition-transform duration-500 pointer-events-none"></div>
<!-- Content --> <!-- Content -->
<div class="flex-grow min-w-0 z-10"> <div class="flex-grow min-w-0 z-10">
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-widest mb-2 block group-hover:text-brand-accent transition-colors"> <span class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest mb-2 block group-hover:text-brand-accent transition-colors">
&lt; PREV_FILE &lt; PREV_FILE
</span> </span>
<h4 class="text-sm font-bold text-white uppercase tracking-tight truncate group-hover:text-brand-accent transition-colors"> <h4 class="text-sm font-bold text-[var(--theme-text-primary)] uppercase tracking-tight truncate group-hover:text-brand-accent transition-colors">
{prevPost.title} {prevPost.title}
</h4> </h4>
</div> </div>
<!-- Thumbnail --> <!-- Thumbnail -->
{prevPost.heroImage && ( {prevPost.heroImage && (
<div class="hidden sm:block flex-shrink-0 w-12 h-12 overflow-hidden border border-white/10 z-10 grayscale group-hover:grayscale-0 transition-all duration-500"> <div class="hidden sm:block flex-shrink-0 w-12 h-12 overflow-hidden border border-[var(--theme-border-primary)] z-10 grayscale group-hover:grayscale-0 transition-all duration-500">
<Image <Image
src={prevPost.heroImage} src={prevPost.heroImage}
alt="" alt=""
@ -59,8 +59,8 @@ const { prevPost, nextPost } = Astro.props;
)} )}
</a> </a>
) : ( ) : (
<div class="border border-white/5 bg-white/[0.01] p-6 flex items-center justify-center"> <div class="border border-[var(--theme-border-secondary)] bg-[var(--theme-hover-bg)] p-6 flex items-center justify-center">
<span class="text-[10px] font-mono text-slate-600 uppercase tracking-widest">/// START_OF_ARCHIVE</span> <span class="text-[10px] font-mono text-[var(--theme-text-subtle)] uppercase tracking-widest">/// START_OF_ARCHIVE</span>
</div> </div>
)} )}
@ -68,14 +68,14 @@ const { prevPost, nextPost } = Astro.props;
{nextPost ? ( {nextPost ? (
<a <a
href={nextPost.href} href={nextPost.href}
class="group relative flex items-center gap-6 p-6 border border-white/10 bg-white/[0.02] hover:border-brand-accent/40 hover:bg-white/[0.04] transition-all duration-500 overflow-hidden" class="group relative flex items-center gap-6 p-6 border border-[var(--theme-border-primary)] bg-[var(--theme-hover-bg)] hover:border-brand-accent/40 hover:bg-[var(--theme-hover-bg-strong)] transition-all duration-500 overflow-hidden"
> >
<div class="absolute top-0 right-0 w-[2px] h-full bg-slate-700 opacity-50 group-hover:bg-brand-accent group-hover:opacity-100 transition-all duration-500"></div> <div class="absolute top-0 right-0 w-[2px] h-full bg-[var(--theme-text-subtle)] opacity-50 group-hover:bg-brand-accent group-hover:opacity-100 transition-all duration-500"></div>
<div class="absolute inset-0 bg-brand-accent/5 translate-x-[100%] group-hover:translate-x-0 transition-transform duration-500 pointer-events-none"></div> <div class="absolute inset-0 bg-brand-accent/5 translate-x-[100%] group-hover:translate-x-0 transition-transform duration-500 pointer-events-none"></div>
<!-- Thumbnail --> <!-- Thumbnail -->
{nextPost.heroImage && ( {nextPost.heroImage && (
<div class="hidden sm:block flex-shrink-0 w-12 h-12 overflow-hidden border border-white/10 z-10 grayscale group-hover:grayscale-0 transition-all duration-500"> <div class="hidden sm:block flex-shrink-0 w-12 h-12 overflow-hidden border border-[var(--theme-border-primary)] z-10 grayscale group-hover:grayscale-0 transition-all duration-500">
<Image <Image
src={nextPost.heroImage} src={nextPost.heroImage}
alt="" alt=""
@ -88,20 +88,19 @@ const { prevPost, nextPost } = Astro.props;
<!-- Content --> <!-- Content -->
<div class="flex-grow min-w-0 text-right z-10"> <div class="flex-grow min-w-0 text-right z-10">
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-widest mb-2 block group-hover:text-brand-accent transition-colors"> <span class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest mb-2 block group-hover:text-brand-accent transition-colors">
NEXT_FILE &gt; NEXT_FILE &gt;
</span> </span>
<h4 class="text-sm font-bold text-white uppercase tracking-tight truncate group-hover:text-brand-accent transition-colors"> <h4 class="text-sm font-bold text-[var(--theme-text-primary)] uppercase tracking-tight truncate group-hover:text-brand-accent transition-colors">
{nextPost.title} {nextPost.title}
</h4> </h4>
</div> </div>
</a> </a>
) : ( ) : (
<div class="border border-white/5 bg-white/[0.01] p-6 flex items-center justify-center"> <div class="border border-[var(--theme-border-secondary)] bg-[var(--theme-hover-bg)] p-6 flex items-center justify-center">
<span class="text-[10px] font-mono text-slate-600 uppercase tracking-widest">/// END_OF_ARCHIVE</span> <span class="text-[10px] font-mono text-[var(--theme-text-subtle)] uppercase tracking-widest">/// END_OF_ARCHIVE</span>
</div> </div>
)} )}
</div> </div>
</nav> </nav>
)} )}

View File

@ -2,13 +2,13 @@
// Reading progress bar that tracks scroll position // Reading progress bar that tracks scroll position
--- ---
<div id="reading-progress-container" class="fixed top-0 left-0 w-full h-[3px] z-[100] bg-brand-dark/50"> <div id="reading-progress-container" class="fixed top-0 left-0 w-full h-[3px] z-[100] bg-[var(--theme-bg-primary)]/50">
<div id="reading-progress-bar" class="h-full bg-brand-accent w-0 transition-[width] duration-100 ease-out shadow-[0_0_10px_rgba(221,65,50,0.5)]"></div> <div id="reading-progress-bar" class="h-full bg-brand-accent w-0 transition-[width] duration-100 ease-out shadow-[0_0_10px_rgba(221,65,50,0.5)]"></div>
</div> </div>
<div id="reading-status" class="fixed top-4 right-4 z-[90] hidden lg:flex items-center gap-3 px-3 py-1 bg-brand-dark/80 backdrop-blur-md border border-white/10 opacity-0 transition-opacity duration-300 pointer-events-none"> <div id="reading-status" class="fixed top-4 right-4 z-[90] hidden lg:flex items-center gap-3 px-3 py-1 bg-[var(--theme-overlay)] backdrop-blur-md border border-[var(--theme-border-primary)] opacity-0 transition-opacity duration-300 pointer-events-none">
<div class="w-1.5 h-1.5 bg-brand-accent rounded-full animate-pulse"></div> <div class="w-1.5 h-1.5 bg-brand-accent rounded-full animate-pulse"></div>
<span class="text-[9px] font-mono text-slate-400 uppercase tracking-widest">READING_BUFFER: <span id="progress-text" class="text-white">0%</span></span> <span class="text-[9px] font-mono text-[var(--theme-text-secondary)] uppercase tracking-widest">READING_BUFFER: <span id="progress-text" class="text-[var(--theme-text-primary)]">0%</span></span>
</div> </div>
<script> <script>
@ -69,4 +69,3 @@
// Re-initialize on Astro page transitions // Re-initialize on Astro page transitions
document.addEventListener('astro:page-load', initReadingProgress); document.addEventListener('astro:page-load', initReadingProgress);
</script> </script>

View File

@ -21,12 +21,12 @@ const { posts, class: className = '' } = Astro.props;
--- ---
{posts.length > 0 && ( {posts.length > 0 && (
<section class:list={['related-posts mt-20 pt-12 border-t border-white/10', className]}> <section class:list={['related-posts mt-20 pt-12 border-t border-[var(--theme-border-primary)]', className]}>
<div class="flex items-center gap-4 mb-8"> <div class="flex items-center gap-4 mb-8">
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-widest font-bold"> <span class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest font-bold">
/// RELATED_ARCHIVES /// RELATED_ARCHIVES
</span> </span>
<span class="h-px flex-grow bg-white/10"></span> <span class="h-px flex-grow bg-[var(--theme-border-primary)]"></span>
</div> </div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
@ -45,4 +45,3 @@ const { posts, class: className = '' } = Astro.props;
</div> </div>
</section> </section>
)} )}

View File

@ -17,10 +17,10 @@ const tocHeadings = headings.filter((h) => h.depth === 2 || h.depth === 3);
{tocHeadings.length > 0 && ( {tocHeadings.length > 0 && (
<nav class:list={['toc', className]} data-toc aria-label="Table of contents"> <nav class:list={['toc', className]} data-toc aria-label="Table of contents">
<div class="flex items-center gap-3 mb-6"> <div class="flex items-center gap-3 mb-6">
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-widest font-bold"> <span class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest font-bold">
/// CONTENTS /// CONTENTS
</span> </span>
<span class="h-px flex-grow bg-white/10"></span> <span class="h-px flex-grow bg-[var(--theme-border-primary)]"></span>
</div> </div>
<ul class="space-y-3"> <ul class="space-y-3">
{tocHeadings.map((heading) => ( {tocHeadings.map((heading) => (
@ -29,15 +29,15 @@ const tocHeadings = headings.filter((h) => h.depth === 2 || h.depth === 3);
href={`#${heading.slug}`} href={`#${heading.slug}`}
data-toc-link={heading.slug} data-toc-link={heading.slug}
class:list={[ class:list={[
'toc-link block text-sm transition-all duration-300 hover:text-white', 'toc-link block text-sm transition-all duration-300 hover:text-[var(--theme-text-primary)]',
heading.depth === 2 heading.depth === 2
? 'text-slate-400 font-medium' ? 'text-[var(--theme-text-secondary)] font-medium'
: 'text-slate-500 pl-4 text-xs', : 'text-[var(--theme-text-muted)] pl-4 text-xs',
]} ]}
> >
<span class="flex items-center gap-2"> <span class="flex items-center gap-2">
{heading.depth === 2 && ( {heading.depth === 2 && (
<span class="w-1.5 h-1.5 bg-slate-600 toc-indicator transition-colors duration-300"></span> <span class="w-1.5 h-1.5 bg-[var(--theme-text-subtle)] toc-indicator transition-colors duration-300"></span>
)} )}
{heading.text} {heading.text}
</span> </span>
@ -78,23 +78,23 @@ const tocHeadings = headings.filter((h) => h.depth === 2 || h.depth === 3);
if (activeHeading && currentActive !== activeHeading) { if (activeHeading && currentActive !== activeHeading) {
// Remove active state from all links // Remove active state from all links
tocLinks.forEach((link) => { tocLinks.forEach((link) => {
link.classList.remove('text-brand-accent', 'text-white'); link.classList.remove('text-brand-accent', 'text-[var(--theme-text-primary)]');
link.classList.add('text-slate-400'); link.classList.add('text-[var(--theme-text-secondary)]');
const indicator = link.querySelector('.toc-indicator'); const indicator = link.querySelector('.toc-indicator');
if (indicator) { if (indicator) {
indicator.classList.remove('bg-brand-accent'); indicator.classList.remove('bg-brand-accent');
indicator.classList.add('bg-slate-600'); indicator.classList.add('bg-[var(--theme-text-subtle)]');
} }
}); });
// Add active state to current link // Add active state to current link
const activeLink = document.querySelector(`[data-toc-link="${activeHeading.id}"]`); const activeLink = document.querySelector(`[data-toc-link="${activeHeading.id}"]`);
if (activeLink) { if (activeLink) {
activeLink.classList.remove('text-slate-400'); activeLink.classList.remove('text-[var(--theme-text-secondary)]');
activeLink.classList.add('text-brand-accent'); activeLink.classList.add('text-brand-accent');
const indicator = activeLink.querySelector('.toc-indicator'); const indicator = activeLink.querySelector('.toc-indicator');
if (indicator) { if (indicator) {
indicator.classList.remove('bg-slate-600'); indicator.classList.remove('bg-[var(--theme-text-subtle)]');
indicator.classList.add('bg-brand-accent'); indicator.classList.add('bg-brand-accent');
} }
} }

View File

@ -0,0 +1,87 @@
---
---
<div class="theme-toggle-group flex items-center gap-3 ml-2 select-none" role="group" aria-label="Theme selector">
<div class="flex items-center text-[var(--theme-text-subtle)] opacity-50">
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
<polyline points="15 10 20 15 15 20"></polyline>
<path d="M4 4v7a4 4 0 0 0 4 4h12"></path>
</svg>
</div>
<div class="flex items-center gap-2.5">
<button
type="button"
class="theme-toggle-dark w-2.5 h-2.5 rounded-full bg-[#000] border border-white/20 hover:border-brand-accent/50 transition-all duration-300 relative group cursor-pointer"
aria-label="Dark theme"
title="Dark theme"
>
<span class="absolute -inset-1 border border-brand-accent rounded-full opacity-0 group-[.is-current-theme]:opacity-100 transition-opacity duration-300"></span>
</button>
<button
type="button"
class="theme-toggle-light w-2.5 h-2.5 rounded-full bg-[#efefef] border border-black/10 hover:border-brand-accent/50 transition-all duration-300 relative group cursor-pointer"
aria-label="Light theme"
title="Light theme"
>
<span class="absolute -inset-1 border border-brand-accent rounded-full opacity-0 group-[.is-current-theme]:opacity-100 transition-opacity duration-300"></span>
</button>
</div>
</div>
<script>
function initThemeToggle() {
const groups = document.querySelectorAll('.theme-toggle-group');
function getTheme(): 'dark' | 'light' {
const stored = localStorage.getItem('theme');
if (stored === 'light' || stored === 'dark') {
return stored;
}
return 'dark';
}
function updateAllToggles(theme: 'dark' | 'light') {
groups.forEach(group => {
const darkBtn = group.querySelector('.theme-toggle-dark');
const lightBtn = group.querySelector('.theme-toggle-light');
darkBtn?.classList.remove('is-current-theme');
lightBtn?.classList.remove('is-current-theme');
if (theme === 'dark') {
darkBtn?.classList.add('is-current-theme');
} else {
lightBtn?.classList.add('is-current-theme');
}
});
}
function setTheme(theme: 'dark' | 'light') {
if (document.documentElement.getAttribute('data-theme') === theme) return;
document.documentElement.classList.add('theme-transition');
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
updateAllToggles(theme);
setTimeout(() => {
document.documentElement.classList.remove('theme-transition');
}, 300);
}
const currentTheme = getTheme();
updateAllToggles(currentTheme);
groups.forEach(group => {
const darkBtn = group.querySelector('.theme-toggle-dark');
const lightBtn = group.querySelector('.theme-toggle-light');
darkBtn?.addEventListener('click', () => setTheme('dark'));
lightBtn?.addEventListener('click', () => setTheme('light'));
});
}
initThemeToggle();
document.addEventListener('astro:page-load', initThemeToggle);
</script>

View File

@ -25,87 +25,126 @@ interface Props {
const { sectionTitle, sectionSubtitle, sectionLabel, description, entries } = Astro.props; const { sectionTitle, sectionSubtitle, sectionLabel, description, entries } = Astro.props;
--- ---
<section id="experience" class="container mx-auto px-6 lg:px-12 py-32 border-t border-white/10"> <section id="experience" class="w-full py-32 border-t border-[var(--theme-border-primary)] bg-[var(--theme-bg-primary)] overflow-hidden">
<div class="container mx-auto px-6 lg:px-12">
<!-- Section Header --> <!-- Section Header -->
<div class="grid grid-cols-1 lg:grid-cols-12 gap-12 mb-24 lg:mb-32 group cursor-pointer"> <div class="grid grid-cols-1 lg:grid-cols-12 gap-12 mb-24 lg:mb-32">
<div class="lg:col-span-8"> <div class="lg:col-span-8 group cursor-default">
<h2 class="text-6xl md:text-8xl lg:text-9xl font-bold uppercase tracking-tighter leading-[0.85] text-white"> <div class="flex items-center gap-3 mb-6 intro-element animate-on-scroll fade-in">
<span class="block animate-on-scroll slide-up">{sectionTitle}</span> <div class="w-2 h-2 bg-brand-accent animate-pulse"></div>
<span class="block animate-on-scroll slide-up stagger-1 text-stroke group-hover:text-brand-accent transition-all duration-500 ease-out">{sectionSubtitle}</span> <span class="font-mono text-[10px] uppercase tracking-[0.3em] text-brand-accent">SYS.RECORDS /// WORK_HISTORY</span>
</h2>
</div>
<div class="lg:col-span-4 flex items-end">
<p class="text-slate-400 text-lg leading-relaxed animate-on-scroll slide-up stagger-2 border-l-2 border-brand-accent pl-6">
{description}
</p>
</div>
</div>
<!-- Experience List -->
<div class="w-full border-t border-white/10">
{entries.map((entry, index) => (
<a
href={entry.link?.url || '#'}
class="group block border-b border-white/10 py-10 hover:border-brand-accent/30 transition-colors duration-300"
>
<!-- Entry Header -->
<div class="flex items-baseline justify-between mb-6">
<div class="flex-grow">
<h3 class="text-3xl md:text-5xl font-bold text-white uppercase tracking-tighter group-hover:text-brand-accent transition-colors duration-300">
{entry.company}
</h3>
<p class="text-sm font-mono text-slate-400 mt-2">
{entry.role}
</p>
</div>
<div class="hidden md:block text-right ml-12 flex-shrink-0">
<div class="text-sm font-mono text-brand-accent">{String(index + 1).padStart(2, '0')}</div>
</div>
</div> </div>
<h2 class="text-5xl md:text-7xl lg:text-8xl font-bold uppercase tracking-tighter leading-[0.85] text-[var(--theme-text-primary)]">
<span class="block">{sectionTitle}</span>
<span class="block text-brand-accent">
{sectionSubtitle}
</span>
</h2>
</div>
<div class="lg:col-span-4 flex flex-col justify-end">
<div class="font-mono text-[10px] text-[var(--theme-text-subtle)] uppercase tracking-widest mb-4 animate-on-scroll fade-in stagger-2 flex items-center gap-2">
<span class="w-8 h-px bg-brand-accent/30"></span>
DATA_ARCHIVE_V7
</div>
<p class="text-[var(--theme-text-secondary)] text-lg leading-relaxed animate-on-scroll slide-up stagger-2 border-l border-brand-accent/30 pl-6">
{description}
</p>
</div>
</div>
<!-- Entry Content Grid --> <!-- Experience List -->
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12"> <div class="w-full border-t border-[var(--theme-border-primary)]">
<!-- Left Column: Metadata --> {entries.map((entry, index) => (
<div class="lg:col-span-4 space-y-6"> <div class="group relative border-b border-[var(--theme-border-primary)] hover:bg-white/[0.01] transition-all duration-500 overflow-hidden">
<div> <!-- Industrial Side Accent -->
<div class="text-[10px] font-mono uppercase tracking-widest text-slate-500 mb-2">Period</div> <div class="absolute left-0 top-0 bottom-0 w-1 bg-brand-accent transform -translate-x-full group-hover:translate-x-0 transition-transform duration-500 ease-out"></div>
<div class="font-bold text-white">{entry.dates}</div>
</div> <a
href={entry.link?.url || '#'}
{entry.tags && entry.tags.length > 0 && ( class="block py-12 lg:py-16 px-4 lg:px-8"
<div> >
<div class="text-[10px] font-mono uppercase tracking-widest text-slate-500 mb-2">Stack</div> <div class="grid grid-cols-1 lg:grid-cols-12 gap-12 items-start">
<div class="text-slate-400 font-mono text-xs md:text-sm tracking-wide group-hover:text-white transition-colors duration-300"> <!-- Left: Header & Role -->
{entry.tags.join(', ')} <div class="lg:col-span-4">
<div class="flex items-center gap-4 mb-4">
<span class="font-mono text-xs text-brand-accent opacity-50 group-hover:opacity-100 transition-opacity">[{entry.systemId || `EXP.0${index + 1}`}]</span>
<div class="flex items-center gap-2">
<div class={`w-1.5 h-1.5 rounded-full ${index === 0 ? 'bg-brand-accent animate-pulse' : 'bg-[var(--theme-text-subtle)]'}`}></div>
<span class="font-mono text-[9px] uppercase tracking-widest text-[var(--theme-text-muted)]">{index === 0 ? 'PRODUCTION_LIVE' : 'PRODUCTION_WRAP'}</span>
</div>
</div> </div>
<h3 class="text-4xl md:text-5xl lg:text-6xl font-bold text-[var(--theme-text-primary)] uppercase tracking-tighter group-hover:text-brand-accent transition-colors duration-300 mb-4">
{entry.company}
</h3>
<p class="font-mono text-xs uppercase tracking-[0.2em] text-[var(--theme-text-secondary)]">
{entry.role}
</p>
</div> </div>
)}
</div>
<!-- Right Column: Description --> <!-- Middle: Metadata -->
<div class="lg:col-span-8"> <div class="lg:col-span-3 space-y-8 pt-2">
<p class="text-slate-400 leading-relaxed mb-6 group-hover:text-white transition-colors duration-300"> <div>
{entry.description} <div class="text-[10px] font-mono uppercase tracking-[0.2em] text-[var(--theme-text-muted)] mb-3 flex items-center gap-2">
</p> <span class="text-brand-accent">/</span> DUR.TIMELINE
</div>
<div class="font-mono text-sm text-[var(--theme-text-primary)]">{entry.dates}</div>
</div>
{entry.achievements && entry.achievements.length > 0 && ( {entry.tags && entry.tags.length > 0 && (
<div class="space-y-4"> <div>
{entry.achievements.map((achievement) => ( <div class="text-[10px] font-mono uppercase tracking-[0.2em] text-[var(--theme-text-muted)] mb-3 flex items-center gap-2">
<div class="border-l-2 border-white/10 pl-4"> <span class="text-brand-accent">/</span> PIP.TOOLSET
<div class="text-[10px] font-mono uppercase tracking-widest text-slate-500 mb-1">
{achievement.label}
</div> </div>
<div class="text-sm text-slate-400 group-hover:text-white transition-colors duration-300"> <div class="flex flex-wrap gap-2">
{achievement.text} {entry.tags.map(tag => (
<span class="text-[9px] font-mono uppercase tracking-wider px-2 py-1 border border-[var(--theme-border-secondary)] text-[var(--theme-text-muted)] group-hover:border-brand-accent/30 group-hover:text-[var(--theme-text-secondary)] transition-all">
{tag}
</span>
))}
</div> </div>
</div> </div>
))} )}
</div> </div>
)}
</div> <!-- Right: Content -->
<div class="lg:col-span-5 pt-2">
<div class="text-[10px] font-mono uppercase tracking-[0.2em] text-[var(--theme-text-muted)] mb-4 flex items-center gap-2">
<span class="text-brand-accent">/</span> LOG.PRODUCTION_MANIFEST
</div>
<p class="text-[var(--theme-text-secondary)] leading-relaxed mb-8 group-hover:text-[var(--theme-text-primary)] transition-colors duration-300">
{entry.description}
</p>
{entry.achievements && entry.achievements.length > 0 && (
<div class="space-y-6">
{entry.achievements.map((achievement) => (
<div class="relative pl-6 py-1 group/item">
<!-- Minimalist bullet -->
<div class="absolute left-0 top-3 w-3 h-px bg-brand-accent/40 group-hover/item:w-5 transition-all"></div>
<div class="text-[9px] font-mono uppercase tracking-widest text-brand-accent/60 mb-1">
{achievement.label}
</div>
<div class="text-sm text-[var(--theme-text-secondary)] group-hover:text-[var(--theme-text-primary)] transition-colors">
{achievement.text}
</div>
</div>
))}
</div>
)}
{entry.link && (
<div class="mt-8 flex items-center gap-2 font-mono text-[10px] text-brand-accent uppercase tracking-widest opacity-0 group-hover:opacity-100 translate-x-[-10px] group-hover:translate-x-0 transition-all duration-500">
<span>View Project</span>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="square" stroke-linejoin="miter">
<path d="M5 12h14M12 5l7 7-7 7"/>
</svg>
</div>
)}
</div>
</div>
</a>
</div> </div>
</a> ))}
))} </div>
</div> </div>
</section> </section>

View File

@ -33,33 +33,33 @@ const { role, client, year, region, projectTitle, projectSubtitle, projectDescri
<source src={videoUrl} type="video/mp4" /> <source src={videoUrl} type="video/mp4" />
</video> </video>
<!-- Cinematic Letterboxing / Gradient Vignette --> <!-- Cinematic Letterboxing / Gradient Vignette -->
<div class="absolute inset-0 bg-gradient-to-b from-brand-dark/80 via-transparent to-brand-dark/80 pointer-events-none"></div> <div class="absolute inset-0 bg-gradient-to-b from-[var(--theme-hero-gradient-top)] via-transparent to-[var(--theme-hero-gradient-top)] pointer-events-none transition-colors duration-500"></div>
<div class="absolute inset-0 bg-gradient-to-r from-brand-dark/40 via-transparent to-brand-dark/40 pointer-events-none"></div> <div class="absolute inset-0 bg-gradient-to-r from-[var(--theme-hero-gradient-side)] via-transparent to-[var(--theme-hero-gradient-side)] pointer-events-none transition-colors duration-500"></div>
<!-- Subtle Grid Overlay --> <!-- Subtle Grid Overlay -->
<div class="absolute inset-0 bg-[linear-gradient(rgba(255,255,255,0.02)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.02)_1px,transparent_1px)] bg-[size:60px_60px] pointer-events-none opacity-30"></div> <div class="absolute inset-0 bg-[linear-gradient(var(--theme-grid-line)_1px,transparent_1px),linear-gradient(90deg,var(--theme-grid-line)_1px,transparent_1px)] bg-[size:60px_60px] pointer-events-none opacity-30"></div>
</div> </div>
<!-- Main Content Container - Spaced to frame the video --> <!-- Main Content Container - Spaced to frame the video -->
<div class="container mx-auto px-6 lg:px-12 relative z-10 flex-1 flex flex-col justify-between py-12 lg:py-16 pointer-events-none"> <div class="container mx-auto px-6 lg:px-12 relative z-10 flex-1 flex flex-col justify-between py-12 lg:py-16 pointer-events-none">
<!-- TOP HUD: Telemetry Data --> <!-- TOP HUD: Telemetry Data -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-8 border-t border-white/20 pt-6 animate-on-scroll slide-up"> <div class="grid grid-cols-2 md:grid-cols-4 gap-8 border-t border-[var(--theme-border-strong)] pt-6 animate-on-scroll slide-up">
<div> <div>
<span class="text-[9px] font-mono text-brand-accent uppercase tracking-widest block mb-1">/// Role</span> <span class="text-[9px] font-mono text-brand-accent uppercase tracking-widest block mb-1">/// Role</span>
<span class="text-xl md:text-2xl font-bold text-white uppercase tracking-tight">{role}</span> <span class="text-xl md:text-2xl font-bold text-[var(--theme-text-primary)] uppercase tracking-tight">{role}</span>
</div> </div>
<div> <div>
<span class="text-[9px] font-mono text-brand-accent uppercase tracking-widest block mb-1">/// Client</span> <span class="text-[9px] font-mono text-brand-accent uppercase tracking-widest block mb-1">/// Client</span>
<span class="text-xl md:text-2xl font-bold text-white uppercase tracking-tight">{client}</span> <span class="text-xl md:text-2xl font-bold text-[var(--theme-text-primary)] uppercase tracking-tight">{client}</span>
</div> </div>
<div> <div>
<span class="text-[9px] font-mono text-brand-accent uppercase tracking-widest block mb-1">/// Year</span> <span class="text-[9px] font-mono text-brand-accent uppercase tracking-widest block mb-1">/// Year</span>
<span class="text-xl md:text-2xl font-bold text-white uppercase tracking-tight">{year}</span> <span class="text-xl md:text-2xl font-bold text-[var(--theme-text-primary)] uppercase tracking-tight">{year}</span>
</div> </div>
<div class="text-right md:text-left"> <div class="text-right md:text-left">
<span class="text-[9px] font-mono text-brand-accent uppercase tracking-widest block mb-1">/// Region</span> <span class="text-[9px] font-mono text-brand-accent uppercase tracking-widest block mb-1">/// Region</span>
<span class="text-xl md:text-2xl font-bold text-white uppercase tracking-tight">{region}</span> <span class="text-xl md:text-2xl font-bold text-[var(--theme-text-primary)] uppercase tracking-tight">{region}</span>
</div> </div>
</div> </div>
@ -74,15 +74,15 @@ const { role, client, year, region, projectTitle, projectSubtitle, projectDescri
</div> </div>
<!-- BOTTOM HUD: Project Details & Stats --> <!-- BOTTOM HUD: Project Details & Stats -->
<div class="border-b border-white/20 pb-6 animate-on-scroll slide-up stagger-1"> <div class="border-b border-[var(--theme-border-strong)] pb-6 animate-on-scroll slide-up stagger-1">
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8 items-end"> <div class="grid grid-cols-1 lg:grid-cols-12 gap-8 items-end">
<!-- Title & Description --> <!-- Title & Description -->
<div class="lg:col-span-7"> <div class="lg:col-span-7">
<h2 class="text-5xl md:text-7xl font-bold uppercase text-white mb-4 tracking-tighter leading-none"> <h2 class="text-5xl md:text-7xl font-bold uppercase text-[var(--theme-text-primary)] mb-4 tracking-tighter leading-none">
{projectTitle} <span class="text-transparent text-stroke">{projectSubtitle}</span> {projectTitle} <span class="text-transparent text-stroke">{projectSubtitle}</span>
</h2> </h2>
<p class="text-slate-300 font-light max-w-lg text-sm md:text-base leading-relaxed"> <p class="text-[var(--theme-text-secondary)] font-light max-w-lg text-sm md:text-base leading-relaxed">
{projectDescription} {projectDescription}
</p> </p>
</div> </div>
@ -92,8 +92,8 @@ const { role, client, year, region, projectTitle, projectSubtitle, projectDescri
<div class="grid grid-cols-2 gap-x-8 gap-y-4 font-mono text-xs"> <div class="grid grid-cols-2 gap-x-8 gap-y-4 font-mono text-xs">
{stats.map((stat) => ( {stats.map((stat) => (
<div class="border-l border-brand-accent/30 pl-3"> <div class="border-l border-brand-accent/30 pl-3">
<span class="block text-slate-500 text-[10px] uppercase mb-1">{stat.label}</span> <span class="block text-[var(--theme-text-muted)] text-[10px] uppercase mb-1">{stat.label}</span>
<span class="block text-white font-bold">{stat.value}</span> <span class="block text-[var(--theme-text-primary)] font-bold">{stat.value}</span>
</div> </div>
))} ))}
</div> </div>

View File

@ -13,7 +13,10 @@ interface Props {
const { headlineLine1, headlineLine2, portfolioYear, location, locationLabel, bio } = Astro.props; const { headlineLine1, headlineLine2, portfolioYear, location, locationLabel, bio } = Astro.props;
--- ---
<section id="hero" class="relative w-full h-[100dvh] overflow-hidden bg-brand-dark"> <section id="hero" class="relative w-full h-[100dvh] overflow-hidden bg-[var(--theme-bg-primary)]">
<!-- Industrial Scanlines -->
<div class="absolute inset-0 z-1 pointer-events-none opacity-[0.03] bg-[linear-gradient(rgba(18,16,16,0)_50%,rgba(0,0,0,0.25)_50%),linear-gradient(90deg,rgba(255,0,0,0.06),rgba(0,255,0,0.02),rgba(0,0,112,0.06))] bg-[length:100%_2px,3px_100%]"></div>
<!-- Background Image (Portrait) - Optimized with AVIF/WebP --> <!-- Background Image (Portrait) - Optimized with AVIF/WebP -->
<div class="absolute top-0 right-0 w-full md:w-1/2 h-full z-0"> <div class="absolute top-0 right-0 w-full md:w-1/2 h-full z-0">
<div class="relative w-full h-full"> <div class="relative w-full h-full">
@ -23,20 +26,30 @@ const { headlineLine1, headlineLine2, portfolioYear, location, locationLabel, bi
widths={[640, 1024, 1600]} widths={[640, 1024, 1600]}
sizes="(max-width: 768px) 100vw, 50vw" sizes="(max-width: 768px) 100vw, 50vw"
alt="Nicholai Vogel portrait" alt="Nicholai Vogel portrait"
class="w-full h-full object-cover object-center opacity-0 mix-blend-luminosity md:opacity-0 transition-opacity duration-[2000ms] ease-out delay-500 intro-element" class="w-full h-full object-cover object-center opacity-0 mix-blend-luminosity transition-opacity duration-[2500ms] ease-out delay-700 intro-element"
id="hero-portrait" id="hero-portrait"
loading="eager" loading="eager"
decoding="sync" decoding="sync"
/> />
<div class="absolute inset-0 bg-gradient-to-l from-transparent via-brand-dark/50 to-brand-dark"></div> <div class="absolute inset-0 bg-gradient-to-l from-transparent via-[var(--theme-hero-gradient-side)] to-[var(--theme-bg-primary)] transition-colors duration-500"></div>
<div class="absolute inset-0 bg-gradient-to-t from-brand-dark via-transparent to-transparent"></div> <div class="absolute inset-0 bg-gradient-to-t from-[var(--theme-bg-primary)] via-transparent to-transparent transition-colors duration-500"></div>
<!-- Technical Overlay Elements -->
<div class="absolute bottom-12 right-12 hidden lg:flex flex-col items-end gap-1 font-mono text-[9px] text-brand-accent/40 uppercase tracking-[0.3em] intro-element opacity-0 delay-1000">
<span>COORD: 38.8339° N, 104.8214° W</span>
<span>ELV: 1,839M</span>
<div class="flex gap-2 mt-2">
<div class="w-8 h-px bg-brand-accent/20"></div>
<div class="w-2 h-px bg-brand-accent/40"></div>
</div>
</div>
</div> </div>
</div> </div>
<!-- The 100 Squares Grid Overlay --> <!-- The 100 Squares Grid Overlay -->
<div id="grid-container" class="absolute inset-0 z-10 w-full h-full grid grid-cols-10 grid-rows-10 pointer-events-none"> <div id="grid-container" class="absolute inset-0 z-10 w-full h-full grid grid-cols-10 grid-rows-10 pointer-events-none">
{Array.from({ length: 100 }).map((_, i) => ( {Array.from({ length: 100 }).map((_, i) => (
<div class="grid-cell w-full h-full border border-white/5 opacity-0 transition-all duration-500 ease-out" data-index={i}></div> <div class="grid-cell w-full h-full border border-[var(--theme-border-secondary)] opacity-0 transition-all duration-500 ease-out" data-index={i}></div>
))} ))}
</div> </div>
@ -46,38 +59,55 @@ const { headlineLine1, headlineLine2, portfolioYear, location, locationLabel, bi
<!-- Top Metadata --> <!-- Top Metadata -->
<div class="flex justify-between items-start w-full intro-element opacity-0 translate-y-4 transition-all duration-1000 ease-out delay-300"> <div class="flex justify-between items-start w-full intro-element opacity-0 translate-y-4 transition-all duration-1000 ease-out delay-300">
<div class="font-mono text-xs uppercase tracking-widest text-slate-500"> <div class="flex items-center gap-3">
{portfolioYear} <div class="w-1.5 h-1.5 bg-brand-accent animate-pulse"></div>
<div class="font-mono text-[10px] uppercase tracking-[0.2em] text-[var(--theme-text-muted)]">
<span class="text-brand-accent mr-1">SYS.PRTF</span> / {portfolioYear}
</div>
</div> </div>
<div class="font-mono text-xs text-slate-500 text-right tracking-wide">
<span class="block text-slate-600 mb-1 uppercase tracking-widest">{locationLabel}</span> <div class="font-mono text-[10px] text-[var(--theme-text-muted)] text-right tracking-[0.15em] uppercase">
{location}<br> <div class="mb-1 flex items-center justify-end gap-2">
<span id="clock" class="text-brand-accent">00:00:00 MST</span> <span class="text-[var(--theme-text-subtle)]">{locationLabel}</span>
<span class="text-brand-accent font-bold">///</span>
</div>
<div class="text-[var(--theme-text-secondary)]">{location}</div>
<div id="clock" class="text-brand-accent mt-0.5">00:00:00 MST</div>
</div> </div>
</div> </div>
<!-- Main Heading & Description --> <!-- Main Heading & Description -->
<div class="max-w-5xl"> <div class="max-w-5xl">
<h1 class="text-6xl md:text-8xl lg:text-9xl tracking-tighter leading-[0.9] font-bold text-white mix-blend-overlay opacity-90 mb-8 perspective-text"> <h1 class="text-6xl md:text-8xl lg:text-9xl tracking-tighter leading-[0.85] font-bold text-[var(--theme-text-primary)] mb-8 perspective-text">
<span class="block intro-element opacity-0 translate-y-10 transition-all duration-1000 ease-out delay-100">{headlineLine1}</span> <span class="block intro-element opacity-0 translate-y-10 transition-all duration-1000 ease-out delay-100">{headlineLine1}</span>
<span class="block text-brand-accent opacity-0 translate-y-10 transition-all duration-1000 ease-out delay-200 intro-element">{headlineLine2}</span> <span class="block text-brand-accent opacity-0 translate-y-10 transition-all duration-1000 ease-out delay-200 intro-element">{headlineLine2}</span>
</h1> </h1>
<p class="font-mono text-sm md:text-base max-w-lg text-slate-400 font-light leading-relaxed intro-element opacity-0 translate-y-6 transition-all duration-1000 ease-out delay-500"> <p class="font-mono text-sm md:text-base max-w-lg text-[var(--theme-text-secondary)] font-light leading-relaxed intro-element opacity-0 translate-y-6 transition-all duration-1000 ease-out delay-500">
{bio} {bio}
</p> </p>
</div> </div>
<!-- Bottom Navigation --> <!-- Bottom Navigation -->
<div class="flex justify-between items-end w-full intro-element opacity-0 transition-all duration-1000 ease-out delay-700"> <div class="flex justify-between items-end w-full intro-element opacity-0 transition-all duration-1000 ease-out delay-700">
<a href="#experience" class="flex items-center justify-center w-12 h-12 border border-white/10 rounded-full text-brand-accent hover:bg-brand-accent hover:text-brand-dark transition-all duration-300 group"> <a href="#experience" class="group flex items-center gap-6 py-2">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="group-hover:animate-bounce"> <div class="relative w-12 h-12 flex items-center justify-center border border-[var(--theme-border-primary)] text-brand-accent hover:border-brand-accent hover:bg-brand-accent/5 transition-all duration-300">
<path d="M12 5v14M19 12l-7 7-7-7"/> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter" class="group-hover:translate-y-1 transition-transform duration-300">
</svg> <path d="M7 13l5 5 5-5M12 6v12"/>
</svg>
<!-- Technical Corner Accent -->
<div class="absolute -top-px -left-px w-2 h-2 border-t border-l border-brand-accent opacity-0 group-hover:opacity-100 transition-opacity"></div>
<div class="absolute -bottom-px -right-px w-2 h-2 border-b border-r border-brand-accent opacity-0 group-hover:opacity-100 transition-opacity"></div>
</div>
<div class="flex flex-col font-mono text-[10px] uppercase tracking-[0.2em]">
<span class="text-[var(--theme-text-muted)] group-hover:text-brand-accent transition-colors">Scroll</span>
<span class="text-[var(--theme-text-subtle)] group-hover:text-[var(--theme-text-secondary)] transition-colors">To Explore</span>
</div>
</a> </a>
<div class="text-right font-mono text-xs text-slate-500 tracking-widest"> <div class="hidden md:block text-right font-mono text-[10px] text-[var(--theme-text-muted)] tracking-[0.2em] uppercase">
SCROLL TO EXPLORE <span class="text-[var(--theme-text-subtle)]">STATUS:</span> <span class="text-brand-accent">READY_FOR_INPUT</span><br>
<span class="text-[var(--theme-text-subtle)]">INDEX:</span> 00.01 / 05.00
</div> </div>
</div> </div>
</div> </div>
@ -91,7 +121,7 @@ const { headlineLine1, headlineLine2, portfolioYear, location, locationLabel, bi
} }
/* Fade out */ /* Fade out */
.grid-cell { .grid-cell {
/* Slightly faster fade-out for a snappier feel */ /* Snappier fade-out */
transition: opacity 0.6s ease-out, background-color 0.6s ease-out; transition: opacity 0.6s ease-out, background-color 0.6s ease-out;
} }

View File

@ -24,46 +24,50 @@ const imageMap: Record<string, string> = {
}; };
--- ---
<section id="skills" class="bg-brand-dark py-32 lg:py-48 overflow-hidden relative cursor-default"> <section id="skills" class="bg-[var(--theme-bg-primary)] py-32 lg:py-48 overflow-hidden relative cursor-default">
<div class="container mx-auto px-6 lg:px-12 relative z-10"> <div class="container mx-auto px-6 lg:px-12 relative z-10">
<!-- Header Section --> <!-- Header Section -->
<div class="mb-24 lg:mb-32 grid grid-cols-1 lg:grid-cols-12 gap-12"> <div class="mb-24 lg:mb-32 grid grid-cols-1 lg:grid-cols-12 gap-12">
<div class="lg:col-span-8"> <div class="lg:col-span-8 group cursor-default">
<h2 class="text-6xl md:text-8xl lg:text-9xl font-bold uppercase tracking-tighter leading-[0.85] text-white"> <div class="flex items-center gap-3 mb-6 intro-element animate-on-scroll fade-in">
<span class="block relative overflow-hidden"> <div class="w-2 h-2 bg-brand-accent animate-pulse"></div>
<span class="animate-on-scroll slide-up block">{sectionTitle}</span> <span class="font-mono text-[10px] uppercase tracking-[0.3em] text-brand-accent">SYS.TOOLSET /// PIPELINE_CAPABILITIES</span>
</span> </div>
<span class="block relative overflow-hidden"> <h2 class="text-5xl md:text-7xl lg:text-8xl font-bold uppercase tracking-tighter leading-[0.85] text-[var(--theme-text-primary)]">
<span class="animate-on-scroll slide-up stagger-1 block text-stroke text-transparent">{sectionSubtitle}</span> <span class="block">{sectionTitle}</span>
</span> <span class="block text-brand-accent">{sectionSubtitle}</span>
</h2> </h2>
</div> </div>
<div class="lg:col-span-4 flex items-end"> <div class="lg:col-span-4 flex flex-col justify-end">
<p class="text-slate-400 text-lg leading-relaxed animate-on-scroll slide-up stagger-2 border-l-2 border-brand-accent pl-6"> <div class="font-mono text-[10px] text-[var(--theme-text-subtle)] uppercase tracking-widest mb-4 flex items-center gap-2">
<span class="w-8 h-px bg-brand-accent/30"></span>
TECH_STACK_MANIFEST
</div>
<p class="text-[var(--theme-text-secondary)] text-lg leading-relaxed border-l border-brand-accent/30 pl-6">
{description} {description}
</p> </p>
</div> </div>
</div> </div>
<!-- Skills Data Grid --> <!-- Skills Data Grid -->
<div class="w-full border-t border-white/10" id="skills-table"> <div class="w-full border-t border-[var(--theme-border-primary)]" id="skills-table">
<!-- Table Header --> <!-- Table Header -->
<div class="grid grid-cols-12 gap-4 py-6 border-b border-white/10 text-[10px] font-mono uppercase tracking-widest text-slate-500 select-none"> <div class="grid grid-cols-12 gap-4 py-8 border-b border-[var(--theme-border-primary)] text-[10px] font-mono uppercase tracking-[0.2em] text-[var(--theme-text-muted)] select-none">
<div class="col-span-2 md:col-span-1">/// ID</div> <div class="col-span-2 md:col-span-1">/// ID.TAG</div>
<div class="col-span-10 md:col-span-4">Domain</div> <div class="col-span-10 md:col-span-4">DOMAIN.SPECIALIZATION</div>
<div class="col-span-12 md:col-span-5 hidden md:block">Stack</div> <div class="col-span-12 md:col-span-5 hidden md:block">PIP.TOOLSET</div>
<div class="col-span-6 md:col-span-2 hidden md:block text-right">Proficiency</div> <div class="col-span-6 md:col-span-2 hidden md:block text-right">LVL.STATUS</div>
</div> </div>
{skills.map((skill, index) => { {skills.map((skill, index) => {
const proficiencyClass = skill.proficiency === "Expert" || skill.proficiency === "Specialist" const proficiencyClass = skill.proficiency === "Expert" || skill.proficiency === "Specialist"
? "border-brand-accent/50 text-brand-accent bg-brand-accent/5" ? "border-brand-accent/50 text-brand-accent bg-brand-accent/5"
: "border-white/20 text-slate-300"; : "border-[var(--theme-border-strong)] text-[var(--theme-text-secondary)]";
return ( return (
<div class={`skill-row group relative grid grid-cols-12 gap-4 py-10 border-b border-white/10 items-center transition-colors duration-300 hover:border-brand-accent/30 overflow-hidden ${index < skills.length - 1 ? '' : ''}`} data-image={imageMap[skill.id] || "default"}> <div class={`skill-row group relative grid grid-cols-12 gap-4 py-10 border-b border-[var(--theme-border-primary)] items-center transition-colors duration-300 hover:border-brand-accent/30 overflow-hidden`} data-image={imageMap[skill.id] || "default"}>
<!-- Hover Background Effect --> <!-- Hover Background Effect -->
<div class="absolute inset-0 bg-brand-accent/5 opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div> <div class="absolute inset-0 bg-brand-accent/5 opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
@ -75,13 +79,13 @@ const imageMap: Record<string, string> = {
<!-- Main Content (Domain) --> <!-- Main Content (Domain) -->
<div class="col-span-10 md:col-span-4 relative z-10"> <div class="col-span-10 md:col-span-4 relative z-10">
<h3 class="text-3xl md:text-5xl font-bold text-white uppercase tracking-tighter group-hover:text-brand-accent transition-colors duration-300">{skill.domain}</h3> <h3 class="text-3xl md:text-5xl font-bold text-[var(--theme-text-primary)] uppercase tracking-tighter group-hover:text-brand-accent transition-colors duration-300">{skill.domain}</h3>
<!-- Scan line effect for ALL items on hover --> <!-- Scan line effect for ALL items on hover -->
<div class="absolute bottom-0 left-0 h-[1px] w-full bg-brand-accent transform scale-x-0 group-hover:scale-x-100 transition-transform duration-700 ease-out origin-left opacity-0 group-hover:opacity-100"></div> <div class="absolute bottom-0 left-0 h-[1px] w-full bg-brand-accent transform scale-x-0 group-hover:scale-x-100 transition-transform duration-700 ease-out origin-left opacity-0 group-hover:opacity-100"></div>
</div> </div>
<!-- Tools Stack --> <!-- Tools Stack -->
<div class="col-span-12 md:col-span-5 text-slate-400 font-mono text-xs md:text-sm tracking-wide group-hover:text-white transition-colors duration-300 z-10"> <div class="col-span-12 md:col-span-5 text-[var(--theme-text-secondary)] font-mono text-xs md:text-sm tracking-wide group-hover:text-[var(--theme-text-primary)] transition-colors duration-300 z-10">
{skill.tools} {skill.tools}
</div> </div>

View File

@ -29,9 +29,17 @@ const {
--- ---
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="scroll-smooth"> <html lang="en" class="scroll-smooth" data-theme="dark">
<head> <head>
<meta name="x-nicholai-marker" content={HTML_MARKER} /> <meta name="x-nicholai-marker" content={HTML_MARKER} />
<!-- Theme initialization script - runs before page render to prevent flash -->
<script is:inline>
(function() {
const stored = localStorage.getItem('theme');
const theme = (stored === 'light' || stored === 'dark') ? stored : 'dark';
document.documentElement.setAttribute('data-theme', theme);
})();
</script>
<BaseHead <BaseHead
title={title} title={title}
description={description} description={description}
@ -43,7 +51,7 @@ const {
<slot name="head" /> <slot name="head" />
</head> </head>
<body class="antialiased selection:bg-brand-accent selection:text-brand-dark bg-brand-dark text-white"> <body class="antialiased selection:bg-brand-accent selection:text-brand-dark">
<!-- Only hydrate custom cursor on devices that can actually benefit from it --> <!-- Only hydrate custom cursor on devices that can actually benefit from it -->
<CustomCursor client:media="(pointer: fine) and (hover: hover)" /> <CustomCursor client:media="(pointer: fine) and (hover: hover)" />
<GridOverlay /> <GridOverlay />
@ -180,4 +188,3 @@ const {
</script> </script>
</body> </body>
</html> </html>

View File

@ -103,7 +103,7 @@ const articleSchema = {
<div class="lg:col-span-8 lg:col-start-3"> <div class="lg:col-span-8 lg:col-start-3">
<!-- Back Navigation --> <!-- Back Navigation -->
<div class="mb-12"> <div class="mb-12">
<a href="/blog" class="inline-flex items-center gap-3 px-5 py-3 border border-slate-700 bg-brand-dark/50 text-xs font-mono font-bold uppercase tracking-widest text-slate-400 hover:border-brand-accent hover:text-white hover:bg-brand-accent/5 transition-all duration-300 group backdrop-blur-sm"> <a href="/blog" class="inline-flex items-center gap-3 px-5 py-3 border border-[var(--theme-text-subtle)] bg-[var(--theme-overlay)] text-xs font-mono font-bold uppercase tracking-widest text-[var(--theme-text-secondary)] hover:border-brand-accent hover:text-[var(--theme-text-primary)] hover:bg-brand-accent/5 transition-all duration-300 group backdrop-blur-sm">
<span class="text-brand-accent group-hover:-translate-x-1 transition-transform duration-300">&lt;</span> <span class="text-brand-accent group-hover:-translate-x-1 transition-transform duration-300">&lt;</span>
<span>RETURN_TO_ARCHIVE</span> <span>RETURN_TO_ARCHIVE</span>
</a> </a>
@ -115,30 +115,30 @@ const articleSchema = {
<!-- Text Content --> <!-- Text Content -->
<div class="order-2 lg:order-1 relative z-10"> <div class="order-2 lg:order-1 relative z-10">
<!-- Metadata --> <!-- Metadata -->
<div class="flex flex-wrap items-center gap-4 text-[10px] font-mono text-slate-500 uppercase tracking-widest mb-8 border-b border-white/10 pb-4"> <div class="flex flex-wrap items-center gap-4 text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest mb-8 border-b border-[var(--theme-border-primary)] pb-4">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div class="w-1.5 h-1.5 bg-brand-accent shadow-[0_0_10px_rgba(221,65,50,0.5)] rounded-full animate-pulse"></div> <div class="w-1.5 h-1.5 bg-brand-accent shadow-[0_0_10px_rgba(221,65,50,0.5)] rounded-full animate-pulse"></div>
<span class="text-brand-accent font-bold">SYS.LOG</span> <span class="text-brand-accent font-bold">SYS.LOG</span>
</div> </div>
<span class="text-slate-700">/</span> <span class="text-[var(--theme-text-subtle)]">/</span>
<FormattedDate date={pubDate} /> <FormattedDate date={pubDate} />
<span class="text-slate-700">/</span> <span class="text-[var(--theme-text-subtle)]">/</span>
<span>{readTime}</span> <span>{readTime}</span>
</div> </div>
{category && ( {category && (
<div class="mb-6"> <div class="mb-6">
<span class="inline-block px-3 py-1.5 text-[10px] font-mono font-bold uppercase tracking-[0.2em] bg-white/5 border border-white/10 text-brand-accent hover:bg-brand-accent/10 transition-colors cursor-default"> <span class="inline-block px-3 py-1.5 text-[10px] font-mono font-bold uppercase tracking-[0.2em] bg-[var(--theme-hover-bg-strong)] border border-[var(--theme-border-primary)] text-brand-accent hover:bg-brand-accent/10 transition-colors cursor-default">
/// {category} /// {category}
</span> </span>
</div> </div>
)} )}
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold text-white uppercase leading-[0.9] tracking-tighter mb-8 break-words text-balance"> <h1 class="text-4xl md:text-5xl lg:text-6xl font-bold text-[var(--theme-text-primary)] uppercase leading-[0.9] tracking-tighter mb-8 break-words text-balance">
{title} {title}
</h1> </h1>
<p class="text-base md:text-lg text-slate-400 leading-relaxed font-light mb-8 border-l-2 border-brand-accent pl-6"> <p class="text-base md:text-lg text-[var(--theme-text-secondary)] leading-relaxed font-light mb-8 border-l-2 border-brand-accent pl-6">
{description} {description}
</p> </p>
@ -146,7 +146,7 @@ const articleSchema = {
{tags && tags.length > 0 && ( {tags && tags.length > 0 && (
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
{tags.map((tag) => ( {tags.map((tag) => (
<span class="px-2 py-1 text-[9px] font-mono uppercase bg-brand-dark border border-slate-800 text-slate-500 hover:border-brand-accent/50 hover:text-white transition-colors cursor-default"> <span class="px-2 py-1 text-[9px] font-mono uppercase bg-[var(--theme-bg-primary)] border border-[var(--theme-border-primary)] text-[var(--theme-text-muted)] hover:border-brand-accent/50 hover:text-[var(--theme-text-primary)] transition-colors cursor-default">
#{tag} #{tag}
</span> </span>
))} ))}
@ -157,7 +157,7 @@ const articleSchema = {
<!-- Hero Image --> <!-- Hero Image -->
{heroImage && ( {heroImage && (
<div class="order-1 lg:order-2"> <div class="order-1 lg:order-2">
<div class="relative aspect-[4/3] lg:aspect-square overflow-hidden border border-white/10 bg-white/[0.02] group"> <div class="relative aspect-[4/3] lg:aspect-square overflow-hidden border border-[var(--theme-border-primary)] bg-[var(--theme-hover-bg)] group">
<!-- Tech corners --> <!-- Tech corners -->
<div class="absolute top-0 left-0 w-2 h-2 border-t border-l border-brand-accent z-20"></div> <div class="absolute top-0 left-0 w-2 h-2 border-t border-l border-brand-accent z-20"></div>
<div class="absolute top-0 right-0 w-2 h-2 border-t border-r border-brand-accent z-20"></div> <div class="absolute top-0 right-0 w-2 h-2 border-t border-r border-brand-accent z-20"></div>
@ -188,30 +188,30 @@ const articleSchema = {
</div> </div>
<!-- Author Footer --> <!-- Author Footer -->
<footer class="mt-24 pt-10 border-t border-white/10"> <footer class="mt-24 pt-10 border-t border-[var(--theme-border-primary)]">
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-6"> <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-6">
<div> <div>
<p class="text-[10px] font-mono text-brand-accent uppercase tracking-widest mb-2 flex items-center gap-2"> <p class="text-[10px] font-mono text-brand-accent uppercase tracking-widest mb-2 flex items-center gap-2">
<span class="w-1.5 h-1.5 bg-brand-accent rounded-full animate-pulse"></span> <span class="w-1.5 h-1.5 bg-brand-accent rounded-full animate-pulse"></span>
/// END TRANSMISSION /// END TRANSMISSION
</p> </p>
<p class="text-slate-400 text-sm font-mono"> <p class="text-[var(--theme-text-secondary)] text-sm font-mono">
LOG_DATE: <FormattedDate date={pubDate} /> LOG_DATE: <FormattedDate date={pubDate} />
{updatedDate && ( {updatedDate && (
<span class="text-slate-500"> // UPDATED: <FormattedDate date={updatedDate} /></span> <span class="text-[var(--theme-text-muted)]"> // UPDATED: <FormattedDate date={updatedDate} /></span>
)} )}
</p> </p>
</div> </div>
<!-- Share Links --> <!-- Share Links -->
<div class="flex items-center gap-6"> <div class="flex items-center gap-6">
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-widest">DATA_UPLINK:</span> <span class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest">DATA_UPLINK:</span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<a <a
href={`https://twitter.com/intent/tweet?text=${encodeURIComponent(title)}&url=${encodeURIComponent(Astro.url.href)}`} href={`https://twitter.com/intent/tweet?text=${encodeURIComponent(title)}&url=${encodeURIComponent(Astro.url.href)}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="w-10 h-10 flex items-center justify-center border border-white/10 text-slate-400 hover:border-brand-accent hover:text-brand-accent transition-all duration-300" class="w-10 h-10 flex items-center justify-center border border-[var(--theme-border-primary)] text-[var(--theme-text-secondary)] hover:border-brand-accent hover:text-brand-accent transition-all duration-300"
aria-label="Share on Twitter" aria-label="Share on Twitter"
> >
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@ -222,7 +222,7 @@ const articleSchema = {
href={`https://www.linkedin.com/shareArticle?mini=true&url=${encodeURIComponent(Astro.url.href)}&title=${encodeURIComponent(title)}`} href={`https://www.linkedin.com/shareArticle?mini=true&url=${encodeURIComponent(Astro.url.href)}&title=${encodeURIComponent(title)}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="w-10 h-10 flex items-center justify-center border border-white/10 text-slate-400 hover:border-brand-accent hover:text-brand-accent transition-all duration-300" class="w-10 h-10 flex items-center justify-center border border-[var(--theme-border-primary)] text-[var(--theme-text-secondary)] hover:border-brand-accent hover:text-brand-accent transition-all duration-300"
aria-label="Share on LinkedIn" aria-label="Share on LinkedIn"
> >
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@ -234,7 +234,7 @@ const articleSchema = {
<button <button
type="button" type="button"
onclick="navigator.clipboard.writeText(window.location.href)" onclick="navigator.clipboard.writeText(window.location.href)"
class="w-10 h-10 flex items-center justify-center border border-white/10 text-slate-400 hover:border-brand-accent hover:text-brand-accent transition-all duration-300" class="w-10 h-10 flex items-center justify-center border border-[var(--theme-border-primary)] text-[var(--theme-text-secondary)] hover:border-brand-accent hover:text-brand-accent transition-all duration-300"
aria-label="Copy link" aria-label="Copy link"
> >
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@ -254,8 +254,8 @@ const articleSchema = {
<RelatedPosts posts={relatedPosts} /> <RelatedPosts posts={relatedPosts} />
<!-- Back to Blog --> <!-- Back to Blog -->
<div class="mt-20 pt-10 border-t border-white/10 flex justify-center lg:justify-start"> <div class="mt-20 pt-10 border-t border-[var(--theme-border-primary)] flex justify-center lg:justify-start">
<a href="/blog" class="inline-flex items-center gap-4 px-8 py-4 border border-slate-600 text-xs font-bold uppercase tracking-widest text-white hover:border-brand-accent hover:bg-brand-accent hover:text-brand-dark transition-all duration-300 group"> <a href="/blog" class="inline-flex items-center gap-4 px-8 py-4 border border-[var(--theme-border-strong)] text-xs font-bold uppercase tracking-widest text-[var(--theme-text-primary)] hover:border-brand-accent hover:bg-brand-accent hover:text-brand-dark transition-all duration-300 group">
<span class="font-mono transition-transform duration-300 group-hover:-translate-x-1">&lt;</span> <span class="font-mono transition-transform duration-300 group-hover:-translate-x-1">&lt;</span>
ACCESS_FULL_ARCHIVE ACCESS_FULL_ARCHIVE
</a> </a>

View File

@ -31,7 +31,7 @@ const categories = [...new Set(allPosts.map((post) => post.data.category).filter
<section class="container mx-auto px-6 lg:px-12"> <section class="container mx-auto px-6 lg:px-12">
<!-- Back Navigation --> <!-- Back Navigation -->
<div class="mb-12"> <div class="mb-12">
<a href="/" class="inline-flex items-center gap-3 px-5 py-3 border border-slate-700 bg-brand-dark/50 text-xs font-mono font-bold uppercase tracking-widest text-slate-400 hover:border-brand-accent hover:text-white hover:bg-brand-accent/5 transition-all duration-300 group backdrop-blur-sm"> <a href="/" class="inline-flex items-center gap-3 px-5 py-3 border border-[var(--theme-border-primary)] bg-[var(--theme-overlay)] text-xs font-mono font-bold uppercase tracking-widest text-[var(--theme-text-secondary)] hover:border-brand-accent hover:text-[var(--theme-text-primary)] hover:bg-brand-accent/5 transition-all duration-300 group backdrop-blur-sm">
<span class="text-brand-accent group-hover:-translate-x-1 transition-transform duration-300">&lt;</span> <span class="text-brand-accent group-hover:-translate-x-1 transition-transform duration-300">&lt;</span>
<span>RETURN_TO_HOME</span> <span>RETURN_TO_HOME</span>
</a> </a>
@ -40,14 +40,21 @@ const categories = [...new Set(allPosts.map((post) => post.data.category).filter
<!-- Page Header --> <!-- Page Header -->
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12 mb-16 lg:mb-24"> <div class="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12 mb-16 lg:mb-24">
<div class="lg:col-span-8"> <div class="lg:col-span-8">
<h1 class="text-6xl md:text-8xl lg:text-9xl font-bold uppercase tracking-tighter leading-[0.85]"> <div class="flex items-center gap-3 mb-6">
<span class="block text-white animate-on-scroll slide-up">BLOG</span> <div class="w-2 h-2 bg-brand-accent animate-pulse"></div>
<span class="block text-transparent text-stroke animate-on-scroll slide-up stagger-1">ARCHIVE</span> <span class="font-mono text-[10px] uppercase tracking-[0.3em] text-brand-accent">SYS.LOG /// PRODUCTION_ARCHIVE</span>
</div>
<h1 class="text-5xl md:text-7xl lg:text-8xl font-bold uppercase tracking-tighter leading-[0.85]">
<span class="block text-[var(--theme-text-primary)]">BLOG</span>
<span class="block text-brand-accent">ARCHIVE</span>
</h1> </h1>
</div> </div>
<div class="lg:col-span-4 flex flex-col justify-end pb-4"> <div class="lg:col-span-4 flex flex-col justify-end">
<div class="font-mono text-xs text-slate-500 uppercase tracking-widest mb-4">/// THOUGHTS & PROCESS</div> <div class="font-mono text-[10px] text-[var(--theme-text-subtle)] uppercase tracking-widest mb-4 flex items-center gap-2">
<p class="text-slate-400 text-base leading-relaxed border-l border-brand-accent pl-6 animate-on-scroll fade-in stagger-2"> <span class="w-8 h-px bg-brand-accent/30"></span>
THOUGHTS & PROCESS
</div>
<p class="text-[var(--theme-text-secondary)] text-lg leading-relaxed border-l border-brand-accent/30 pl-6">
Deep dives into VFX production, technical pipelines, and creative process. Sharing lessons from the front lines of visual effects. Deep dives into VFX production, technical pipelines, and creative process. Sharing lessons from the front lines of visual effects.
</p> </p>
</div> </div>
@ -61,10 +68,10 @@ const categories = [...new Set(allPosts.map((post) => post.data.category).filter
<span class="text-[10px] font-mono text-brand-accent uppercase tracking-widest font-bold"> <span class="text-[10px] font-mono text-brand-accent uppercase tracking-widest font-bold">
SYS.BLOG /// FEATURED SYS.BLOG /// FEATURED
</span> </span>
<span class="h-px flex-grow bg-white/10"></span> <span class="h-px flex-grow bg-[var(--theme-border-secondary)]"></span>
</div> </div>
<article class="group relative border border-white/10 bg-white/[0.02] hover:border-brand-accent/40 transition-all duration-500 overflow-hidden"> <article class="group relative border border-[var(--theme-border-primary)] bg-[var(--theme-hover-bg)] hover:border-brand-accent/40 transition-all duration-500 overflow-hidden">
<!-- Accent indicator strip --> <!-- Accent indicator strip -->
<div class="absolute top-0 left-0 w-1 h-full bg-brand-accent"></div> <div class="absolute top-0 left-0 w-1 h-full bg-brand-accent"></div>
<div class="absolute top-0 left-0 w-full h-1 bg-brand-accent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div> <div class="absolute top-0 left-0 w-full h-1 bg-brand-accent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
@ -81,14 +88,14 @@ const categories = [...new Set(allPosts.map((post) => post.data.category).filter
class="w-full h-full object-cover transition-transform duration-[1.2s] ease-out group-hover:scale-105" class="w-full h-full object-cover transition-transform duration-[1.2s] ease-out group-hover:scale-105"
/> />
)} )}
<div class="absolute inset-0 bg-brand-dark/30 group-hover:bg-brand-dark/10 transition-colors duration-500"></div> <div class="absolute inset-0 bg-[var(--theme-card-overlay)] group-hover:opacity-50 transition-opacity duration-500"></div>
<div class="absolute inset-0 bg-gradient-to-r from-transparent via-transparent to-brand-dark/80 hidden lg:block"></div> <div class="absolute inset-0 bg-gradient-to-r from-transparent via-transparent to-[var(--theme-card-gradient)] hidden lg:block"></div>
<div class="absolute inset-0 bg-gradient-to-t from-brand-dark/60 to-transparent lg:hidden"></div> <div class="absolute inset-0 bg-gradient-to-t from-[var(--theme-card-gradient)] to-transparent lg:hidden"></div>
<!-- Category badge --> <!-- Category badge -->
{featuredPost.data.category && ( {featuredPost.data.category && (
<div class="absolute top-6 left-6"> <div class="absolute top-6 left-6">
<span class="px-4 py-2 text-[10px] font-mono font-bold uppercase tracking-widest bg-brand-dark/80 border border-brand-accent/50 text-brand-accent backdrop-blur-sm"> <span class="px-4 py-2 text-[10px] font-mono font-bold uppercase tracking-widest bg-[var(--theme-overlay)] border border-brand-accent/50 text-brand-accent backdrop-blur-sm">
{featuredPost.data.category} {featuredPost.data.category}
</span> </span>
</div> </div>
@ -105,21 +112,21 @@ const categories = [...new Set(allPosts.map((post) => post.data.category).filter
<span class="text-[10px] font-mono text-brand-accent uppercase tracking-widest"> <span class="text-[10px] font-mono text-brand-accent uppercase tracking-widest">
<FormattedDate date={featuredPost.data.pubDate} /> <FormattedDate date={featuredPost.data.pubDate} />
</span> </span>
<span class="h-px w-8 bg-white/20"></span> <span class="h-px w-8 bg-[var(--theme-border-strong)]"></span>
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-widest"> <span class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest">
5 min read 5 min read
</span> </span>
</div> </div>
<!-- Title --> <!-- Title -->
<a href={`/blog/${featuredPost.id}/`}> <a href={`/blog/${featuredPost.id}/`}>
<h2 class="text-3xl lg:text-4xl xl:text-5xl font-bold text-white uppercase tracking-tight mb-6 group-hover:text-brand-accent transition-colors duration-300 leading-tight"> <h2 class="text-3xl lg:text-4xl xl:text-5xl font-bold text-[var(--theme-text-primary)] uppercase tracking-tight mb-6 group-hover:text-brand-accent transition-colors duration-300 leading-tight">
{featuredPost.data.title} {featuredPost.data.title}
</h2> </h2>
</a> </a>
<!-- Description --> <!-- Description -->
<p class="text-slate-400 text-base lg:text-lg font-light leading-relaxed mb-8 line-clamp-3"> <p class="text-[var(--theme-text-secondary)] text-base lg:text-lg font-light leading-relaxed mb-8 line-clamp-3">
{featuredPost.data.description} {featuredPost.data.description}
</p> </p>
@ -127,7 +134,7 @@ const categories = [...new Set(allPosts.map((post) => post.data.category).filter
{featuredPost.data.tags && featuredPost.data.tags.length > 0 && ( {featuredPost.data.tags && featuredPost.data.tags.length > 0 && (
<div class="flex flex-wrap gap-2 mb-8"> <div class="flex flex-wrap gap-2 mb-8">
{featuredPost.data.tags.slice(0, 5).map((tag: string) => ( {featuredPost.data.tags.slice(0, 5).map((tag: string) => (
<span class="px-3 py-1.5 text-[10px] font-mono uppercase border border-white/10 text-slate-500 group-hover:border-white/20 transition-colors"> <span class="px-3 py-1.5 text-[10px] font-mono uppercase border border-[var(--theme-border-primary)] text-[var(--theme-text-muted)] group-hover:border-[var(--theme-border-strong)] transition-colors">
{tag} {tag}
</span> </span>
))} ))}
@ -135,13 +142,13 @@ const categories = [...new Set(allPosts.map((post) => post.data.category).filter
)} )}
<!-- Read link --> <!-- Read link -->
<div class="pt-6 border-t border-white/10"> <div class="pt-6 border-t border-[var(--theme-border-primary)]">
<a <a
href={`/blog/${featuredPost.id}/`} href={`/blog/${featuredPost.id}/`}
class="inline-flex items-center gap-4 text-xs font-bold uppercase tracking-widest text-white hover:text-brand-accent transition-all duration-300 group/link" class="inline-flex items-center gap-4 text-xs font-bold uppercase tracking-widest text-[var(--theme-text-primary)] hover:text-brand-accent transition-all duration-300 group/link"
> >
Read Full Article Read Full Article
<span class="block w-8 h-[1px] bg-white/30 group-hover/link:bg-brand-accent group-hover/link:w-12 transition-all duration-300"></span> <span class="block w-8 h-[1px] bg-[var(--theme-border-strong)] group-hover/link:bg-brand-accent group-hover/link:w-12 transition-all duration-300"></span>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="14" width="14"
@ -168,10 +175,10 @@ const categories = [...new Set(allPosts.map((post) => post.data.category).filter
<!-- Latest Section with Filters --> <!-- Latest Section with Filters -->
<div class="mb-16 lg:mb-24"> <div class="mb-16 lg:mb-24">
<div class="flex items-center gap-4 mb-8"> <div class="flex items-center gap-4 mb-8">
<span class="text-[10px] font-mono text-slate-500 uppercase tracking-widest font-bold"> <span class="text-[10px] font-mono text-[var(--theme-text-muted)] uppercase tracking-widest font-bold">
/// LATEST TRANSMISSIONS /// LATEST TRANSMISSIONS
</span> </span>
<span class="h-px flex-grow bg-white/10"></span> <span class="h-px flex-grow bg-[var(--theme-border-secondary)]"></span>
</div> </div>
<!-- Filters Component --> <!-- Filters Component -->
@ -202,10 +209,10 @@ const categories = [...new Set(allPosts.map((post) => post.data.category).filter
<!-- Empty state (hidden by default, shown via JS when no results) --> <!-- Empty state (hidden by default, shown via JS when no results) -->
<div id="no-results" class="hidden text-center py-20"> <div id="no-results" class="hidden text-center py-20">
<div class="text-slate-500 font-mono text-sm uppercase tracking-widest mb-4"> <div class="text-[var(--theme-text-muted)] font-mono text-sm uppercase tracking-widest mb-4">
/// NO MATCHING ARTICLES FOUND /// NO MATCHING ARTICLES FOUND
</div> </div>
<p class="text-slate-400 text-sm"> <p class="text-[var(--theme-text-secondary)] text-sm">
Try adjusting your search or filter criteria. Try adjusting your search or filter criteria.
</p> </p>
</div> </div>

View File

@ -16,7 +16,7 @@ const contactContent = contactEntry.data;
<div class="fixed inset-0 z-0 pointer-events-none"> <div class="fixed inset-0 z-0 pointer-events-none">
<div class="w-full h-full grid grid-cols-12 gap-4 opacity-[0.03]"> <div class="w-full h-full grid grid-cols-12 gap-4 opacity-[0.03]">
{Array.from({ length: 12 }).map((_) => ( {Array.from({ length: 12 }).map((_) => (
<div class="h-full border-r border-white"></div> <div class="h-full border-r border-[var(--theme-text-primary)]"></div>
))} ))}
</div> </div>
</div> </div>
@ -24,15 +24,15 @@ const contactContent = contactEntry.data;
<section class="relative z-10 min-h-screen flex flex-col pt-32 lg:pt-48 pb-20 px-6 lg:px-12"> <section class="relative z-10 min-h-screen flex flex-col pt-32 lg:pt-48 pb-20 px-6 lg:px-12">
<!-- Page Header --> <!-- Page Header -->
<div class="grid grid-cols-1 lg:grid-cols-12 gap-12 mb-20 lg:mb-32 border-b border-white/10 pb-12"> <div class="grid grid-cols-1 lg:grid-cols-12 gap-12 mb-20 lg:mb-32 border-b border-[var(--theme-border-primary)] pb-12">
<div class="lg:col-span-8"> <div class="lg:col-span-8">
<h1 class="text-6xl md:text-8xl lg:text-9xl font-bold uppercase tracking-tighter leading-[0.85] text-white mb-8"> <h1 class="text-6xl md:text-8xl lg:text-9xl font-bold uppercase tracking-tighter leading-[0.85] text-[var(--theme-text-primary)] mb-8">
<span class="block animate-on-scroll slide-up">{contactContent.pageTitleLine1}</span> <span class="block animate-on-scroll slide-up">{contactContent.pageTitleLine1}</span>
<span class="block text-brand-accent animate-on-scroll slide-up stagger-1">{contactContent.pageTitleLine2}</span> <span class="block text-brand-accent animate-on-scroll slide-up stagger-1">{contactContent.pageTitleLine2}</span>
</h1> </h1>
</div> </div>
<div class="lg:col-span-4 flex flex-col justify-end"> <div class="lg:col-span-4 flex flex-col justify-end">
<p class="font-mono text-sm text-slate-400 leading-relaxed mb-8 border-l border-brand-accent pl-6 animate-on-scroll fade-in stagger-2"> <p class="font-mono text-sm text-[var(--theme-text-secondary)] leading-relaxed mb-8 border-l border-brand-accent pl-6 animate-on-scroll fade-in stagger-2">
{contactContent.availabilityText} {contactContent.availabilityText}
</p> </p>
</div> </div>
@ -53,11 +53,11 @@ const contactContent = contactEntry.data;
type="text" type="text"
id="name" id="name"
name="name" name="name"
class="block w-full bg-transparent border-b border-white/20 py-4 text-xl text-white focus:outline-none focus:border-brand-accent transition-colors duration-300 placeholder-transparent peer" class="block w-full bg-transparent border-b border-[var(--theme-border-strong)] py-4 text-xl text-[var(--theme-text-primary)] focus:outline-none focus:border-brand-accent transition-colors duration-300 placeholder-transparent peer"
placeholder="Name" placeholder="Name"
required required
/> />
<label for="name" class="absolute left-0 top-4 text-slate-500 text-sm font-mono uppercase tracking-widest transition-all duration-300 peer-focus:-top-6 peer-focus:text-xs peer-focus:text-brand-accent peer-valid:-top-6 peer-valid:text-xs peer-valid:text-slate-400 pointer-events-none"> <label for="name" class="absolute left-0 top-4 text-[var(--theme-text-muted)] text-sm font-mono uppercase tracking-widest transition-all duration-300 peer-focus:-top-6 peer-focus:text-xs peer-focus:text-brand-accent peer-valid:-top-6 peer-valid:text-xs peer-valid:text-[var(--theme-text-secondary)] pointer-events-none">
{contactContent.formLabels?.name} {contactContent.formLabels?.name}
</label> </label>
</div> </div>
@ -67,11 +67,11 @@ const contactContent = contactEntry.data;
type="email" type="email"
id="email" id="email"
name="email" name="email"
class="block w-full bg-transparent border-b border-white/20 py-4 text-xl text-white focus:outline-none focus:border-brand-accent transition-colors duration-300 placeholder-transparent peer" class="block w-full bg-transparent border-b border-[var(--theme-border-strong)] py-4 text-xl text-[var(--theme-text-primary)] focus:outline-none focus:border-brand-accent transition-colors duration-300 placeholder-transparent peer"
placeholder="Email" placeholder="Email"
required required
/> />
<label for="email" class="absolute left-0 top-4 text-slate-500 text-sm font-mono uppercase tracking-widest transition-all duration-300 peer-focus:-top-6 peer-focus:text-xs peer-focus:text-brand-accent peer-valid:-top-6 peer-valid:text-xs peer-valid:text-slate-400 pointer-events-none"> <label for="email" class="absolute left-0 top-4 text-[var(--theme-text-muted)] text-sm font-mono uppercase tracking-widest transition-all duration-300 peer-focus:-top-6 peer-focus:text-xs peer-focus:text-brand-accent peer-valid:-top-6 peer-valid:text-xs peer-valid:text-[var(--theme-text-secondary)] pointer-events-none">
{contactContent.formLabels?.email} {contactContent.formLabels?.email}
</label> </label>
</div> </div>
@ -80,22 +80,22 @@ const contactContent = contactEntry.data;
<div class="group relative" id="custom-select"> <div class="group relative" id="custom-select">
<input type="hidden" name="subject" id="subject-input" required> <input type="hidden" name="subject" id="subject-input" required>
<button type="button" id="select-trigger" class="block w-full text-left bg-transparent border-b border-white/20 py-4 text-xl text-white focus:outline-none focus:border-brand-accent transition-colors duration-300 flex justify-between items-center group-hover:border-white/40"> <button type="button" id="select-trigger" class="block w-full text-left bg-transparent border-b border-[var(--theme-border-strong)] py-4 text-xl text-[var(--theme-text-primary)] focus:outline-none focus:border-brand-accent transition-colors duration-300 flex justify-between items-center group-hover:border-[var(--theme-border-primary)]">
<span id="select-value" class="text-transparent">Select</span> <!-- Hidden placeholder text to keep height --> <span id="select-value" class="text-transparent">Select</span> <!-- Hidden placeholder text to keep height -->
<div class="text-brand-accent transform transition-transform duration-300" id="select-arrow"> <div class="text-brand-accent transform transition-transform duration-300" id="select-arrow">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
</div> </div>
</button> </button>
<label id="select-label" class="absolute left-0 top-4 text-slate-500 text-sm font-mono uppercase tracking-widest transition-all duration-300 pointer-events-none"> <label id="select-label" class="absolute left-0 top-4 text-[var(--theme-text-muted)] text-sm font-mono uppercase tracking-widest transition-all duration-300 pointer-events-none">
{contactContent.formLabels?.subject} {contactContent.formLabels?.subject}
</label> </label>
<!-- Dropdown Menu --> <!-- Dropdown Menu -->
<div id="select-options" class="absolute left-0 top-full w-full bg-brand-dark border border-white/20 shadow-2xl z-50 hidden opacity-0 transform translate-y-2 transition-all duration-200 origin-top mt-2"> <div id="select-options" class="absolute left-0 top-full w-full bg-[var(--theme-bg-primary)] border border-[var(--theme-border-strong)] shadow-2xl z-50 hidden opacity-0 transform translate-y-2 transition-all duration-200 origin-top mt-2">
<div class="p-1"> <div class="p-1">
{contactContent.subjectOptions?.map((option) => ( {contactContent.subjectOptions?.map((option) => (
<div class="option px-5 py-4 hover:bg-white/5 cursor-pointer text-white text-lg font-light transition-colors flex items-center gap-3 group/option" data-value={option.value}> <div class="option px-5 py-4 hover:bg-[var(--theme-hover-bg-strong)] cursor-pointer text-[var(--theme-text-primary)] text-lg font-light transition-colors flex items-center gap-3 group/option" data-value={option.value}>
<span class="w-1.5 h-1.5 rounded-full bg-brand-accent opacity-0 group-hover/option:opacity-100 transition-opacity"></span> <span class="w-1.5 h-1.5 rounded-full bg-brand-accent opacity-0 group-hover/option:opacity-100 transition-opacity"></span>
{option.label} {option.label}
</div> </div>
@ -109,19 +109,19 @@ const contactContent = contactEntry.data;
id="message" id="message"
name="message" name="message"
rows="4" rows="4"
class="block w-full bg-transparent border-b border-white/20 py-4 text-xl text-white focus:outline-none focus:border-brand-accent transition-colors duration-300 placeholder-transparent peer resize-none" class="block w-full bg-transparent border-b border-[var(--theme-border-strong)] py-4 text-xl text-[var(--theme-text-primary)] focus:outline-none focus:border-brand-accent transition-colors duration-300 placeholder-transparent peer resize-none"
placeholder="Message" placeholder="Message"
required required
></textarea> ></textarea>
<label for="message" class="absolute left-0 top-4 text-slate-500 text-sm font-mono uppercase tracking-widest transition-all duration-300 peer-focus:-top-6 peer-focus:text-xs peer-focus:text-brand-accent peer-valid:-top-6 peer-valid:text-xs peer-valid:text-slate-400 pointer-events-none"> <label for="message" class="absolute left-0 top-4 text-[var(--theme-text-muted)] text-sm font-mono uppercase tracking-widest transition-all duration-300 peer-focus:-top-6 peer-focus:text-xs peer-focus:text-brand-accent peer-valid:-top-6 peer-valid:text-xs peer-valid:text-[var(--theme-text-secondary)] pointer-events-none">
{contactContent.formLabels?.message} {contactContent.formLabels?.message}
</label> </label>
</div> </div>
<div class="pt-8"> <div class="pt-8">
<button type="submit" id="submit-btn" class="group relative inline-flex items-center justify-center gap-4 px-8 py-4 bg-transparent border border-white/20 hover:border-brand-accent hover:bg-brand-accent/5 transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed"> <button type="submit" id="submit-btn" class="group relative inline-flex items-center justify-center gap-4 px-8 py-4 bg-transparent border border-[var(--theme-border-strong)] hover:border-brand-accent hover:bg-brand-accent/5 transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed">
<span id="submit-text" data-default-text={contactContent.formLabels?.submit} class="font-mono text-xs font-bold uppercase tracking-widest text-white group-hover:text-brand-accent transition-colors">{contactContent.formLabels?.submit}</span> <span id="submit-text" data-default-text={contactContent.formLabels?.submit} class="font-mono text-xs font-bold uppercase tracking-widest text-[var(--theme-text-primary)] group-hover:text-brand-accent transition-colors">{contactContent.formLabels?.submit}</span>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-slate-500 group-hover:text-brand-accent group-hover:translate-x-1 transition-all"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-[var(--theme-text-muted)] group-hover:text-brand-accent group-hover:translate-x-1 transition-all"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
</button> </button>
</div> </div>
</form> </form>
@ -131,19 +131,19 @@ const contactContent = contactEntry.data;
<div class="lg:col-span-5 space-y-16 animate-on-scroll slide-left stagger-4"> <div class="lg:col-span-5 space-y-16 animate-on-scroll slide-left stagger-4">
<!-- Data Block 1 --> <!-- Data Block 1 -->
<div class="relative pl-6 border-l border-white/10"> <div class="relative pl-6 border-l border-[var(--theme-border-primary)]">
<h3 class="font-mono text-xs text-slate-500 uppercase tracking-widest mb-4">Direct Link</h3> <h3 class="font-mono text-xs text-[var(--theme-text-muted)] uppercase tracking-widest mb-4">Direct Link</h3>
<a href={`mailto:${contactContent.email}`} class="text-2xl md:text-3xl font-bold text-white hover:text-brand-accent transition-colors break-all"> <a href={`mailto:${contactContent.email}`} class="text-2xl md:text-3xl font-bold text-[var(--theme-text-primary)] hover:text-brand-accent transition-colors break-all">
{contactContent.email} {contactContent.email}
</a> </a>
</div> </div>
<!-- Data Block 2 --> <!-- Data Block 2 -->
<div class="relative pl-6 border-l border-white/10"> <div class="relative pl-6 border-l border-[var(--theme-border-primary)]">
<h3 class="font-mono text-xs text-slate-500 uppercase tracking-widest mb-4">Coordinates</h3> <h3 class="font-mono text-xs text-[var(--theme-text-muted)] uppercase tracking-widest mb-4">Coordinates</h3>
<p class="text-xl text-white font-light"> <p class="text-xl text-[var(--theme-text-primary)] font-light">
{contactContent.location}<br> {contactContent.location}<br>
<span class="text-slate-500 text-base">{contactContent.locationCountry}</span> <span class="text-[var(--theme-text-muted)] text-base">{contactContent.locationCountry}</span>
</p> </p>
<div class="mt-4 font-mono text-xs text-brand-accent"> <div class="mt-4 font-mono text-xs text-brand-accent">
{contactContent.coordinates} {contactContent.coordinates}
@ -151,14 +151,14 @@ const contactContent = contactEntry.data;
</div> </div>
<!-- Data Block 3 --> <!-- Data Block 3 -->
<div class="relative pl-6 border-l border-white/10"> <div class="relative pl-6 border-l border-[var(--theme-border-primary)]">
<h3 class="font-mono text-xs text-slate-500 uppercase tracking-widest mb-4">Social Feed</h3> <h3 class="font-mono text-xs text-[var(--theme-text-muted)] uppercase tracking-widest mb-4">Social Feed</h3>
<ul class="space-y-4"> <ul class="space-y-4">
{contactContent.socialLinks?.map((link) => ( {contactContent.socialLinks?.map((link) => (
<li> <li>
<a href={link.url} class="flex items-center gap-4 group"> <a href={link.url} class="flex items-center gap-4 group">
<span class="text-slate-400 group-hover:text-white transition-colors text-lg">{link.name}</span> <span class="text-[var(--theme-text-secondary)] group-hover:text-[var(--theme-text-primary)] transition-colors text-lg">{link.name}</span>
<svg class="w-4 h-4 text-slate-600 group-hover:text-brand-accent transition-colors transform group-hover:translate-x-1 group-hover:-translate-y-1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="7" y1="17" x2="17" y2="7"/><polyline points="7 7 17 7 17 17"/></svg> <svg class="w-4 h-4 text-[var(--theme-text-subtle)] group-hover:text-brand-accent transition-colors transform group-hover:translate-x-1 group-hover:-translate-y-1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="7" y1="17" x2="17" y2="7"/><polyline points="7 7 17 7 17 17"/></svg>
</a> </a>
</li> </li>
))} ))}
@ -174,7 +174,7 @@ const contactContent = contactEntry.data;
<div id="toast-container" class="fixed top-6 right-6 z-50 pointer-events-none"></div> <div id="toast-container" class="fixed top-6 right-6 z-50 pointer-events-none"></div>
<!-- Full-Screen Modal for Loading and Response --> <!-- Full-Screen Modal for Loading and Response -->
<div id="transmission-modal" class="fixed inset-0 z-[100] flex items-center justify-center bg-brand-dark/95 backdrop-blur-xl opacity-0 pointer-events-none transition-opacity duration-500"> <div id="transmission-modal" class="fixed inset-0 z-[100] flex items-center justify-center bg-[var(--theme-overlay-heavy)] backdrop-blur-xl opacity-0 pointer-events-none transition-opacity duration-500">
<!-- Loading State --> <!-- Loading State -->
<div id="loading-state" class="text-center"> <div id="loading-state" class="text-center">
@ -200,7 +200,7 @@ const contactContent = contactEntry.data;
<!-- Loading Text --> <!-- Loading Text -->
<div class="space-y-4"> <div class="space-y-4">
<h2 class="text-3xl md:text-5xl font-bold text-white uppercase tracking-tight"> <h2 class="text-3xl md:text-5xl font-bold text-[var(--theme-text-primary)] uppercase tracking-tight">
<span id="loading-text">Transmitting</span> <span id="loading-text">Transmitting</span>
</h2> </h2>
<div class="flex items-center justify-center gap-2"> <div class="flex items-center justify-center gap-2">
@ -208,15 +208,15 @@ const contactContent = contactEntry.data;
<div class="w-2 h-2 bg-brand-accent rounded-full animate-bounce delay-100"></div> <div class="w-2 h-2 bg-brand-accent rounded-full animate-bounce delay-100"></div>
<div class="w-2 h-2 bg-brand-accent rounded-full animate-bounce delay-200"></div> <div class="w-2 h-2 bg-brand-accent rounded-full animate-bounce delay-200"></div>
</div> </div>
<p class="font-mono text-sm text-slate-400 uppercase tracking-widest">Signal Processing</p> <p class="font-mono text-sm text-[var(--theme-text-secondary)] uppercase tracking-widest">Signal Processing</p>
</div> </div>
</div> </div>
<!-- Response State (hidden initially) --> <!-- Response State (hidden initially) -->
<div id="response-state" class="hidden w-full h-full absolute inset-0 z-10 flex flex-col items-center justify-center p-6 opacity-0 transition-all duration-700"> <div id="response-state" class="hidden w-full h-full absolute inset-0 z-10 flex flex-col items-center justify-center p-6 opacity-0 transition-all duration-700">
<!-- Close button --> <!-- Close button -->
<button id="close-modal" class="absolute top-8 right-8 z-50 p-3 border border-white/20 hover:border-brand-accent hover:bg-brand-accent/10 transition-all duration-300 group"> <button id="close-modal" class="absolute top-8 right-8 z-50 p-3 border border-[var(--theme-border-strong)] hover:border-brand-accent hover:bg-brand-accent/10 transition-all duration-300 group">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-white group-hover:text-brand-accent transition-colors"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-[var(--theme-text-primary)] group-hover:text-brand-accent transition-colors">
<line x1="18" y1="6" x2="6" y2="18"></line> <line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line> <line x1="6" y1="6" x2="18" y2="18"></line>
</svg> </svg>
@ -251,15 +251,15 @@ const contactContent = contactEntry.data;
</BaseLayout> </BaseLayout>
<style> <style>
/* Custom autofill styles to match dark theme */ /* Custom autofill styles to match theme */
input:-webkit-autofill, input:-webkit-autofill,
input:-webkit-autofill:hover, input:-webkit-autofill:hover,
input:-webkit-autofill:focus, input:-webkit-autofill:focus,
textarea:-webkit-autofill, textarea:-webkit-autofill,
textarea:-webkit-autofill:hover, textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus { textarea:-webkit-autofill:focus {
-webkit-text-fill-color: white; -webkit-text-fill-color: var(--theme-text-primary);
-webkit-box-shadow: 0 0 0px 1000px #0B0D11 inset; -webkit-box-shadow: 0 0 0px 1000px var(--theme-bg-primary) inset;
transition: background-color 5000s ease-in-out 0s; transition: background-color 5000s ease-in-out 0s;
} }
@ -267,13 +267,13 @@ const contactContent = contactEntry.data;
.label-active { .label-active {
top: -1.5rem !important; top: -1.5rem !important;
font-size: 0.75rem !important; font-size: 0.75rem !important;
color: #94A3B8 !important; color: var(--theme-text-secondary) !important;
} }
/* Dropdown open state */ /* Dropdown open state */
.dropdown-open #select-arrow { .dropdown-open #select-arrow {
transform: rotate(180deg); transform: rotate(180deg);
color: white; color: var(--theme-text-primary);
} }
/* Custom Animations */ /* Custom Animations */
@ -322,28 +322,29 @@ const contactContent = contactEntry.data;
} }
.custom-scrollbar::-webkit-scrollbar-track { .custom-scrollbar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05); background: var(--theme-hover-bg-strong);
} }
.custom-scrollbar::-webkit-scrollbar-thumb { .custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(0, 255, 255, 0.3); background: var(--color-brand-accent);
border-radius: 4px; border-radius: 4px;
opacity: 0.3;
} }
.custom-scrollbar::-webkit-scrollbar-thumb:hover { .custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(0, 255, 255, 0.5); opacity: 0.5;
} }
/* Response Content Prose Styles - Enhanced Readability */ /* Response Content Prose Styles - Enhanced Readability */
.prose-response { .prose-response {
color: white; color: var(--theme-text-primary);
text-align: left; text-align: left;
} }
.prose-response h1, .prose-response h1,
.prose-response h2, .prose-response h2,
.prose-response h3 { .prose-response h3 {
color: white; color: var(--theme-text-primary);
margin-top: 1.5em; margin-top: 1.5em;
margin-bottom: 0.75em; margin-bottom: 0.75em;
font-weight: 800; font-weight: 800;
@ -354,7 +355,7 @@ const contactContent = contactEntry.data;
.prose-response h1 { .prose-response h1 {
font-size: 3.5rem; font-size: 3.5rem;
background: linear-gradient(to right, #fff, #94a3b8); background: linear-gradient(to right, var(--theme-text-primary), var(--theme-text-secondary));
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
margin-bottom: 0.5em; margin-bottom: 0.5em;
@ -366,53 +367,53 @@ const contactContent = contactEntry.data;
.prose-response h3 { .prose-response h3 {
font-size: 1.75rem; font-size: 1.75rem;
color: #ff4d00; color: var(--color-brand-accent);
} }
.prose-response p { .prose-response p {
margin-bottom: 1.5em; margin-bottom: 1.5em;
line-height: 1.8; line-height: 1.8;
color: rgba(255, 255, 255, 0.9); color: var(--theme-text-secondary);
font-size: 1.5rem; /* Increased size significantly */ font-size: 1.5rem;
font-weight: 300; font-weight: 300;
max-width: 65ch; max-width: 65ch;
margin-right: auto; margin-right: auto;
} }
.prose-response strong { .prose-response strong {
color: #ff4d00; color: var(--color-brand-accent);
font-weight: 600; font-weight: 600;
} }
.prose-response em { .prose-response em {
font-style: italic; font-style: italic;
color: #94a3b8; color: var(--theme-text-muted);
} }
/* Blockquote for signature or special text */ /* Blockquote for signature or special text */
.prose-response blockquote { .prose-response blockquote {
border-left: none; /* Removed standard border */ border-left: none;
margin: 3em 0 1em; margin: 3em 0 1em;
padding: 0; padding: 0;
color: #ff4d00; color: var(--color-brand-accent);
font-family: 'Courier New', monospace; font-family: 'Courier New', monospace;
font-size: 1rem; font-size: 1rem;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.2em; letter-spacing: 0.2em;
display: inline-block; display: inline-block;
border-top: 1px solid rgba(255, 77, 0, 0.3); border-top: 1px solid rgba(221, 65, 50, 0.3);
padding-top: 2em; padding-top: 2em;
} }
.prose-response a { .prose-response a {
color: #ff4d00; color: var(--color-brand-accent);
text-decoration: underline; text-decoration: underline;
text-underline-offset: 4px; text-underline-offset: 4px;
transition: all 0.3s; transition: all 0.3s;
} }
.prose-response a:hover { .prose-response a:hover {
color: white; color: var(--theme-text-primary);
text-decoration-thickness: 2px; text-decoration-thickness: 2px;
} }
@ -461,14 +462,14 @@ const contactContent = contactEntry.data;
// Update UI // Update UI
selectValue.textContent = text; selectValue.textContent = text;
selectValue.classList.remove('text-transparent'); selectValue.classList.remove('text-transparent');
selectValue.classList.add('text-white'); selectValue.classList.add('text-[var(--theme-text-primary)]');
// Update Data // Update Data
hiddenInput.value = value; hiddenInput.value = value;
// Update Label Style // Update Label Style
selectLabel.classList.add('label-active'); selectLabel.classList.add('label-active');
selectLabel.classList.add('text-brand-accent'); // Highlight when selected selectLabel.classList.add('text-brand-accent');
closeDropdown(); closeDropdown();
}); });

View File

@ -109,33 +109,117 @@
} }
@utility text-stroke { @utility text-stroke {
-webkit-text-stroke: 1px rgba(255, 255, 255, 0.15); -webkit-text-stroke: 1px var(--theme-text-stroke);
color: transparent;
}
@utility text-stroke-dark {
-webkit-text-stroke: 1px var(--theme-text-stroke-inverted);
color: transparent; color: transparent;
} }
@utility skill-tag { @utility skill-tag {
@apply text-[10px] font-mono font-bold uppercase tracking-wider px-3 py-2 border border-slate-700 text-slate-400 hover:border-brand-accent hover:text-white transition-all duration-300 cursor-default select-none; @apply text-[10px] font-mono font-bold uppercase tracking-wider px-3 py-2 border border-[var(--theme-border-strong)] text-[var(--theme-text-secondary)] hover:border-brand-accent hover:text-[var(--theme-text-primary)] transition-all duration-300 cursor-default select-none;
} }
@utility btn-primary { @utility btn-primary {
@apply bg-brand-accent text-brand-dark px-8 py-4 text-xs font-bold uppercase tracking-widest hover:bg-white transition-all duration-300 inline-block; @apply bg-brand-accent text-brand-dark px-8 py-4 text-xs font-bold uppercase tracking-widest hover:bg-[var(--theme-text-primary)] hover:text-[var(--theme-bg-primary)] transition-all duration-300 inline-block;
} }
@utility btn-ghost { @utility btn-ghost {
@apply border border-slate-600 text-white px-8 py-4 text-xs font-bold uppercase tracking-widest hover:border-brand-accent hover:bg-brand-accent/5 transition-all duration-300 inline-block; @apply border border-[var(--theme-border-strong)] text-[var(--theme-text-primary)] px-8 py-4 text-xs font-bold uppercase tracking-widest hover:border-brand-accent hover:bg-brand-accent/5 transition-all duration-300 inline-block;
} }
@utility grid-overlay { @utility grid-overlay {
background-size: 100px 100px; background-size: 100px 100px;
background-image: linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px); background-image: linear-gradient(to right, var(--theme-grid-line) 1px, transparent 1px);
pointer-events: none; pointer-events: none;
z-index: 0; z-index: 0;
} }
/* ===== THEME SYSTEM ===== */
/* Dark mode (default) */
:root {
--vh-full: 100vh;
--vh-full: 100dvh;
/* Theme colors - Dark mode defaults */
--theme-bg-primary: #0B0D11;
--theme-bg-secondary: #151921;
--theme-bg-tertiary: #1E293B;
--theme-text-primary: #ffffff;
--theme-text-secondary: #94A3B8;
--theme-text-muted: #64748B;
--theme-text-subtle: #475569;
--theme-border-primary: rgba(255, 255, 255, 0.1);
--theme-border-secondary: rgba(255, 255, 255, 0.05);
--theme-border-strong: rgba(255, 255, 255, 0.2);
--theme-overlay: rgba(11, 13, 17, 0.8);
--theme-overlay-heavy: rgba(11, 13, 17, 0.98);
--theme-grid-line: rgba(255, 255, 255, 0.03);
--theme-hover-bg: rgba(255, 255, 255, 0.02);
--theme-hover-bg-strong: rgba(255, 255, 255, 0.05);
--theme-text-stroke: rgba(255, 255, 255, 0.15);
--theme-text-stroke-inverted: rgba(0, 0, 0, 0.15);
--theme-scrollbar-track: #0B0D11;
--theme-scrollbar-thumb: #334155;
--theme-code-bg: rgba(255, 77, 0, 0.1);
--theme-blockquote-bg: linear-gradient(135deg, rgba(255, 77, 0, 0.05), rgba(21, 25, 33, 0.8));
--theme-divider: rgba(255, 255, 255, 0.1);
--theme-decorative-opacity: 0.02;
--theme-card-overlay: rgba(11, 13, 17, 0.3);
--theme-card-gradient: rgba(11, 13, 17, 0.6);
--theme-hero-gradient-top: rgba(11, 13, 17, 0.8);
--theme-hero-gradient-side: rgba(11, 13, 17, 0.4);
}
/* Light mode */
[data-theme="light"] {
--theme-bg-primary: #FAFAFA;
--theme-bg-secondary: #F1F3F5;
--theme-bg-tertiary: #E9ECEF;
--theme-text-primary: #0B0D11;
--theme-text-secondary: #475569;
--theme-text-muted: #64748B;
--theme-text-subtle: #94A3B8;
--theme-border-primary: rgba(0, 0, 0, 0.1);
--theme-border-secondary: rgba(0, 0, 0, 0.05);
--theme-border-strong: rgba(0, 0, 0, 0.15);
--theme-overlay: rgba(250, 250, 250, 0.9);
--theme-overlay-heavy: rgba(250, 250, 250, 0.98);
--theme-grid-line: rgba(0, 0, 0, 0.04);
--theme-hover-bg: rgba(0, 0, 0, 0.02);
--theme-hover-bg-strong: rgba(0, 0, 0, 0.05);
--theme-text-stroke: rgba(0, 0, 0, 0.2);
--theme-text-stroke-inverted: rgba(255, 255, 255, 0.15);
--theme-scrollbar-track: #FAFAFA;
--theme-scrollbar-thumb: #CBD5E1;
--theme-code-bg: rgba(221, 65, 50, 0.08);
--theme-blockquote-bg: linear-gradient(135deg, rgba(221, 65, 50, 0.05), rgba(241, 243, 245, 0.9));
--theme-divider: rgba(0, 0, 0, 0.08);
--theme-decorative-opacity: 0.04;
--theme-card-overlay: rgba(250, 250, 250, 0.15);
--theme-card-gradient: rgba(250, 250, 250, 0.4);
--theme-hero-gradient-top: rgba(250, 250, 250, 0.5);
--theme-hero-gradient-side: rgba(250, 250, 250, 0.2);
}
/* Theme transition for smooth switching */
html.theme-transition,
html.theme-transition *,
html.theme-transition *::before,
html.theme-transition *::after {
transition: background-color 0.3s ease,
border-color 0.3s ease,
color 0.3s ease,
box-shadow 0.3s ease !important;
}
/* Base Styles */ /* Base Styles */
body { body {
background-color: var(--color-brand-dark); background-color: var(--theme-bg-primary);
color: #ffffff; color: var(--theme-text-primary);
overflow-x: hidden; overflow-x: hidden;
} }
@ -150,23 +234,17 @@ html {
} }
} }
/* Mobile viewport height fix - uses dvh with vh fallback */
:root {
--vh-full: 100vh;
--vh-full: 100dvh;
}
/* Custom Scrollbar */ /* Custom Scrollbar */
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 8px; width: 8px;
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
background: var(--color-brand-dark); background: var(--theme-scrollbar-track);
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
background: #334155; background: var(--theme-scrollbar-thumb);
transition: background 0.3s ease; transition: background 0.3s ease;
} }
@ -295,7 +373,7 @@ html {
.cursor-outline { .cursor-outline {
width: 40px; width: 40px;
height: 40px; height: 40px;
border: 1px solid rgba(255, 77, 0, 0.5); border: 1px solid rgba(221, 65, 50, 0.5);
z-index: 99999; z-index: 99999;
transition: width 0.3s cubic-bezier(0.16, 1, 0.3, 1), transition: width 0.3s cubic-bezier(0.16, 1, 0.3, 1),
height 0.3s cubic-bezier(0.16, 1, 0.3, 1), height 0.3s cubic-bezier(0.16, 1, 0.3, 1),
@ -315,7 +393,7 @@ a:hover~.cursor-outline,
button:hover~.cursor-outline { button:hover~.cursor-outline {
width: 60px; width: 60px;
height: 60px; height: 60px;
background-color: rgba(255, 77, 0, 0.05); background-color: rgba(221, 65, 50, 0.05);
border-color: var(--color-brand-accent); border-color: var(--color-brand-accent);
} }
@ -348,15 +426,15 @@ a {
.hover-border-glow:hover { .hover-border-glow:hover {
border-color: var(--color-brand-accent); border-color: var(--color-brand-accent);
box-shadow: 0 0 20px rgba(255, 77, 0, 0.1); box-shadow: 0 0 20px rgba(221, 65, 50, 0.1);
} }
/* Gradient divider */ /* Gradient divider */
.divider-gradient { .divider-gradient {
background: linear-gradient(to right, background: linear-gradient(to right,
transparent, transparent,
rgba(255, 255, 255, 0.1) 20%, var(--theme-divider) 20%,
rgba(255, 255, 255, 0.1) 80%, var(--theme-divider) 80%,
transparent); transparent);
} }
@ -364,19 +442,19 @@ a {
.divider-accent { .divider-accent {
background: linear-gradient(to right, background: linear-gradient(to right,
transparent, transparent,
rgba(255, 77, 0, 0.2) 50%, rgba(221, 65, 50, 0.2) 50%,
transparent); transparent);
} }
/* ===== PROSE / MARKDOWN STYLES ===== */ /* ===== PROSE / MARKDOWN STYLES ===== */
.prose-custom { .prose-custom {
color: #94A3B8; color: var(--theme-text-secondary);
line-height: 1.8; line-height: 1.8;
font-size: 1.0625rem; font-size: 1.0625rem;
} }
.prose-custom h2 { .prose-custom h2 {
color: #ffffff; color: var(--theme-text-primary);
font-size: 1.75rem; font-size: 1.75rem;
font-weight: 700; font-weight: 700;
text-transform: uppercase; text-transform: uppercase;
@ -384,7 +462,7 @@ a {
margin-top: 3.5rem; margin-top: 3.5rem;
margin-bottom: 1.25rem; margin-bottom: 1.25rem;
padding-bottom: 0.75rem; padding-bottom: 0.75rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1); border-bottom: 1px solid var(--theme-border-primary);
position: relative; position: relative;
scroll-margin-top: 6rem; scroll-margin-top: 6rem;
} }
@ -398,7 +476,7 @@ a {
} }
.prose-custom h3 { .prose-custom h3 {
color: #ffffff; color: var(--theme-text-primary);
font-size: 1.25rem; font-size: 1.25rem;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
@ -409,7 +487,7 @@ a {
} }
.prose-custom h4 { .prose-custom h4 {
color: #ffffff; color: var(--theme-text-primary);
font-size: 1.1rem; font-size: 1.1rem;
font-weight: 600; font-weight: 600;
margin-top: 1.5rem; margin-top: 1.5rem;
@ -429,17 +507,17 @@ a {
} }
.prose-custom a:hover { .prose-custom a:hover {
color: #ffffff; color: var(--theme-text-primary);
border-bottom-color: var(--color-brand-accent); border-bottom-color: var(--color-brand-accent);
} }
.prose-custom strong { .prose-custom strong {
color: #ffffff; color: var(--theme-text-primary);
font-weight: 600; font-weight: 600;
} }
.prose-custom em { .prose-custom em {
color: #CBD5E1; color: var(--theme-text-secondary);
font-style: italic; font-style: italic;
} }
@ -492,14 +570,14 @@ a {
.prose-custom blockquote { .prose-custom blockquote {
position: relative; position: relative;
border-left: 3px solid var(--color-brand-accent); border-left: 3px solid var(--color-brand-accent);
background: linear-gradient(135deg, rgba(255, 77, 0, 0.05), rgba(21, 25, 33, 0.8)); background: var(--theme-blockquote-bg);
padding: 1.5rem 1.5rem 1.5rem 2rem; padding: 1.5rem 1.5rem 1.5rem 2rem;
margin: 2.5rem 0; margin: 2.5rem 0;
font-style: italic; font-style: italic;
color: #CBD5E1; color: var(--theme-text-secondary);
border-right: 1px solid rgba(255, 255, 255, 0.05); border-right: 1px solid var(--theme-border-secondary);
border-top: 1px solid rgba(255, 255, 255, 0.05); border-top: 1px solid var(--theme-border-secondary);
border-bottom: 1px solid rgba(255, 255, 255, 0.05); border-bottom: 1px solid var(--theme-border-secondary);
} }
.prose-custom blockquote::before { .prose-custom blockquote::before {
@ -507,7 +585,7 @@ a {
position: absolute; position: absolute;
top: -0.75rem; top: -0.75rem;
left: 1rem; left: 1rem;
background: var(--color-brand-dark); background: var(--theme-bg-primary);
padding: 0 0.5rem; padding: 0 0.5rem;
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: 0.625rem; font-size: 0.625rem;
@ -528,19 +606,19 @@ a {
/* Enhanced Code - Inline */ /* Enhanced Code - Inline */
.prose-custom code { .prose-custom code {
color: var(--color-brand-accent); color: var(--color-brand-accent);
background-color: rgba(255, 77, 0, 0.1); background-color: var(--theme-code-bg);
padding: 0.2rem 0.5rem; padding: 0.2rem 0.5rem;
border-radius: 0; border-radius: 0;
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: 0.85em; font-size: 0.85em;
border: 1px solid rgba(255, 77, 0, 0.2); border: 1px solid rgba(221, 65, 50, 0.2);
} }
/* Enhanced Code Blocks - Terminal Style */ /* Enhanced Code Blocks - Terminal Style */
.prose-custom pre { .prose-custom pre {
position: relative; position: relative;
background-color: var(--color-brand-panel); background-color: var(--theme-bg-secondary);
border: 1px solid rgba(255, 255, 255, 0.1); border: 1px solid var(--theme-border-primary);
padding: 0; padding: 0;
margin: 2.5rem 0; margin: 2.5rem 0;
overflow: hidden; overflow: hidden;
@ -549,14 +627,14 @@ a {
.prose-custom pre::before { .prose-custom pre::before {
content: "TERMINAL"; content: "TERMINAL";
display: block; display: block;
background: rgba(255, 255, 255, 0.03); background: var(--theme-hover-bg);
border-bottom: 1px solid rgba(255, 255, 255, 0.1); border-bottom: 1px solid var(--theme-border-primary);
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: 0.625rem; font-size: 0.625rem;
font-weight: 700; font-weight: 700;
letter-spacing: 0.15em; letter-spacing: 0.15em;
color: #64748B; color: var(--theme-text-muted);
text-transform: uppercase; text-transform: uppercase;
} }
@ -564,7 +642,7 @@ a {
display: block; display: block;
background: none; background: none;
padding: 1.5rem; padding: 1.5rem;
color: #CBD5E1; color: var(--theme-text-secondary);
border: none; border: none;
overflow-x: auto; overflow-x: auto;
} }
@ -585,25 +663,25 @@ a {
content: ""; content: "";
flex: 1; flex: 1;
height: 1px; height: 1px;
background: linear-gradient(to right, transparent, rgba(255, 77, 0, 0.3)); background: linear-gradient(to right, transparent, rgba(221, 65, 50, 0.3));
} }
.prose-custom hr::after { .prose-custom hr::after {
content: ""; content: "";
flex: 1; flex: 1;
height: 1px; height: 1px;
background: linear-gradient(to left, transparent, rgba(255, 77, 0, 0.3)); background: linear-gradient(to left, transparent, rgba(221, 65, 50, 0.3));
} }
/* Enhanced Images */ /* Enhanced Images */
.prose-custom img { .prose-custom img {
border: 1px solid rgba(255, 255, 255, 0.1); border: 1px solid var(--theme-border-primary);
margin: 2.5rem 0; margin: 2.5rem 0;
transition: border-color 0.3s ease; transition: border-color 0.3s ease;
} }
.prose-custom img:hover { .prose-custom img:hover {
border-color: rgba(255, 77, 0, 0.3); border-color: rgba(221, 65, 50, 0.3);
} }
/* Image Captions (for figures) */ /* Image Captions (for figures) */
@ -620,7 +698,7 @@ a {
font-size: 0.6875rem; font-size: 0.6875rem;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.1em; letter-spacing: 0.1em;
color: #64748B; color: var(--theme-text-muted);
margin-top: 0.75rem; margin-top: 0.75rem;
padding-left: 0.5rem; padding-left: 0.5rem;
border-left: 2px solid var(--color-brand-accent); border-left: 2px solid var(--color-brand-accent);
@ -634,7 +712,7 @@ a {
.prose-custom .video-container video { .prose-custom .video-container video {
width: 100%; width: 100%;
border: 1px solid rgba(255, 255, 255, 0.1); border: 1px solid var(--theme-border-primary);
} }
.prose-custom .video-container p { .prose-custom .video-container p {
@ -642,7 +720,7 @@ a {
font-size: 0.6875rem; font-size: 0.6875rem;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.1em; letter-spacing: 0.1em;
color: #64748B; color: var(--theme-text-muted);
margin-top: 0.75rem; margin-top: 0.75rem;
margin-bottom: 0; margin-bottom: 0;
} }
@ -656,8 +734,8 @@ a {
} }
.prose-custom thead { .prose-custom thead {
background: rgba(255, 255, 255, 0.03); background: var(--theme-hover-bg);
border-bottom: 1px solid rgba(255, 255, 255, 0.1); border-bottom: 1px solid var(--theme-border-primary);
} }
.prose-custom th { .prose-custom th {
@ -666,17 +744,54 @@ a {
font-weight: 700; font-weight: 700;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.1em; letter-spacing: 0.1em;
color: #64748B; color: var(--theme-text-muted);
padding: 1rem; padding: 1rem;
text-align: left; text-align: left;
} }
.prose-custom td { .prose-custom td {
padding: 1rem; padding: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.05); border-bottom: 1px solid var(--theme-border-secondary);
color: #94A3B8; color: var(--theme-text-secondary);
} }
.prose-custom tr:hover td { .prose-custom tr:hover td {
background: rgba(255, 255, 255, 0.02); background: var(--theme-hover-bg);
}
/* ===== THEME-AWARE UTILITY CLASSES ===== */
/* Background colors */
.bg-theme-primary {
background-color: var(--theme-bg-primary);
}
.bg-theme-secondary {
background-color: var(--theme-bg-secondary);
}
.bg-theme-tertiary {
background-color: var(--theme-bg-tertiary);
}
/* Text colors */
.text-theme-primary {
color: var(--theme-text-primary);
}
.text-theme-secondary {
color: var(--theme-text-secondary);
}
.text-theme-muted {
color: var(--theme-text-muted);
}
/* Border colors */
.border-theme-primary {
border-color: var(--theme-border-primary);
}
.border-theme-secondary {
border-color: var(--theme-border-secondary);
} }