Update site content and layout for personal branding
- Changed site title and description to reflect personal branding as a VFX Artist & Technical Generalist. - Added social links for contact and networking. - Revamped footer layout for improved aesthetics and functionality. - Reorganized blog and homepage layouts to enhance user experience and visual appeal. - Updated global styles to incorporate Tailwind CSS for a modern design approach.
This commit is contained in:
parent
ab075a7c91
commit
c3bbf19862
@ -30,9 +30,15 @@ const { title, description, image = FallbackImage } = Astro.props;
|
||||
/>
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
||||
<!-- Font preloads -->
|
||||
<link rel="preload" href="/fonts/atkinson-regular.woff" as="font" type="font/woff" crossorigin />
|
||||
<link rel="preload" href="/fonts/atkinson-bold.woff" as="font" type="font/woff" crossorigin />
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&family=Space+Mono:wght@400;700&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
<!-- Icons -->
|
||||
<script src="https://unpkg.com/lucide@latest"></script>
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
|
||||
43
src/components/CustomCursor.tsx
Normal file
43
src/components/CustomCursor.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
|
||||
const CustomCursor = () => {
|
||||
const dotRef = useRef<HTMLDivElement>(null);
|
||||
const outlineRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const dot = dotRef.current;
|
||||
const outline = outlineRef.current;
|
||||
|
||||
if (!dot || !outline) return;
|
||||
|
||||
const onMouseMove = (e: MouseEvent) => {
|
||||
const posX = e.clientX;
|
||||
const posY = e.clientY;
|
||||
|
||||
// Dot follows instantly
|
||||
dot.style.left = `${posX}px`;
|
||||
dot.style.top = `${posY}px`;
|
||||
|
||||
// Outline follows with animation
|
||||
outline.animate({
|
||||
left: `${posX}px`,
|
||||
top: `${posY}px`
|
||||
}, { duration: 500, fill: "forwards" });
|
||||
};
|
||||
|
||||
window.addEventListener('mousemove', onMouseMove);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', onMouseMove);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={dotRef} className="cursor-dot hidden md:block"></div>
|
||||
<div ref={outlineRef} className="cursor-outline hidden md:block"></div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomCursor;
|
||||
@ -2,61 +2,44 @@
|
||||
const today = new Date();
|
||||
---
|
||||
|
||||
<footer>
|
||||
© {today.getFullYear()} Your name here. All rights reserved.
|
||||
<div class="social-links">
|
||||
<a href="https://m.webtoo.ls/@astro" target="_blank">
|
||||
<span class="sr-only">Follow Astro on Mastodon</span>
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
aria-hidden="true"
|
||||
width="32"
|
||||
height="32"
|
||||
astro-icon="social/mastodon"
|
||||
><path
|
||||
fill="currentColor"
|
||||
d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z"
|
||||
></path></svg
|
||||
>
|
||||
</a>
|
||||
<a href="https://twitter.com/astrodotbuild" target="_blank">
|
||||
<span class="sr-only">Follow Astro on Twitter</span>
|
||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32" astro-icon="social/twitter"
|
||||
><path
|
||||
fill="currentColor"
|
||||
d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"
|
||||
></path></svg
|
||||
>
|
||||
</a>
|
||||
<a href="https://github.com/withastro/astro" target="_blank">
|
||||
<span class="sr-only">Go to Astro's GitHub repo</span>
|
||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32" astro-icon="social/github"
|
||||
><path
|
||||
fill="currentColor"
|
||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
||||
></path></svg
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
<footer class="container mx-auto px-6 lg:px-12 py-32 relative overflow-hidden">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-12 items-end">
|
||||
<div>
|
||||
<h2
|
||||
class="text-6xl md:text-8xl font-bold uppercase leading-none tracking-tighter mb-8 text-white group cursor-pointer">
|
||||
Let's<br>
|
||||
<span
|
||||
class="text-stroke group-hover:text-brand-accent transition-colors duration-300">Build</span><br>
|
||||
Reality.
|
||||
</h2>
|
||||
<div class="flex flex-wrap gap-6 mt-12">
|
||||
<a href="mailto:nicholai@nicholai.work" class="btn-primary">nicholai@nicholai.work</a>
|
||||
<a href="tel:7196604281" class="btn-ghost">719 660 4281</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md:text-right">
|
||||
<div class="mb-12">
|
||||
<p class="text-xs font-bold uppercase text-slate-500 mb-4 tracking-widest">Social Uplink</p>
|
||||
<ul class="space-y-2">
|
||||
<li><a href="https://nicholai.work"
|
||||
class="text-white hover:text-brand-accent text-lg font-mono">nicholai.work</a></li>
|
||||
<li><a href="#"
|
||||
class="text-white hover:text-brand-accent text-lg font-mono">@nicholai.exe</a></li>
|
||||
<li><a href="#" class="text-white hover:text-brand-accent text-lg font-mono">LinkedIn</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex justify-end items-end gap-2 text-[10px] text-slate-600 font-mono uppercase">
|
||||
<span>© {today.getFullYear()} Nicholai Vogel</span>
|
||||
<span>/</span>
|
||||
<span>V7 SYSTEM</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Decorative huge text bg -->
|
||||
<div class="absolute -bottom-10 left-0 w-full text-center pointer-events-none opacity-5">
|
||||
<span class="text-[15rem] font-bold text-white uppercase leading-none whitespace-nowrap">VOGEL</span>
|
||||
</div>
|
||||
</footer>
|
||||
<style>
|
||||
footer {
|
||||
padding: 2em 1em 6em 1em;
|
||||
background: linear-gradient(var(--gray-gradient)) no-repeat;
|
||||
color: rgb(var(--gray));
|
||||
text-align: center;
|
||||
}
|
||||
.social-links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
.social-links a {
|
||||
text-decoration: none;
|
||||
color: rgb(var(--gray));
|
||||
}
|
||||
.social-links a:hover {
|
||||
color: rgb(var(--gray-dark));
|
||||
}
|
||||
</style>
|
||||
|
||||
21
src/components/GridOverlay.astro
Normal file
21
src/components/GridOverlay.astro
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
---
|
||||
<!-- Fixed Grid Overlay -->
|
||||
<div class="fixed inset-0 grid-overlay h-screen w-screen"></div>
|
||||
|
||||
<!-- 12 Column Guide (Visual Only - Low Opacity) -->
|
||||
<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">
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
<div class="border-r border-white/5 h-full hidden md:block"></div>
|
||||
</div>
|
||||
|
||||
39
src/components/Navigation.astro
Normal file
39
src/components/Navigation.astro
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
---
|
||||
<nav
|
||||
class="fixed top-0 left-0 w-full z-50 px-6 lg:px-12 py-8 flex justify-between items-center backdrop-blur-sm border-b border-white/5">
|
||||
<div class="flex items-center gap-4 group cursor-none-target">
|
||||
<div
|
||||
class="w-10 h-10 border border-brand-accent/50 flex items-center justify-center bg-brand-accent/5 group-hover:bg-brand-accent transition-colors duration-300">
|
||||
<span class="font-bold text-brand-accent group-hover:text-brand-dark">NV</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xs font-mono uppercase tracking-widest text-slate-400">Status</span>
|
||||
<span class="text-xs font-bold text-brand-accent animate-pulse">AVAILABLE FOR WORK</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hidden md:flex gap-12">
|
||||
<a href="/#about"
|
||||
class="text-xs font-bold uppercase tracking-widest text-slate-500 hover:text-white transition-colors">01.
|
||||
Profile</a>
|
||||
<a href="/#work"
|
||||
class="text-xs font-bold uppercase tracking-widest text-slate-500 hover:text-white transition-colors">02.
|
||||
Selected Works</a>
|
||||
<a href="/#experience"
|
||||
class="text-xs font-bold uppercase tracking-widest text-slate-500 hover:text-white transition-colors">03.
|
||||
History</a>
|
||||
<a href="/#skills"
|
||||
class="text-xs font-bold uppercase tracking-widest text-slate-500 hover:text-white transition-colors">04.
|
||||
Stack</a>
|
||||
<a href="/blog"
|
||||
class="text-xs font-bold uppercase tracking-widest text-slate-500 hover:text-white transition-colors">05.
|
||||
Blog</a>
|
||||
</div>
|
||||
|
||||
<a href="mailto:nicholai@nicholai.work"
|
||||
class="border border-slate-600 px-6 py-3 text-xs font-bold uppercase tracking-widest hover:border-brand-accent hover:bg-brand-accent/10 transition-all cursor-none-target">
|
||||
Contact
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
99
src/components/sections/Experience.astro
Normal file
99
src/components/sections/Experience.astro
Normal file
@ -0,0 +1,99 @@
|
||||
---
|
||||
---
|
||||
<section id="experience" class="container mx-auto px-6 lg:px-12 py-24">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-12">
|
||||
<div class="lg:col-span-4">
|
||||
<h2 class="text-4xl font-bold uppercase tracking-tight mb-2 text-stroke">Experience</h2>
|
||||
<h2 class="text-4xl font-bold uppercase tracking-tight mb-8">History</h2>
|
||||
<p class="text-slate-400 mb-8 max-w-sm">
|
||||
From founding my own VFX house to supervising global campaigns. I bridge the gap between
|
||||
creative vision and technical execution.
|
||||
</p>
|
||||
<a href="https://biohazardvfx.com" target="_blank"
|
||||
class="inline-flex items-center gap-2 text-xs font-mono uppercase text-brand-accent hover:underline">
|
||||
Visit Biohazard VFX <i data-lucide="arrow-up-right" class="w-4 h-4"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-8 relative">
|
||||
<!-- Vertical line -->
|
||||
<div
|
||||
class="absolute left-0 top-0 bottom-0 w-[1px] bg-gradient-to-b from-brand-accent via-slate-800 to-transparent">
|
||||
</div>
|
||||
|
||||
<!-- Item 1 -->
|
||||
<div class="pl-12 mb-20 relative reveal-text">
|
||||
<div
|
||||
class="absolute left-[-5px] top-2 w-2.5 h-2.5 bg-brand-dark border border-brand-accent rounded-full">
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-row md:items-baseline gap-4 mb-4">
|
||||
<h3 class="text-2xl font-bold text-white uppercase">Biohazard VFX</h3>
|
||||
<span class="font-mono text-xs text-brand-accent bg-brand-accent/10 px-2 py-1">FOUNDER / VFX
|
||||
SUPERVISOR</span>
|
||||
<span class="font-mono text-xs text-slate-500 ml-auto">MAR 2022 - OCT 2025</span>
|
||||
</div>
|
||||
<p class="text-slate-400 mb-6 leading-relaxed">
|
||||
Founded and led a VFX studio specializing in high-end commercial and music video work for
|
||||
Post Malone, ENHYPEN, and Nike. Architected a custom pipeline combining cloud and
|
||||
self-hosted infrastructure.
|
||||
</p>
|
||||
<ul class="space-y-2 mb-6">
|
||||
<li class="flex items-start gap-3 text-sm text-slate-300">
|
||||
<span class="text-brand-accent mt-1">▹</span>
|
||||
Designed 7-plate reconciliation workflows for ENHYPEN (projection mapping live action
|
||||
onto CAD).
|
||||
</li>
|
||||
<li class="flex items-start gap-3 text-sm text-slate-300">
|
||||
<span class="text-brand-accent mt-1">▹</span>
|
||||
Developed QA systems for AI-generated assets, transforming mid-tier output into
|
||||
production-ready deliverables.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Item 2 -->
|
||||
<div class="pl-12 mb-20 relative reveal-text">
|
||||
<div class="absolute left-[-5px] top-2 w-2.5 h-2.5 bg-slate-700 rounded-full"></div>
|
||||
<div class="flex flex-col md:flex-row md:items-baseline gap-4 mb-4">
|
||||
<h3 class="text-2xl font-bold text-white uppercase">Stinkfilms</h3>
|
||||
<span class="font-mono text-xs text-slate-400 border border-slate-700 px-2 py-1">GLOBAL
|
||||
PRODUCTION STUDIO</span>
|
||||
<span class="font-mono text-xs text-slate-500 ml-auto">SUMMER 2024</span>
|
||||
</div>
|
||||
<p class="text-slate-400 mb-6 leading-relaxed">
|
||||
Led Biohazard VFX team (60+ artists) alongside director Felix Brady to create a brand film
|
||||
for G-Star Raw.
|
||||
</p>
|
||||
<div
|
||||
class="border border-white/5 bg-white/5 p-6 backdrop-blur-sm hover:border-brand-accent/50 transition-colors cursor-pointer group">
|
||||
<h4 class="text-sm font-bold uppercase text-white mb-2 flex justify-between">
|
||||
Project: G-Star Raw Olympics Campaign
|
||||
<i data-lucide="arrow-right"
|
||||
class="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity text-brand-accent"></i>
|
||||
</h4>
|
||||
<p class="text-xs text-slate-400 mb-4">Managed full CG environments in Blender/Houdini and
|
||||
integrated AI/ML workflows (Stable Diffusion reference gen, Copycat cleanup).</p>
|
||||
<a href="https://stinkfilms.com"
|
||||
class="text-[10px] font-bold text-brand-accent uppercase tracking-widest">View Case
|
||||
Study</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Item 3 -->
|
||||
<div class="pl-12 relative reveal-text">
|
||||
<div class="absolute left-[-5px] top-2 w-2.5 h-2.5 bg-slate-700 rounded-full"></div>
|
||||
<div class="flex flex-col md:flex-row md:items-baseline gap-4 mb-4">
|
||||
<h3 class="text-2xl font-bold text-white uppercase">Freelance</h3>
|
||||
<span class="font-mono text-xs text-slate-400 border border-slate-700 px-2 py-1">2D/3D
|
||||
ARTIST</span>
|
||||
<span class="font-mono text-xs text-slate-500 ml-auto">2015 - PRESENT</span>
|
||||
</div>
|
||||
<p class="text-slate-400 mb-4 leading-relaxed">
|
||||
Compositor for Abyss Digital and major labels (Atlantic, Interscope). Clients: David
|
||||
Kushner, Opium, Lil Durk, Don Toliver.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
55
src/components/sections/FeaturedProject.astro
Normal file
55
src/components/sections/FeaturedProject.astro
Normal file
@ -0,0 +1,55 @@
|
||||
---
|
||||
---
|
||||
<section id="work" class="py-24">
|
||||
<div class="container mx-auto px-6 lg:px-12 mb-12">
|
||||
<span class="text-xs font-mono text-brand-accent mb-2 block">/// HIGHLIGHT</span>
|
||||
<h2 class="text-5xl md:text-7xl font-bold uppercase text-white mb-4">G-Star Raw <span
|
||||
class="text-stroke">Olympics</span></h2>
|
||||
</div>
|
||||
|
||||
<!-- Full Width Project Card -->
|
||||
<div class="w-full h-[80vh] relative group overflow-hidden border-y border-white/10">
|
||||
<!-- Abstract Background representing the project -->
|
||||
<div
|
||||
class="absolute inset-0 bg-[url('https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=2564&auto=format&fit=crop')] bg-cover bg-center transition-transform duration-1000 group-hover:scale-105">
|
||||
</div>
|
||||
<div class="absolute inset-0 bg-brand-dark/80 mix-blend-multiply"></div>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-brand-dark via-brand-dark/20 to-transparent"></div>
|
||||
|
||||
<!-- Grid Overlay on image -->
|
||||
<div
|
||||
class="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdHRlcm4gaWQ9ImIiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGNpcmNsZSBjeD0iMiIgY3k9IjIiIHI9IjEiIGZpbGw9InJnYmEoMjU1LDI1NSwyNTUsMC4xKSIvPjwvcGF0dGVybj48cmVjdCB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIGZpbGw9InVybCgjYikiLz48L3BhdHRlcm4+PC9kZWZzPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjYSkiLz48L3N2Zz4=')] opacity-30">
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="absolute bottom-0 left-0 w-full p-6 lg:p-12 flex flex-col md:flex-row items-end justify-between">
|
||||
<div
|
||||
class="max-w-2xl transform translate-y-8 group-hover:translate-y-0 transition-transform duration-500">
|
||||
<div class="flex gap-2 mb-4">
|
||||
<span class="bg-brand-accent text-brand-dark text-[10px] font-bold uppercase px-2 py-1">VFX
|
||||
Supervision</span>
|
||||
<span
|
||||
class="border border-white/30 text-white text-[10px] font-bold uppercase px-2 py-1">AI/ML</span>
|
||||
<span
|
||||
class="border border-white/30 text-white text-[10px] font-bold uppercase px-2 py-1">Houdini</span>
|
||||
</div>
|
||||
<p class="text-xl md:text-2xl text-white font-medium mb-6">
|
||||
Managed full CG environment builds, procedural city generation, and integrated AI-generated
|
||||
normal maps for relighting in Nuke.
|
||||
</p>
|
||||
<a href="https://f.io/7ijf23Wm"
|
||||
class="inline-flex items-center gap-3 text-sm font-bold uppercase tracking-widest text-white hover:text-brand-accent transition-colors">
|
||||
Watch Making Of <i data-lucide="play-circle" class="w-5 h-5"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="hidden md:block text-right">
|
||||
<span class="block text-[10px] uppercase text-slate-500 tracking-widest mb-1">Year</span>
|
||||
<span class="block text-2xl font-bold text-white mb-4">2024</span>
|
||||
<span class="block text-[10px] uppercase text-slate-500 tracking-widest mb-1">Client</span>
|
||||
<span class="block text-xl font-bold text-white">Stinkfilms</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
60
src/components/sections/Hero.astro
Normal file
60
src/components/sections/Hero.astro
Normal file
@ -0,0 +1,60 @@
|
||||
---
|
||||
---
|
||||
<section id="about" class="container mx-auto px-6 lg:px-12 min-h-[70vh] flex flex-col justify-center relative">
|
||||
<div class="grid grid-cols-1 md:grid-cols-12 gap-6">
|
||||
<div class="col-span-12">
|
||||
<p class="font-mono text-brand-accent mb-4 ml-1 reveal-text">/// TECHNICAL GENERALIST & VFX
|
||||
SUPERVISOR</p>
|
||||
<h1
|
||||
class="text-6xl md:text-8xl lg:text-[10rem] font-bold text-massive uppercase leading-none tracking-tighter mb-8 text-white">
|
||||
<span class="reveal-text block delay-100">Visual</span>
|
||||
<span
|
||||
class="reveal-text block delay-200 text-transparent bg-clip-text bg-gradient-to-tr from-brand-accent to-white">Alchemist</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="col-span-12 md:col-span-6 lg:col-span-5 lg:col-start-8 mt-12 border-l border-brand-accent/30 pl-8 reveal-text delay-300">
|
||||
<p class="text-slate-400 text-lg leading-relaxed mb-8">
|
||||
I am a problem solver who loves visual effects. With 10 years of experience creating end-to-end
|
||||
visual content for clients like <span class="text-white font-semibold">Post Malone</span>, <span
|
||||
class="text-white font-semibold">Stinkfilms</span>, and <span
|
||||
class="text-white font-semibold">Adidas</span>. Comfortable managing teams while staying
|
||||
knee-deep in hands-on shot work.
|
||||
</p>
|
||||
<div class="flex gap-4">
|
||||
<a href="#work"
|
||||
class="group flex items-center gap-3 text-xs font-bold uppercase tracking-widest text-white hover:text-brand-accent transition-colors">
|
||||
<span class="w-8 h-[1px] bg-brand-accent group-hover:w-12 transition-all"></span>
|
||||
View Selected Works
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hero Footer -->
|
||||
<div
|
||||
class="absolute bottom-0 w-full left-0 px-6 lg:px-12 pb-12 hidden md:flex justify-between items-end opacity-50">
|
||||
<div class="font-mono text-xs text-slate-500">
|
||||
LOC: COLORADO SPRINGS<br>
|
||||
TIME: <span id="clock">00:00:00</span>
|
||||
</div>
|
||||
<div class="font-mono text-xs text-slate-500 text-right">
|
||||
SCROLL<br>
|
||||
↓
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
function updateClock() {
|
||||
const clock = document.getElementById('clock');
|
||||
if (clock) {
|
||||
const now = new Date();
|
||||
const timeString = now.toLocaleTimeString('en-US', {hour12: false, timeZone: 'America/Denver'});
|
||||
clock.textContent = timeString + " MST";
|
||||
}
|
||||
}
|
||||
setInterval(updateClock, 1000);
|
||||
updateClock();
|
||||
</script>
|
||||
|
||||
73
src/components/sections/Skills.astro
Normal file
73
src/components/sections/Skills.astro
Normal file
@ -0,0 +1,73 @@
|
||||
---
|
||||
---
|
||||
<section id="skills" class="container mx-auto px-6 lg:px-12 py-24 bg-brand-panel border-y border-white/5">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-12">
|
||||
<div class="col-span-1 md:col-span-2 lg:col-span-4 mb-8">
|
||||
<h2 class="text-4xl font-bold uppercase mb-2">Technical Arsenal</h2>
|
||||
<p class="text-slate-400 font-mono text-sm">/// SOFTWARE & LANGUAGES</p>
|
||||
</div>
|
||||
|
||||
<!-- Compositing -->
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-white uppercase mb-6 flex items-center gap-2">
|
||||
<i data-lucide="layers" class="w-4 h-4 text-brand-accent"></i> Compositing
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="skill-tag">Nuke/NukeX</span>
|
||||
<span class="skill-tag">ComfyUI</span>
|
||||
<span class="skill-tag">After Effects</span>
|
||||
<span class="skill-tag">Photoshop</span>
|
||||
<span class="skill-tag">Deep Compositing</span>
|
||||
<span class="skill-tag">Live Action VFX</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3D -->
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-white uppercase mb-6 flex items-center gap-2">
|
||||
<i data-lucide="box" class="w-4 h-4 text-brand-accent"></i> 3D Generalist
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="skill-tag">Houdini</span>
|
||||
<span class="skill-tag">Blender</span>
|
||||
<span class="skill-tag">Maya</span>
|
||||
<span class="skill-tag">USD</span>
|
||||
<span class="skill-tag">Solaris/Karma</span>
|
||||
<span class="skill-tag">Unreal Engine</span>
|
||||
<span class="skill-tag">Substance</span>
|
||||
<span class="skill-tag">Procedural Gen</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI/ML -->
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-white uppercase mb-6 flex items-center gap-2">
|
||||
<i data-lucide="cpu" class="w-4 h-4 text-brand-accent"></i> AI/ML Integration
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="skill-tag bg-brand-accent/10 border-brand-accent/50 text-brand-accent">Stable
|
||||
Diffusion</span>
|
||||
<span class="skill-tag">LoRA Training</span>
|
||||
<span class="skill-tag">Dataset Prep</span>
|
||||
<span class="skill-tag">Synthetic Data</span>
|
||||
<span class="skill-tag">Prompt Engineering</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dev -->
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-white uppercase mb-6 flex items-center gap-2">
|
||||
<i data-lucide="code" class="w-4 h-4 text-brand-accent"></i> Development
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="skill-tag">Python</span>
|
||||
<span class="skill-tag">JavaScript</span>
|
||||
<span class="skill-tag">React</span>
|
||||
<span class="skill-tag">Docker</span>
|
||||
<span class="skill-tag">Linux</span>
|
||||
<span class="skill-tag">Pipeline Dev</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
// Place any global data in this file.
|
||||
// You can import this data from anywhere in your site by using the `import` keyword.
|
||||
|
||||
export const SITE_TITLE = 'Astro Blog';
|
||||
export const SITE_DESCRIPTION = 'Welcome to my website!';
|
||||
export const SITE_TITLE = 'Nicholai Vogel | VFX Artist & Technical Generalist';
|
||||
export const SITE_DESCRIPTION = 'VFX Artist & Technical Generalist specializing in Houdini, Nuke, and AI/ML integration. Founder of Biohazard VFX.';
|
||||
export const SOCIAL_LINKS = {
|
||||
email: 'nicholai@nicholai.work',
|
||||
website: 'https://nicholai.work',
|
||||
linkedin: '#' // Update when available
|
||||
};
|
||||
|
||||
61
src/layouts/BaseLayout.astro
Normal file
61
src/layouts/BaseLayout.astro
Normal file
@ -0,0 +1,61 @@
|
||||
---
|
||||
import BaseHead from '../components/BaseHead.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import GridOverlay from '../components/GridOverlay.astro';
|
||||
import Navigation from '../components/Navigation.astro';
|
||||
import CustomCursor from '../components/CustomCursor';
|
||||
import { SITE_TITLE, SITE_DESCRIPTION } from '../consts';
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
const { title = SITE_TITLE, description = SITE_DESCRIPTION } = Astro.props;
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="scroll-smooth">
|
||||
<head>
|
||||
<BaseHead title={title} description={description} />
|
||||
</head>
|
||||
<body class="antialiased selection:bg-brand-accent selection:text-brand-dark bg-brand-dark text-white">
|
||||
<CustomCursor client:load />
|
||||
<GridOverlay />
|
||||
<Navigation />
|
||||
|
||||
<main class="relative z-10 pt-32 lg:pt-48 pb-24 min-h-screen">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
|
||||
<script>
|
||||
// Initialize Lucide icons
|
||||
// @ts-ignore
|
||||
if (window.lucide) {
|
||||
// @ts-ignore
|
||||
window.lucide.createIcons();
|
||||
}
|
||||
|
||||
// Intersection Observer for Reveal Animations
|
||||
const observerOptions = {
|
||||
threshold: 0.1,
|
||||
rootMargin: "0px"
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('active');
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
|
||||
document.querySelectorAll('.reveal-text').forEach(el => {
|
||||
observer.observe(el);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,86 +1,37 @@
|
||||
---
|
||||
import { Image } from 'astro:assets';
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
import BaseHead from '../components/BaseHead.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import BaseLayout from './BaseLayout.astro';
|
||||
import FormattedDate from '../components/FormattedDate.astro';
|
||||
import Header from '../components/Header.astro';
|
||||
import { Image } from 'astro:assets';
|
||||
|
||||
type Props = CollectionEntry<'blog'>['data'];
|
||||
|
||||
const { title, description, pubDate, updatedDate, heroImage } = Astro.props;
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<BaseHead title={title} description={description} />
|
||||
<style>
|
||||
main {
|
||||
width: calc(100% - 2em);
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
.hero-image {
|
||||
width: 100%;
|
||||
}
|
||||
.hero-image img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--box-shadow);
|
||||
}
|
||||
.prose {
|
||||
width: 720px;
|
||||
max-width: calc(100% - 2em);
|
||||
margin: auto;
|
||||
padding: 1em;
|
||||
color: rgb(var(--gray-dark));
|
||||
}
|
||||
.title {
|
||||
margin-bottom: 1em;
|
||||
padding: 1em 0;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
}
|
||||
.title h1 {
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
.date {
|
||||
margin-bottom: 0.5em;
|
||||
color: rgb(var(--gray));
|
||||
}
|
||||
.last-updated-on {
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Header />
|
||||
<main>
|
||||
<article>
|
||||
<div class="hero-image">
|
||||
{heroImage && <Image width={1020} height={510} src={heroImage} alt="" />}
|
||||
<BaseLayout title={title} description={description}>
|
||||
<article class="container mx-auto px-6 lg:px-12 max-w-4xl">
|
||||
<div class="mb-12">
|
||||
<div class="mb-6">
|
||||
{heroImage && <Image width={1020} height={510} src={heroImage} alt="" class="w-full h-auto rounded-none border border-white/10" />}
|
||||
</div>
|
||||
<div class="border-b border-white/10 pb-8 mb-8">
|
||||
<div class="flex items-center gap-4 text-xs font-mono text-slate-500 uppercase tracking-widest mb-4">
|
||||
<FormattedDate date={pubDate} />
|
||||
{
|
||||
updatedDate && (
|
||||
<div class="italic">
|
||||
(Updated: <FormattedDate date={updatedDate} />)
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div class="prose">
|
||||
<div class="title">
|
||||
<div class="date">
|
||||
<FormattedDate date={pubDate} />
|
||||
{
|
||||
updatedDate && (
|
||||
<div class="last-updated-on">
|
||||
Last updated on <FormattedDate date={updatedDate} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<h1>{title}</h1>
|
||||
<hr />
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
<h1 class="text-4xl md:text-6xl font-bold text-white uppercase leading-tight mb-4">{title}</h1>
|
||||
<p class="text-xl text-slate-400 leading-relaxed">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="prose prose-invert prose-lg max-w-none text-slate-300 prose-headings:text-white prose-headings:uppercase prose-headings:font-bold prose-a:text-brand-accent prose-a:no-underline hover:prose-a:underline prose-strong:text-white prose-code:text-brand-accent prose-code:bg-brand-accent/10 prose-code:px-1 prose-code:rounded-sm prose-pre:bg-brand-panel prose-pre:border prose-pre:border-white/10">
|
||||
<slot />
|
||||
</div>
|
||||
</article>
|
||||
</BaseLayout>
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
---
|
||||
import AboutHeroImage from '../assets/blog-placeholder-about.jpg';
|
||||
import Layout from '../layouts/BlogPost.astro';
|
||||
---
|
||||
|
||||
<Layout
|
||||
title="About Me"
|
||||
description="Lorem ipsum dolor sit amet"
|
||||
pubDate={new Date('August 08 2021')}
|
||||
heroImage={AboutHeroImage}
|
||||
>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua. Vitae ultricies leo integer malesuada nunc vel risus commodo
|
||||
viverra. Adipiscing enim eu turpis egestas pretium. Euismod elementum nisi quis eleifend quam
|
||||
adipiscing. In hac habitasse platea dictumst vestibulum. Sagittis purus sit amet volutpat. Netus
|
||||
et malesuada fames ac turpis egestas. Eget magna fermentum iaculis eu non diam phasellus
|
||||
vestibulum lorem. Varius sit amet mattis vulputate enim. Habitasse platea dictumst quisque
|
||||
sagittis. Integer quis auctor elit sed vulputate mi. Dictumst quisque sagittis purus sit amet.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Morbi tristique senectus et netus. Id semper risus in hendrerit gravida rutrum quisque non
|
||||
tellus. Habitasse platea dictumst quisque sagittis purus sit amet. Tellus molestie nunc non
|
||||
blandit massa. Cursus vitae congue mauris rhoncus. Accumsan tortor posuere ac ut. Fringilla urna
|
||||
porttitor rhoncus dolor. Elit ullamcorper dignissim cras tincidunt lobortis. In cursus turpis
|
||||
massa tincidunt dui ut ornare lectus. Integer feugiat scelerisque varius morbi enim nunc.
|
||||
Bibendum neque egestas congue quisque egestas diam. Cras ornare arcu dui vivamus arcu felis
|
||||
bibendum. Dignissim suspendisse in est ante in nibh mauris. Sed tempus urna et pharetra pharetra
|
||||
massa massa ultricies mi.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Mollis nunc sed id semper risus in. Convallis a cras semper auctor neque. Diam sit amet nisl
|
||||
suscipit. Lacus viverra vitae congue eu consequat ac felis donec. Egestas integer eget aliquet
|
||||
nibh praesent tristique magna sit amet. Eget magna fermentum iaculis eu non diam. In vitae
|
||||
turpis massa sed elementum. Tristique et egestas quis ipsum suspendisse ultrices. Eget lorem
|
||||
dolor sed viverra ipsum. Vel turpis nunc eget lorem dolor sed viverra. Posuere ac ut consequat
|
||||
semper viverra nam. Laoreet suspendisse interdum consectetur libero id faucibus. Diam phasellus
|
||||
vestibulum lorem sed risus ultricies tristique. Rhoncus dolor purus non enim praesent elementum
|
||||
facilisis. Ultrices tincidunt arcu non sodales neque. Tempus egestas sed sed risus pretium quam
|
||||
vulputate. Viverra suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Fringilla
|
||||
urna porttitor rhoncus dolor purus non. Amet dictum sit amet justo donec enim.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Mattis ullamcorper velit sed ullamcorper morbi tincidunt. Tortor posuere ac ut consequat semper
|
||||
viverra. Tellus mauris a diam maecenas sed enim ut sem viverra. Venenatis urna cursus eget nunc
|
||||
scelerisque viverra mauris in. Arcu ac tortor dignissim convallis aenean et tortor at. Curabitur
|
||||
gravida arcu ac tortor dignissim convallis aenean et tortor. Egestas tellus rutrum tellus
|
||||
pellentesque eu. Fusce ut placerat orci nulla pellentesque dignissim enim sit amet. Ut enim
|
||||
blandit volutpat maecenas volutpat blandit aliquam etiam. Id donec ultrices tincidunt arcu. Id
|
||||
cursus metus aliquam eleifend mi.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Tempus quam pellentesque nec nam aliquam sem. Risus at ultrices mi tempus imperdiet. Id porta
|
||||
nibh venenatis cras sed felis eget velit. Ipsum a arcu cursus vitae. Facilisis magna etiam
|
||||
tempor orci eu lobortis elementum. Tincidunt dui ut ornare lectus sit. Quisque non tellus orci
|
||||
ac. Blandit libero volutpat sed cras. Nec tincidunt praesent semper feugiat nibh sed pulvinar
|
||||
proin gravida. Egestas integer eget aliquet nibh praesent tristique magna.
|
||||
</p>
|
||||
</Layout>
|
||||
@ -1,10 +1,8 @@
|
||||
---
|
||||
import { Image } from 'astro:assets';
|
||||
import { getCollection } from 'astro:content';
|
||||
import BaseHead from '../../components/BaseHead.astro';
|
||||
import Footer from '../../components/Footer.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import FormattedDate from '../../components/FormattedDate.astro';
|
||||
import Header from '../../components/Header.astro';
|
||||
import { SITE_DESCRIPTION, SITE_TITLE } from '../../consts';
|
||||
|
||||
const posts = (await getCollection('blog')).sort(
|
||||
@ -12,103 +10,49 @@ const posts = (await getCollection('blog')).sort(
|
||||
);
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
|
||||
<style>
|
||||
main {
|
||||
width: 960px;
|
||||
}
|
||||
ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2rem;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
ul li {
|
||||
width: calc(50% - 1rem);
|
||||
}
|
||||
ul li * {
|
||||
text-decoration: none;
|
||||
transition: 0.2s ease;
|
||||
}
|
||||
ul li:first-child {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
ul li:first-child img {
|
||||
width: 100%;
|
||||
}
|
||||
ul li:first-child .title {
|
||||
font-size: 2.369rem;
|
||||
}
|
||||
ul li img {
|
||||
margin-bottom: 0.5rem;
|
||||
border-radius: 12px;
|
||||
}
|
||||
ul li a {
|
||||
display: block;
|
||||
}
|
||||
.title {
|
||||
margin: 0;
|
||||
color: rgb(var(--black));
|
||||
line-height: 1;
|
||||
}
|
||||
.date {
|
||||
margin: 0;
|
||||
color: rgb(var(--gray));
|
||||
}
|
||||
ul li a:hover h4,
|
||||
ul li a:hover .date {
|
||||
color: rgb(var(--accent));
|
||||
}
|
||||
ul a:hover img {
|
||||
box-shadow: var(--box-shadow);
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
ul {
|
||||
gap: 0.5em;
|
||||
}
|
||||
ul li {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
ul li:first-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ul li:first-child .title {
|
||||
font-size: 1.563em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<Header />
|
||||
<main>
|
||||
<section>
|
||||
<ul>
|
||||
{
|
||||
posts.map((post) => (
|
||||
<li>
|
||||
<a href={`/blog/${post.id}/`}>
|
||||
{post.data.heroImage && (
|
||||
<Image width={720} height={360} src={post.data.heroImage} alt="" />
|
||||
)}
|
||||
<h4 class="title">{post.data.title}</h4>
|
||||
<p class="date">
|
||||
<BaseLayout title={SITE_TITLE} description={SITE_DESCRIPTION}>
|
||||
<section class="container mx-auto px-6 lg:px-12">
|
||||
<div class="mb-16">
|
||||
<h1 class="text-6xl md:text-8xl font-bold uppercase text-white mb-4 tracking-tighter">Blog</h1>
|
||||
<p class="text-slate-400 font-mono">/// THOUGHTS & PROCESS</p>
|
||||
</div>
|
||||
<ul class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-16">
|
||||
{
|
||||
posts.map((post) => (
|
||||
<li class="group">
|
||||
<a href={`/blog/${post.id}/`} class="block">
|
||||
<div class="relative aspect-[16/9] mb-6 overflow-hidden border border-white/10 group-hover:border-brand-accent/50 transition-colors">
|
||||
{post.data.heroImage && (
|
||||
<Image
|
||||
width={720}
|
||||
height={360}
|
||||
src={post.data.heroImage}
|
||||
alt=""
|
||||
class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
||||
/>
|
||||
)}
|
||||
<div class="absolute inset-0 bg-brand-dark/20 group-hover:bg-transparent transition-colors"></div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<span class="text-xs font-mono text-brand-accent uppercase tracking-widest">
|
||||
<FormattedDate date={post.data.pubDate} />
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
</span>
|
||||
</div>
|
||||
<h4 class="text-2xl font-bold text-white uppercase mb-2 group-hover:text-brand-accent transition-colors leading-tight">
|
||||
{post.data.title}
|
||||
</h4>
|
||||
<p class="text-slate-400 line-clamp-2">
|
||||
{/* We could add a description field to blog posts or just show title */}
|
||||
</p>
|
||||
<span class="mt-4 text-xs font-bold uppercase tracking-widest text-slate-500 group-hover:text-white transition-colors flex items-center gap-2">
|
||||
Read Article <span class="block w-4 h-[1px] bg-slate-500 group-hover:bg-brand-accent transition-colors"></span>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</section>
|
||||
</BaseLayout>
|
||||
|
||||
@ -1,49 +1,18 @@
|
||||
---
|
||||
import BaseHead from '../components/BaseHead.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import Header from '../components/Header.astro';
|
||||
import { SITE_DESCRIPTION, SITE_TITLE } from '../consts';
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import Hero from '../components/sections/Hero.astro';
|
||||
import Experience from '../components/sections/Experience.astro';
|
||||
import FeaturedProject from '../components/sections/FeaturedProject.astro';
|
||||
import Skills from '../components/sections/Skills.astro';
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
|
||||
</head>
|
||||
<body>
|
||||
<Header />
|
||||
<main>
|
||||
<h1>🧑🚀 Hello, Astronaut!</h1>
|
||||
<p>
|
||||
Welcome to the official <a href="https://astro.build/">Astro</a> blog starter template. This
|
||||
template serves as a lightweight, minimally-styled starting point for anyone looking to build
|
||||
a personal website, blog, or portfolio with Astro.
|
||||
</p>
|
||||
<p>
|
||||
This template comes with a few integrations already configured in your
|
||||
<code>astro.config.mjs</code> file. You can customize your setup with
|
||||
<a href="https://astro.build/integrations">Astro Integrations</a> to add tools like Tailwind,
|
||||
React, or Vue to your project.
|
||||
</p>
|
||||
<p>Here are a few ideas on how to get started with the template:</p>
|
||||
<ul>
|
||||
<li>Edit this page in <code>src/pages/index.astro</code></li>
|
||||
<li>Edit the site header items in <code>src/components/Header.astro</code></li>
|
||||
<li>Add your name to the footer in <code>src/components/Footer.astro</code></li>
|
||||
<li>Check out the included blog posts in <code>src/content/blog/</code></li>
|
||||
<li>Customize the blog post page layout in <code>src/layouts/BlogPost.astro</code></li>
|
||||
</ul>
|
||||
<p>
|
||||
Have fun! If you get stuck, remember to
|
||||
<a href="https://docs.astro.build/">read the docs</a>
|
||||
or <a href="https://astro.build/chat">join us on Discord</a> to ask questions.
|
||||
</p>
|
||||
<p>
|
||||
Looking for a blog template with a bit more personality? Check out
|
||||
<a href="https://github.com/Charca/astro-blog-template">astro-blog-template</a>
|
||||
by <a href="https://twitter.com/Charca">Maxi Ferreira</a>.
|
||||
</p>
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
<BaseLayout>
|
||||
<Hero />
|
||||
<div class="w-full h-[1px] bg-white/10 my-24"></div>
|
||||
<Experience />
|
||||
<div class="container mx-auto px-6 lg:px-12">
|
||||
<div class="w-full h-[1px] bg-white/10"></div>
|
||||
</div>
|
||||
<FeaturedProject />
|
||||
<Skills />
|
||||
</BaseLayout>
|
||||
|
||||
@ -1,155 +1,126 @@
|
||||
/*
|
||||
The CSS in this style tag is based off of Bear Blog's default CSS.
|
||||
https://github.com/HermanMartinus/bearblog/blob/297026a877bc2ab2b3bdfbd6b9f7961c350917dd/templates/styles/blog/default.css
|
||||
License MIT: https://github.com/HermanMartinus/bearblog/blob/master/LICENSE.md
|
||||
*/
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--accent: #2337ff;
|
||||
--accent-dark: #000d8a;
|
||||
--black: 15, 18, 25;
|
||||
--gray: 96, 115, 159;
|
||||
--gray-light: 229, 233, 240;
|
||||
--gray-dark: 34, 41, 57;
|
||||
--gray-gradient: rgba(var(--gray-light), 50%), #fff;
|
||||
--box-shadow:
|
||||
0 2px 6px rgba(var(--gray), 25%), 0 8px 24px rgba(var(--gray), 33%), 0 16px 32px
|
||||
rgba(var(--gray), 33%);
|
||||
@theme {
|
||||
--color-brand-dark: #0B0D11;
|
||||
--color-brand-panel: #151921;
|
||||
--color-brand-accent: #FFB84C;
|
||||
--color-brand-cyan: #22D3EE;
|
||||
--color-brand-red: #E11D48;
|
||||
|
||||
--font-sans: "Inter", sans-serif;
|
||||
--font-mono: "Space Mono", monospace;
|
||||
|
||||
--animate-reveal: reveal 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
|
||||
@keyframes reveal {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Atkinson";
|
||||
src: url("/fonts/atkinson-regular.woff") format("woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
|
||||
@utility text-massive {
|
||||
line-height: 0.9;
|
||||
letter-spacing: -0.04em;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Atkinson";
|
||||
src: url("/fonts/atkinson-bold.woff") format("woff");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
|
||||
@utility text-stroke {
|
||||
-webkit-text-stroke: 1px rgba(255, 255, 255, 0.1);
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
@utility skill-tag {
|
||||
@apply text-[10px] font-mono font-bold uppercase tracking-wider px-2 py-2 border border-slate-700 text-slate-400 hover:border-brand-accent hover:text-white transition-colors cursor-default select-none;
|
||||
}
|
||||
|
||||
@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-colors inline-block;
|
||||
}
|
||||
|
||||
@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-colors inline-block;
|
||||
}
|
||||
|
||||
@utility grid-overlay {
|
||||
background-size: 100px 100px;
|
||||
background-image: linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* Base Styles */
|
||||
body {
|
||||
font-family: "Atkinson", sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
background: linear-gradient(var(--gray-gradient)) no-repeat;
|
||||
background-size: 100% 600px;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
color: rgb(var(--gray-dark));
|
||||
font-size: 20px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
main {
|
||||
width: 720px;
|
||||
max-width: calc(100% - 2em);
|
||||
margin: auto;
|
||||
padding: 3em 1em;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: rgb(var(--black));
|
||||
line-height: 1.2;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3.052em;
|
||||
}
|
||||
h2 {
|
||||
font-size: 2.441em;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.953em;
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.563em;
|
||||
}
|
||||
h5 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
strong,
|
||||
b {
|
||||
font-weight: 700;
|
||||
}
|
||||
a {
|
||||
color: var(--accent);
|
||||
}
|
||||
a:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.prose p {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
}
|
||||
input {
|
||||
font-size: 16px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
}
|
||||
code {
|
||||
padding: 2px 5px;
|
||||
background-color: rgb(var(--gray-light));
|
||||
border-radius: 2px;
|
||||
}
|
||||
pre {
|
||||
padding: 1.5em;
|
||||
border-radius: 8px;
|
||||
}
|
||||
pre > code {
|
||||
all: unset;
|
||||
}
|
||||
blockquote {
|
||||
border-left: 4px solid var(--accent);
|
||||
padding: 0 0 0 20px;
|
||||
margin: 0;
|
||||
font-size: 1.333em;
|
||||
}
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid rgb(var(--gray-light));
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
body {
|
||||
font-size: 18px;
|
||||
}
|
||||
main {
|
||||
padding: 1em;
|
||||
}
|
||||
background-color: var(--color-brand-dark);
|
||||
color: #ffffff;
|
||||
overflow-x: hidden;
|
||||
/* cursor: none; Handled by CustomCursor component logic, but we might want it here if we implement the custom cursor fully */
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: absolute !important;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
overflow: hidden;
|
||||
/* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
|
||||
clip: rect(1px 1px 1px 1px);
|
||||
/* maybe deprecated but we need to support legacy browsers */
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
/* modern browsers, clip-path works inwards from each corner */
|
||||
clip-path: inset(50%);
|
||||
/* added line to stop words getting smushed together (as they go onto separate lines and some screen readers do not understand line feeds as a space */
|
||||
white-space: nowrap;
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--color-brand-dark);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #334155;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--color-brand-accent);
|
||||
}
|
||||
|
||||
/* Reveal Animation Class (handled by JS intersection observer usually, but basic class here) */
|
||||
.reveal-text {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: all 0.8s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.reveal-text.active {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Cursor Styles */
|
||||
.cursor-dot,
|
||||
.cursor-outline {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 50%;
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cursor-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--color-brand-accent);
|
||||
}
|
||||
|
||||
.cursor-outline {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 1px solid rgba(255, 184, 76, 0.5);
|
||||
transition: width 0.2s, height 0.2s, background-color 0.2s;
|
||||
}
|
||||
|
||||
/* Interactive Elements Cursor Hover Effect */
|
||||
.hover-trigger:hover ~ .cursor-outline,
|
||||
a:hover ~ .cursor-outline,
|
||||
button:hover ~ .cursor-outline {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background-color: rgba(255, 184, 76, 0.05);
|
||||
border-color: var(--color-brand-accent);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user