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:
parent
dc215c89f4
commit
2ca66ccc6d
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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>© {today.getFullYear()} Nicholai Vogel</span>
|
<span>© {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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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">
|
||||||
< PREV_FILE
|
< 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 >
|
NEXT_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">
|
||||||
{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>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
87
src/components/ThemeToggle.astro
Normal file
87
src/components/ThemeToggle.astro
Normal 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>
|
||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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"><</span>
|
<span class="text-brand-accent group-hover:-translate-x-1 transition-transform duration-300"><</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"><</span>
|
<span class="font-mono transition-transform duration-300 group-hover:-translate-x-1"><</span>
|
||||||
ACCESS_FULL_ARCHIVE
|
ACCESS_FULL_ARCHIVE
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -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"><</span>
|
<span class="text-brand-accent group-hover:-translate-x-1 transition-transform duration-300"><</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>
|
||||||
|
|||||||
@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user