Further content updates

This commit is contained in:
Greg Kilmer
2026-07-04 14:21:51 -04:00
parent c298562266
commit db6ff9f213
13 changed files with 405 additions and 220 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 625 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 909 KiB

+33 -19
View File
@@ -8,7 +8,7 @@
* The kicker is rendered as a monospace number/label per the spec's "Accent/ * The kicker is rendered as a monospace number/label per the spec's "Accent/
* quotes: a monospace or display font sparingly for section numbers" guidance. * quotes: a monospace or display font sparingly for section numbers" guidance.
*/ */
import Icon from '@components/Icon.astro'; import Icon from "@components/Icon.astro";
interface Props { interface Props {
/** Monospace kicker, e.g. "02" or "Experience". */ /** Monospace kicker, e.g. "02" or "Experience". */
@@ -19,32 +19,46 @@ interface Props {
/** Optional mdi icon shown beside the kicker. */ /** Optional mdi icon shown beside the kicker. */
icon?: string; icon?: string;
class?: string; class?: string;
align?: 'left' | 'center'; align?: "left" | "center";
} }
const { kicker, title, subtitle, icon, class: className = '', align = 'left' } = Astro.props; const {
kicker,
title,
subtitle,
icon,
class: className = "",
align = "left",
} = Astro.props;
--- ---
<div <div
class:list={[ class:list={[
'flex flex-col gap-3', "flex flex-col gap-3",
align === 'center' && 'items-center text-center', align === "center" && "items-center text-center",
className, className,
]} ]}
> >
{kicker && ( {
<span kicker && (
class="inline-flex items-center gap-2 font-mono text-xs uppercase tracking-[0.22em]" <span
style="color: var(--color-accent);" class="inline-flex items-center gap-2 font-mono text-xs uppercase tracking-[0.22em]"
> style="color: var(--color-accent);"
{icon && <Icon name={icon} class="h-4 w-4" />} >
{kicker} {icon && <Icon name={icon} class="h-4 w-4" />}
</span> {kicker}
)} </span>
)
}
<h2>{title}</h2> <h2>{title}</h2>
{subtitle && ( {
<p class="max-w-2xl text-base sm:text-lg" style="color: var(--color-text-muted);"> subtitle && (
{subtitle} <p
</p> class="max-w-2xl text-base sm:text-lg"
)} style="color: var(--color-text-muted);"
>
{subtitle}
</p>
)
}
</div> </div>
+131 -67
View File
@@ -11,58 +11,101 @@
* treatment (decorative, not a proficiency claim) to feel like the spec's * treatment (decorative, not a proficiency claim) to feel like the spec's
* "visual skill bars" suggestion without implying false metrics. * "visual skill bars" suggestion without implying false metrics.
*/ */
import Icon from '@components/Icon.astro'; import Icon from "@components/Icon.astro";
import Placeholder from '@components/layout/Placeholder.astro'; import Placeholder from "@components/layout/Placeholder.astro";
const core = [ const core = [
{ icon: 'cloud', label: 'Cloud Architecture & Development' }, { icon: "cloud", label: "Cloud Architecture & Development" },
{ icon: 'cellphone-link', label: 'Mobile Application Development' }, { icon: "cellphone-link", label: "Mobile Application Development" },
{ icon: 'server-network', label: 'SAP Business Technology Platform (BTP)' }, { icon: "server-network", label: "SAP Business Technology Platform (BTP)" },
{ icon: 'account-group', label: 'Team Leadership & Technical Mentorship' }, { icon: "account-group", label: "Team Leadership & Technical Mentorship" },
{ icon: 'translate', label: 'Cross-functional Communication' }, { icon: "translate", label: "Cross-functional Communication" },
]; ];
const emerging = [ const emerging = [
{ icon: 'chart-line', label: 'SAP BTP deep-dive (active expansion)' }, { icon: "chart-line", label: "SAP BTP deep-dive (active expansion)" },
{ icon: 'robot', label: 'AI-powered development tooling' }, { icon: "robot", label: "Cutting edge AI-powered development tooling" },
{ icon: 'cog-refresh', label: 'Advanced automation and workflow engineering' }, {
icon: "cog-refresh",
label: "Advanced automation and workflow engineering",
},
]; ];
// Technical stack mixes known items (from spec context) with explicit gaps. // Technical stack mixes known items (from spec context) with explicit gaps.
const techStack = [ const techStack = [
{ icon: 'code-braces', label: 'Languages', value: null, hint: 'Likely JavaScript/TypeScript, Python, Java, ABAP given SAP focus.' }, {
{ icon: 'cube-outline', label: 'Frameworks', value: null, hint: 'Add your primary frameworks here.' }, icon: "code-braces",
{ icon: 'microsoft-azure-devops', label: 'Cloud Platforms', value: 'SAP BTP, [others]', hint: 'List the cloud platforms you ship on.' }, label: "Languages",
{ icon: 'git', label: 'Tools', value: 'Git, CI/CD pipelines, [fill in]', hint: 'Add the rest of your toolchain.' }, value:
{ icon: 'brain', label: 'AI/ML', value: 'Hands-on experience with machine learning frameworks and LLM integration', hint: null }, "JavaScript, TypeScript, Java, Python, C#, C, SQL, HTML, CSS, and many more.",
hint: "JavaScript, TypeScript, Java, Python, C#, C, SQL, HTML, CSS, and many more.",
},
{
icon: "cube-outline",
label: "Frameworks",
value: "React, Vue, Astro, Flask",
hint: "Add your primary frameworks here.",
},
{
icon: "microsoft-azure-devops",
label: "Cloud Platforms",
value: "SAP BTP, Google Cloud Platform, AWS",
hint: "List the cloud platforms you ship on.",
},
{
icon: "git",
label: "Tools",
value: "Git, Gitea, CI/CD pipelines",
hint: "Add the rest of your toolchain.",
},
{
icon: "brain",
label: "AI/ML",
value:
"Hands-on experience with machine learning frameworks and LLM integration and agent harnesses like ClaudeCode, Hermes, and OpenClaw",
hint: null,
},
]; ];
--- ---
<div class="flex flex-col gap-8"> <div class="flex flex-col gap-8">
<!-- Core Competencies --> <!-- Core Competencies -->
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<h3 class="font-sans" style="color: var(--color-text);">Core Competencies</h3> <h3 class="font-sans" style="color: var(--color-text);">
Core Competencies
</h3>
<ul class="grid gap-3 sm:grid-cols-2"> <ul class="grid gap-3 sm:grid-cols-2">
{core.map((item, i) => ( {
<li core.map((item, i) => (
class="reveal card flex items-center gap-3" <li
data-reveal class="reveal card flex items-center gap-3"
style={`transition-delay:${i * 60}ms`} data-reveal
> style={`transition-delay:${i * 60}ms`}
<span
class="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg"
style="background: color-mix(in srgb, var(--color-accent) 16%, transparent); color: var(--color-accent);"
aria-hidden="true"
> >
<Icon name={item.icon} class="h-5 w-5" /> <span
</span> class="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg"
<span class="text-sm font-medium" style="color: var(--color-text);">{item.label}</span> style="background: color-mix(in srgb, var(--color-accent) 16%, transparent); color: var(--color-accent);"
{/* Decorative bar; intentionally not a % so we never imply false metrics. */} aria-hidden="true"
<span class="ml-auto hidden h-1.5 w-16 overflow-hidden rounded-full sm:block" style="background: var(--color-border);" aria-hidden="true"> >
<span class="block h-full w-full" style="background: var(--color-accent);"></span> <Icon name={item.icon} class="h-5 w-5" />
</span> </span>
</li> <span class="text-sm font-medium" style="color: var(--color-text);">
))} {item.label}
</span>
{/* Decorative bar; intentionally not a % so we never imply false metrics. */}
<span
class="ml-auto hidden h-1.5 w-16 overflow-hidden rounded-full sm:block"
style="background: var(--color-border);"
aria-hidden="true"
>
<span
class="block h-full w-full"
style="background: var(--color-accent);"
/>
</span>
</li>
))
}
</ul> </ul>
</div> </div>
@@ -70,46 +113,67 @@ const techStack = [
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<h3 class="font-sans" style="color: var(--color-text);">Technical Stack</h3> <h3 class="font-sans" style="color: var(--color-text);">Technical Stack</h3>
<div class="grid gap-4 sm:grid-cols-2"> <div class="grid gap-4 sm:grid-cols-2">
{techStack.map((item) => ( {
<div class="card flex flex-col gap-2"> techStack.map((item) => (
<div class="flex items-center gap-3"> <div class="card flex flex-col gap-2">
<span <div class="flex items-center gap-3">
class="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg" <span
style="background: color-mix(in srgb, var(--color-secondary) 18%, transparent); color: var(--color-secondary);" class="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg"
aria-hidden="true" style="background: color-mix(in srgb, var(--color-secondary) 18%, transparent); color: var(--color-secondary);"
> aria-hidden="true"
<Icon name={item.icon} class="h-5 w-5" /> >
</span> <Icon name={item.icon} class="h-5 w-5" />
<span class="font-sans text-sm font-semibold" style="color: var(--color-text);">{item.label}</span> </span>
<span
class="font-sans text-sm font-semibold"
style="color: var(--color-text);"
>
{item.label}
</span>
</div>
{item.value ? (
<p class="text-sm" style="color: var(--color-text-muted);">
{item.value}
</p>
) : (
<Placeholder
label={item.label}
hint={item.hint ?? ""}
class="mt-1"
/>
)}
</div> </div>
{item.value ))
? <p class="text-sm" style="color: var(--color-text-muted);">{item.value}</p> }
: <Placeholder label={item.label} hint={item.hint ?? ''} class="mt-1" />}
</div>
))}
</div> </div>
</div> </div>
<!-- Emerging & Exploring --> <!-- Emerging & Exploring -->
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<h3 class="font-sans" style="color: var(--color-text);">Emerging &amp; Exploring</h3> <h3 class="font-sans" style="color: var(--color-text);">
Emerging &amp; Exploring
</h3>
<ul class="grid gap-3 sm:grid-cols-3"> <ul class="grid gap-3 sm:grid-cols-3">
{emerging.map((item, i) => ( {
<li emerging.map((item, i) => (
class="reveal card flex items-start gap-3" <li
data-reveal class="reveal card flex items-start gap-3"
style={`transition-delay:${i * 70}ms`} data-reveal
> style={`transition-delay:${i * 70}ms`}
<span
class="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg"
style="background: color-mix(in srgb, var(--color-accent) 16%, transparent); color: var(--color-accent);"
aria-hidden="true"
> >
<Icon name={item.icon} class="h-5 w-5" /> <span
</span> class="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg"
<span class="text-sm" style="color: var(--color-text);">{item.label}</span> style="background: color-mix(in srgb, var(--color-accent) 16%, transparent); color: var(--color-accent);"
</li> aria-hidden="true"
))} >
<Icon name={item.icon} class="h-5 w-5" />
</span>
<span class="text-sm" style="color: var(--color-text);">
{item.label}
</span>
</li>
))
}
</ul> </ul>
</div> </div>
</div> </div>
+8 -7
View File
@@ -3,11 +3,11 @@
# This seeds the Experience timeline. The narrative bullets below are the spec's # This seeds the Experience timeline. The narrative bullets below are the spec's
# template bullets; Greg should replace the bracketed placeholders with real # template bullets; Greg should replace the bracketed placeholders with real
# outcomes, metrics, and dates. See docs/CONTENT.md for how to add more roles. # outcomes, metrics, and dates. See docs/CONTENT.md for how to add more roles.
company: 'Implico Inc.' company: "Implico Inc."
title: 'Software Developer / Lead Developer / Team Lead' title: "Software Developer / Lead Developer / BTP Cloud and Mobile App Lead"
period: '[Greg to fill dates]' period: "Mar 2019 - Present"
focus: 'Cloud and mobile application development, SAP-adjacent solutions.' focus: "BTP, Cloud, and Mobile application development, SAP-adjacent solutions."
icon: 'briefcase' icon: "briefcase"
order: 10 order: 10
--- ---
@@ -15,8 +15,9 @@ Current expansion: **SAP Business Technology Platform (BTP)**.
Key contributions: Key contributions:
- Lead development on [cloud/mobile project], delivering [specific outcome]. - Lead development on Carrier Connect, delivering a robust 3rd party carrier logistics tool.
- Architect and maintain [system/platform], supporting [X users/clients]. - Architect and maintain multiple carrier integration systems, supporting over 1000 active users and 100 companies.
- Collaborate with cross-functional teams to translate business requirements into technical solutions. - Collaborate with cross-functional teams to translate business requirements into technical solutions.
- Mentor junior developers and establish coding standards and review practices. - Mentor junior developers and establish coding standards and review practices.
- Drive adoption of new technologies and frameworks within the team. - Drive adoption of new technologies and frameworks within the team.
- Maintain responsible AI usage guidelines in an every changing ecosystem.
+4 -4
View File
@@ -4,12 +4,12 @@
# entries. To replace it: copy this file, edit the frontmatter + body, and bump # entries. To replace it: copy this file, edit the frontmatter + body, and bump
# `placeholder: false`. Or just delete this file and add your own. See # `placeholder: false`. Or just delete this file and add your own. See
# docs/CONTENT.md. The schema lives in src/content.config.ts. # docs/CONTENT.md. The schema lives in src/content.config.ts.
title: 'Your project here' title: "PUT PROJECTS HERE"
role: 'Lead Developer' role: "Lead Developer"
tech: ['TypeScript', 'Node.js', 'SAP BTP'] tech: ["TypeScript", "Node.js", "SAP BTP"]
summary: > summary: >
This is a placeholder so the project grid has the right layout. Replace it This is a placeholder so the project grid has the right layout. Replace it
with a real 2-3 sentence description of what the project does and your with a real 2-3 sentence description of what the project does and my
contribution, then add links below. contribution, then add links below.
order: 100 order: 100
placeholder: true placeholder: true
+7 -6
View File
@@ -27,7 +27,7 @@ const quickFacts = [
{ icon: "dice-multiple", text: "Running D&D & Daggerheart campaigns" }, { icon: "dice-multiple", text: "Running D&D & Daggerheart campaigns" },
{ {
icon: "book-open-variant", icon: "book-open-variant",
text: "Currently reading: Heir of Fire by Sarah J. Maas", text: "Currently tandem reading: Heir of Fire and Tower of Dawn by Sarah J. Maas",
}, },
{ icon: "tea", text: "Fueled by loose-leaf Chinese tea and Mountain Dew" }, { icon: "tea", text: "Fueled by loose-leaf Chinese tea and Mountain Dew" },
]; ];
@@ -64,11 +64,12 @@ const quickFacts = [
class="border-l-2 pl-5 font-sans text-xl leading-relaxed sm:text-2xl" class="border-l-2 pl-5 font-sans text-xl leading-relaxed sm:text-2xl"
style="border-color: var(--color-accent); color: var(--color-text);" style="border-color: var(--color-accent); color: var(--color-text);"
> >
I am Greg: a software developer based in Philadelphia who spends his I am Greg: a software developer and technical team lead based in
free time running tabletop RPGs, making resin dice and custom cards, 3D Philadelphia who spends his free time running tabletop RPGs, making
printing anything I can, and reading his way through fantasy series at resin dice and custom cards, 3D printing anything I can, and reading his
an alarming rate. I live with my partner Krystina, our mini dachshund way through fantasy series at an alarming rate. I live with my partner
Kashi, and more board games than any reasonable apartment should hold. Krystina, our mini dachshund Kashi, and more board games than any
reasonable apartment should hold.
</p> </p>
</Reveal> </Reveal>
</section> </section>
+3 -3
View File
@@ -25,7 +25,7 @@ const cards = [
{ {
icon: "briefcase", icon: "briefcase",
title: "Professional", title: "Professional",
copy: "From SAP-adjacent cloud apps to full-stack mobile — a decade of building software that businesses actually use.", copy: "From SAP-adjacent cloud to full-stack mobile, a decade of building software that businesses actually use.",
href: "/professional", href: "/professional",
image: "/images/pro-workspace.jpg", image: "/images/pro-workspace.jpg",
alt: "Developer workspace with code on screen", alt: "Developer workspace with code on screen",
@@ -33,7 +33,7 @@ const cards = [
{ {
icon: "dice-multiple", icon: "dice-multiple",
title: "Tabletop", title: "Tabletop",
copy: "Currently running two campaigns in Foundry VTT and playtesting MCDM's Crows. Always rolling dice.", copy: "Currently obsessed with Spirit Island, Magic: The Gathering, and running campaigns in Foundry VTT. Always planning more; always rolling dice.",
href: "/interests/ttrpgs", href: "/interests/ttrpgs",
image: "/images/ttrpg-dice.jpg", image: "/images/ttrpg-dice.jpg",
alt: "Polyhedral dice on a dark surface", alt: "Polyhedral dice on a dark surface",
@@ -41,7 +41,7 @@ const cards = [
{ {
icon: "palette", icon: "palette",
title: "Making", title: "Making",
copy: "Resin dice, 3D prints, and whatever else catches my eye. If it involves layers and precision, I am in.", copy: "Resin dice, 3D prints, TCG proxies, and whatever else catches my eye. If it involves really nice cardboard or mastering a new craft, I am in.",
href: "/interests/making", href: "/interests/making",
image: "/images/making-resin.jpg", image: "/images/making-resin.jpg",
alt: "Resin crafting in progress", alt: "Resin crafting in progress",
+103 -35
View File
@@ -5,31 +5,55 @@
* Per-interest template: header + tagline, verbatim overview, platforms card * Per-interest template: header + tagline, verbatim overview, platforms card
* grid, favorite genres, currently playing placeholder, and related interests. * grid, favorite genres, currently playing placeholder, and related interests.
*/ */
import BaseLayout from '@layouts/BaseLayout.astro'; import BaseLayout from "@layouts/BaseLayout.astro";
import Reveal from '@components/motion/Reveal.astro'; import Reveal from "@components/motion/Reveal.astro";
import InterestHero from '@components/layout/InterestHero.astro'; import InterestHero from "@components/layout/InterestHero.astro";
import InterestCard from '@components/cards/InterestCard.astro'; import InterestCard from "@components/cards/InterestCard.astro";
import SectionHeader from '@components/layout/SectionHeader.astro'; import SectionHeader from "@components/layout/SectionHeader.astro";
import RelatedInterests from '@components/layout/RelatedInterests.astro'; import RelatedInterests from "@components/layout/RelatedInterests.astro";
import Placeholder from '@components/layout/Placeholder.astro'; import Placeholder from "@components/layout/Placeholder.astro";
import Icon from '@components/Icon.astro'; import Icon from "@components/Icon.astro";
const platforms = [ const platforms = [
{ icon: 'sony-playstation', title: 'PlayStation 5', body: 'Main console for AAA titles and exclusives.' }, {
{ icon: 'nintendo-switch', title: 'Nintendo Switch / Switch 2', body: 'Handheld and docked. Nintendo first-party plus indie gems.' }, icon: "sony-playstation",
{ icon: 'steam', title: 'Steam', body: 'The primary PC library. Strategy, RPGs, and an ever-growing backlog.' }, title: "PlayStation 5",
body: "Main console for AAA titles and exclusives.",
},
{
icon: "nintendo-switch",
title: "Nintendo Switch / Switch 2",
body: "Handheld and docked. Nintendo first-party plus indie gems.",
},
{
icon: "steam",
title: "Steam",
body: "The primary PC library. Strategy, RPGs, and an ever-growing backlog.",
},
]; ];
const genres = [ const genres = [
'RPGs (both Western and JRPG)', "RPGs (both Western and JRPG)",
'Strategy and tactics', "Strategy and tactics",
'Open-world exploration', "Building and crafting",
'Indie darlings', "Automation",
"Open-world exploration",
"Indie darlings",
]; ];
const related = [ const related = [
{ href: '/interests/pokemon', title: 'Pokémon', icon: 'pokeball', blurb: 'Nintendo console overlap and a lifelong obsession.' }, {
{ href: '/interests/ttrpgs', title: 'TTRPGs & Tabletop', icon: 'dice-multiple', blurb: 'Digital RPGs as another expression of the same impulse.' }, href: "/interests/pokemon",
title: "Pokémon",
icon: "pokeball",
blurb: "Nintendo console overlap and a lifelong obsession.",
},
{
href: "/interests/ttrpgs",
title: "TTRPGs & Tabletop",
icon: "dice-multiple",
blurb: "Digital RPGs as another expression of the same impulse.",
},
]; ];
--- ---
@@ -48,48 +72,92 @@ const related = [
<div class="container-prose flex flex-col gap-20 py-12 sm:py-16"> <div class="container-prose flex flex-col gap-20 py-12 sm:py-16">
{/* Overview */} {/* Overview */}
<section class="flex flex-col gap-5 text-lg leading-relaxed" style="color: var(--color-text-muted);"> <section
<Reveal><p>Gaming is my other constant. I span console generations with a PlayStation 5, Nintendo Switch 2, and a Steam library that has gotten politely out of hand. I'm particularly drawn to RPGs, strategy games, and anything with a deeply layered world. If a game has lore compendiums, I'm reading them.</p></Reveal> class="flex flex-col gap-5 text-lg leading-relaxed"
style="color: var(--color-text-muted);"
>
<Reveal
><p>
Gaming is my other constant. I span console generations with a
PlayStation 5, Nintendo Switch 2, and a Steam library that has gotten
politely out of hand. I'm particularly drawn to RPGs, strategy games,
and anything with a deeply layered world. If a game has lore
compendiums, I'm reading them.
</p></Reveal
>
</section> </section>
{/* Platforms */} {/* Platforms */}
<section class="flex flex-col gap-6"> <section class="flex flex-col gap-6">
<Reveal><SectionHeader kicker="Platforms" icon="gamepad-variant" title="Where I play" /></Reveal> <Reveal
><SectionHeader
kicker="Platforms"
icon="gamepad-variant"
title="Where I play"
/></Reveal
>
<div class="grid gap-5 sm:grid-cols-3"> <div class="grid gap-5 sm:grid-cols-3">
{platforms.map((p, i) => ( {
<Reveal delay={i * 80}> platforms.map((p, i) => (
<InterestCard icon={p.icon} title={p.title}>{p.body}</InterestCard> <Reveal delay={i * 80}>
</Reveal> <InterestCard icon={p.icon} title={p.title}>
))} {p.body}
</InterestCard>
</Reveal>
))
}
</div> </div>
</section> </section>
{/* Genres */} {/* Genres */}
<section class="flex flex-col gap-5"> <section class="flex flex-col gap-5">
<Reveal><SectionHeader kicker="Tastes" icon="shape" title="Favorite genres" /></Reveal> <Reveal
><SectionHeader
kicker="Tastes"
icon="shape"
title="Favorite genres"
/></Reveal
>
<Reveal delay={80}> <Reveal delay={80}>
<ul class="flex flex-wrap gap-3"> <ul class="flex flex-wrap gap-3">
{genres.map((g) => ( {
<li class="pill text-sm"> genres.map((g) => (
<Icon name="star-four-points" class="h-3.5 w-3.5" /> <li class="pill text-sm">
{g} <Icon name="star-four-points" class="h-3.5 w-3.5" />
</li> {g}
))} </li>
))
}
</ul> </ul>
</Reveal> </Reveal>
</section> </section>
{/* Currently */} {/* Currently */}
<section class="flex flex-col gap-6"> <section class="flex flex-col gap-6">
<Reveal><SectionHeader kicker="Currently" icon="calendar-clock" title="Now playing" /></Reveal> <Reveal
><SectionHeader
kicker="Currently"
icon="calendar-clock"
title="Now playing"
/></Reveal
>
<div class="grid gap-4 sm:grid-cols-[1fr_1fr]"> <div class="grid gap-4 sm:grid-cols-[1fr_1fr]">
<figure <figure
class="relative overflow-hidden rounded-xl border" class="relative overflow-hidden rounded-xl border"
style="border-color: color-mix(in srgb, var(--color-accent) 35%, transparent);" style="border-color: color-mix(in srgb, var(--color-accent) 35%, transparent);"
> >
<img src="/images/gaming-setup.jpg" alt="Gaming setup" loading="lazy" decoding="async" class="aspect-[4/3] w-full object-cover" /> <img
src="/images/gaming-setup.jpg"
alt="Gaming setup"
loading="lazy"
decoding="async"
class="aspect-[4/3] w-full object-cover"
/>
</figure> </figure>
<Placeholder label="Currently playing" hint="Add the games you are actively playing right now." /> <Placeholder
label="Currently playing"
hint="Add the games you are actively playing right now."
/>
</div> </div>
</section> </section>
+113 -72
View File
@@ -6,51 +6,56 @@
* sub-page, using the spec's taglines. Sets surface="interest" so the whole * sub-page, using the spec's taglines. Sets surface="interest" so the whole
* page reads warm (cream/amber) regardless of the global light/dark toggle. * page reads warm (cream/amber) regardless of the global light/dark toggle.
*/ */
import BaseLayout from '@layouts/BaseLayout.astro'; import BaseLayout from "@layouts/BaseLayout.astro";
import Reveal from '@components/motion/Reveal.astro'; import Reveal from "@components/motion/Reveal.astro";
import Icon from '@components/Icon.astro'; import Icon from "@components/Icon.astro";
import SectionHeader from '@components/layout/SectionHeader.astro'; import SectionHeader from "@components/layout/SectionHeader.astro";
import AnimatedBackground from '@components/hero/AnimatedBackground.tsx'; import AnimatedBackground from "@components/hero/AnimatedBackground.tsx";
import SacredGeometry from '@components/mystic/SacredGeometry.astro'; import SacredGeometry from "@components/mystic/SacredGeometry.astro";
import ConstellationField from '@components/mystic/ConstellationField.tsx'; import ConstellationField from "@components/mystic/ConstellationField.tsx";
const interests = [ const interests = [
{ {
href: '/interests/ttrpgs', href: "/interests/ttrpgs",
icon: 'dice-multiple', icon: "dice-multiple",
title: 'TTRPGs & Tabletop', title: "TTRPGs & Tabletop",
tagline: 'Three campaigns, two systems, one rolling table.', tagline: "Have dice, will roll.",
blurb: 'Long-running D&D and Daggerheart campaigns on Foundry VTT, plus the Crows playtest. Worldbuilding, NPC relationships, and player-driven narrative.', blurb:
image: '/images/ttrpg-dice.jpg', "Card Games, Board Games, and Long-running D&D and Daggerheart campaigns on Foundry VTT. Worldbuilding, NPC relationships, and player-driven narrative.",
alt: 'Polyhedral dice on a dark surface', image: "/images/ttrpg-dice.jpg",
alt: "Boardgame Shelf",
}, },
{ {
href: '/interests/gaming', href: "/interests/gaming",
icon: 'sony-playstation', icon: "sony-playstation",
title: 'Gaming', title: "Gaming",
tagline: 'If it has a screen or a controller, I have probably played it.', tagline: "We do not talk about my backlog.",
blurb: 'PlayStation 5, Nintendo Switch 2, and a politely out-of-hand Steam library. RPGs, strategy, and anything with a deeply layered world.', blurb:
image: '/images/gaming-setup.jpg', "PlayStation 5, Nintendo Switch 2, and a politely out-of-hand Steam library. RPGs, strategy, and anything with a deeply layered world.",
alt: 'Gaming setup with controller', image: "/images/gaming-setup.jpg",
alt: "Gaming setup desk",
}, },
{ {
href: '/interests/reading', href: "/interests/reading",
icon: 'book-open-page-variant', icon: "book-open-page-variant",
title: 'Reading & Media', title: "Reading & Media",
tagline: 'Perpetually three books ahead of my to-read list.', tagline: "Perpetually three books ahead of my to-read list.",
blurb: 'The Cosmere, Dune, the Pendergast saga, and Throne of Glass. Plus light novels, manga, manhwa, anime, and film.', blurb:
image: '/images/fantasy-books.jpg', "The Cosmere, Dune, the Pendergast saga, and Throne of Glass. Plus light novels, manga, manhwa, anime, and film.",
alt: 'Fantasy bookshelf', image: "/images/fantasy-books.jpg",
alt: "Fantasy bookshelf",
}, },
{ {
href: '/interests/making', href: "/interests/making",
icon: 'palette', icon: "palette",
title: 'Making & Creating', title: "Making & Creating",
tagline: 'If it involves layers and precision, I am in.', tagline:
blurb: 'Resin dice in a pressure pot, miniatures and terrain on the Elegoo Mars 4 and Bambu Labs P1S. Much of it ends up on the virtual table.', "If it involves really nice cardboard or mastering a new craft, I am in.",
image: '/images/making-resin.jpg', blurb:
alt: 'Resin crafting in progress', "Resin dice in a pressure pot, fun and functional prints on the P1s, custom TCG proxies and decks. Passion and utility.",
}, image: "/images/making-resin.jpg",
alt: "Polyhedral dice on a dark bag",
} /*
{ {
href: '/interests/pokemon', href: '/interests/pokemon',
icon: 'pokeball', icon: 'pokeball',
@@ -59,7 +64,7 @@ const interests = [
blurb: 'A lifelong obsession from Red and Blue to modern competitive. Steel/Psychic, beautiful design, absolutely devastating in battle.', blurb: 'A lifelong obsession from Red and Blue to modern competitive. Steel/Psychic, beautiful design, absolutely devastating in battle.',
image: '/images/pokemon-cards.jpg', image: '/images/pokemon-cards.jpg',
alt: 'Trading card collection', alt: 'Trading card collection',
}, },*/,
]; ];
--- ---
@@ -70,9 +75,13 @@ const interests = [
> >
<!-- Hero --> <!-- Hero -->
<section class="relative min-h-[60vh] overflow-hidden pt-28 sm:pt-32"> <section class="relative min-h-[60vh] overflow-hidden pt-28 sm:pt-32">
{/* Full animated Milky Way field + stars, matching the Home hero treatment. */} {
/* Full animated Milky Way field + stars, matching the Home hero treatment. */
}
<AnimatedBackground client:idle transitionName="sky" /> <AnimatedBackground client:idle transitionName="sky" />
<div class="container-prose relative z-10 flex flex-col items-start gap-6 py-16"> <div
class="container-prose relative z-10 flex flex-col items-start gap-6 py-16"
>
<Reveal> <Reveal>
<SectionHeader <SectionHeader
kicker="The personal wing" kicker="The personal wing"
@@ -94,38 +103,59 @@ const interests = [
/> />
<div class="container-prose relative z-10"> <div class="container-prose relative z-10">
<div class="grid gap-5 md:grid-cols-2 lg:grid-cols-3"> <div class="grid gap-5 md:grid-cols-2 lg:grid-cols-3">
{interests.map((item, i) => ( {
<Reveal delay={i * 80}> interests.map((item, i) => (
<a href={item.href} class="card card-interactive group flex h-full flex-col gap-4 overflow-hidden p-0"> <Reveal delay={i * 80}>
<div class="aspect-[16/10] w-full overflow-hidden"> <a
<img href={item.href}
src={item.image} class="card card-interactive group flex h-full flex-col gap-4 overflow-hidden p-0"
alt={item.alt} >
loading="lazy" <div class="aspect-[16/10] w-full overflow-hidden">
decoding="async" <img
class="h-full w-full object-cover transition-transform duration-700 group-hover:scale-105" src={item.image}
/> alt={item.alt}
</div> loading="lazy"
<div class="flex flex-col gap-3 px-5 pb-5"> decoding="async"
<div class="flex items-center justify-between"> class="h-full w-full object-cover transition-transform duration-700 group-hover:scale-105"
<span />
class="inline-flex h-12 w-12 items-center justify-center rounded-xl" </div>
style="background: color-mix(in srgb, var(--color-accent) 16%, transparent); color: var(--color-accent);" <div class="flex flex-col gap-3 px-5 pb-5">
aria-hidden="true" <div class="flex items-center justify-between">
<span
class="inline-flex h-12 w-12 items-center justify-center rounded-xl"
style="background: color-mix(in srgb, var(--color-accent) 16%, transparent); color: var(--color-accent);"
aria-hidden="true"
>
<Icon name={item.icon} class="h-6 w-6" />
</span>
<Icon
name="arrow-right"
class="h-5 w-5 transition-transform group-hover:translate-x-1"
style="color: var(--color-accent);"
/>
</div>
<div class="flex flex-col gap-2">
<h3 class="font-sans" style="color: var(--color-text);">
{item.title}
</h3>
<p
class="font-sans text-sm italic"
style="color: var(--color-secondary);"
>
{item.tagline}
</p>
</div>
<p
class="grow text-sm leading-relaxed"
style="color: var(--color-text-muted);"
> >
<Icon name={item.icon} class="h-6 w-6" /> {item.blurb}
</span> </p>
<Icon name="arrow-right" class="h-5 w-5 transition-transform group-hover:translate-x-1" style="color: var(--color-accent);" />
</div> </div>
<div class="flex flex-col gap-2"> </a>
<h3 class="font-sans" style="color: var(--color-text);">{item.title}</h3> </Reveal>
<p class="font-sans text-sm italic" style="color: var(--color-secondary);">{item.tagline}</p> ))
</div> }
<p class="grow text-sm leading-relaxed" style="color: var(--color-text-muted);">{item.blurb}</p>
</div>
</a>
</Reveal>
))}
{/* Cross-link back to professional wing */} {/* Cross-link back to professional wing */}
<Reveal delay={interests.length * 80}> <Reveal delay={interests.length * 80}>
@@ -134,9 +164,20 @@ const interests = [
class="card card-interactive group flex h-full flex-col justify-center gap-2 border-dashed p-5 text-center" class="card card-interactive group flex h-full flex-col justify-center gap-2 border-dashed p-5 text-center"
style="border-color: color-mix(in srgb, var(--color-accent) 40%, transparent);" style="border-color: color-mix(in srgb, var(--color-accent) 40%, transparent);"
> >
<Icon name="briefcase" class="mx-auto h-7 w-7" style="color: var(--color-accent);" /> <Icon
<p class="font-sans font-semibold" style="color: var(--color-text);">The other wing</p> name="briefcase"
<p class="text-sm" style="color: var(--color-text-muted);">See where curiosity meets the day job.</p> class="mx-auto h-7 w-7"
style="color: var(--color-accent);"
/>
<p
class="font-sans font-semibold"
style="color: var(--color-text);"
>
The other wing
</p>
<p class="text-sm" style="color: var(--color-text-muted);">
See where curiosity meets the day job.
</p>
</a> </a>
</Reveal> </Reveal>
</div> </div>
+3 -7
View File
@@ -55,8 +55,8 @@ const projects = (await getCollection("projects")).sort(
<SectionHeader <SectionHeader
kicker="Professional" kicker="Professional"
icon="briefcase" icon="briefcase"
title="Resume & portfolio" title="Resume & Portfolio"
subtitle="Clean, confident, skimmable. Browsable here, screenshot-able for a recruiter, printable if needed." subtitle="Clean, confident, skimmable."
/> />
</Reveal> </Reveal>
<Reveal delay={80}> <Reveal delay={80}>
@@ -152,7 +152,7 @@ const projects = (await getCollection("projects")).sort(
kicker="04" kicker="04"
icon="view-grid" icon="view-grid"
title="Projects" title="Projects"
subtitle="What I've built. Replace the placeholder with 3-6 of your own." subtitle="What I've built. [TODO: Replace the placeholder with 3-6 of my projects]"
/> />
</Reveal> </Reveal>
<div class="grid gap-5 sm:grid-cols-2"> <div class="grid gap-5 sm:grid-cols-2">
@@ -164,10 +164,6 @@ const projects = (await getCollection("projects")).sort(
)) ))
} }
</div> </div>
<Placeholder
label="Add 3-6 projects"
hint="Drop a new markdown file in src/content/projects/ for each notable project. See docs/CONTENT.md."
/>
</section> </section>
{/* ===== 5. Education ===== */} {/* ===== 5. Education ===== */}