Copy update and some formatting

This commit is contained in:
Greg Kilmer
2026-07-04 12:51:42 -04:00
parent 29b4a13345
commit c298562266
9 changed files with 545 additions and 317 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 KiB

After

Width:  |  Height:  |  Size: 802 KiB

+31 -11
View File
@@ -9,7 +9,7 @@
* Left -> Professional (polished tone)
* Right -> Interests (warm tone)
*/
import Icon from '@components/Icon.astro';
import Icon from "@components/Icon.astro";
interface Props {
/** Optional override copy for each side. Defaults to spec phrasing. */
@@ -20,10 +20,10 @@ interface Props {
}
const {
leftHref = '/professional',
rightHref = '/interests',
leftText = 'See where I apply that curiosity professionally',
rightText = 'See where it takes me outside of work',
leftHref = "/professional",
rightHref = "/interests",
leftText = "See where I apply that curiosity professionally",
rightText = "See where it takes me outside of work",
} = Astro.props;
---
@@ -32,15 +32,25 @@ const {
href={leftHref}
class="card card-interactive group flex items-center justify-between gap-4"
>
<Icon
name="arrow-left"
class="h-6 w-6 shrink-0 transition-transform group-hover:translate-x-1"
style="color: var(--color-accent);"
/>
<span class="flex flex-col gap-1">
<span class="font-mono text-xs uppercase tracking-[0.18em]" style="color: var(--color-text-subtle);">
<span
class="font-mono text-xs uppercase tracking-[0.18em]"
style="color: var(--color-text-subtle); text-align:right;"
>
The professional wing
</span>
<span class="font-sans text-lg font-semibold" style="color: var(--color-text);">
<span
class="font-sans text-lg font-semibold"
style="color: var(--color-text);"
>
{leftText}
</span>
</span>
<Icon name="arrow-right" class="h-6 w-6 shrink-0 transition-transform group-hover:translate-x-1" style="color: var(--color-accent);" />
</a>
<a
@@ -48,13 +58,23 @@ const {
class="card card-interactive group flex items-center justify-between gap-4"
>
<span class="flex flex-col gap-1">
<span class="font-mono text-xs uppercase tracking-[0.18em]" style="color: var(--color-text-subtle);">
<span
class="font-mono text-xs uppercase tracking-[0.18em]"
style="color: var(--color-text-subtle);"
>
The personal wing
</span>
<span class="font-sans text-lg font-semibold" style="color: var(--color-text);">
<span
class="font-sans text-lg font-semibold"
style="color: var(--color-text);"
>
{rightText}
</span>
</span>
<Icon name="arrow-right" class="h-6 w-6 shrink-0 transition-transform group-hover:translate-x-1" style="color: var(--color-accent);" />
<Icon
name="arrow-right"
class="h-6 w-6 shrink-0 transition-transform group-hover:translate-x-1"
style="color: var(--color-accent);"
/>
</a>
</div>
+20 -22
View File
@@ -17,67 +17,65 @@
* </CelestialFrame>
* <CelestialFrame rounded="none"> ... </CelestialFrame>
*/
import StarBurst from './StarBurst.astro';
import StarBurst from "./StarBurst.astro";
interface Props {
class?: string;
/** Corner rounding. */
rounded?: 'lg' | 'xl' | '2xl' | 'none';
rounded?: "lg" | "xl" | "2xl" | "none";
/** Stroke emphasis on the frame border. */
emphasis?: 'subtle' | 'strong';
emphasis?: "subtle" | "strong";
}
const {
class: className = '',
rounded = '2xl',
emphasis = 'subtle',
class: className = "",
rounded = "2xl",
emphasis = "subtle",
} = Astro.props;
const radius = {
none: 'rounded-none',
lg: 'rounded-lg',
xl: 'rounded-xl',
'2xl': 'rounded-2xl',
none: "rounded-none",
lg: "rounded-lg",
xl: "rounded-xl",
"2xl": "rounded-2xl",
}[rounded];
const borderStyle =
emphasis === 'strong'
? 'border-2'
: 'border';
const borderStyle = emphasis === "strong" ? "border-2" : "border";
---
<figure
class:list={['relative', radius, borderStyle, className]}
style="border-color: color-mix(in srgb, var(--color-accent) 45%, transparent);"
class:list={["relative", radius, borderStyle, className]}
style="border-color: color-mix(in srgb, var(--color-accent) 75%, transparent); border-width: 3px;"
>
{/* Faint sacred-geometry tint behind the image. */}
<div
class:list={['absolute inset-0', radius]}
class:list={["absolute inset-0", radius]}
style="background: color-mix(in srgb, var(--color-violet, var(--color-accent)) 14%, var(--color-surface-sunken));"
aria-hidden="true"
>
</div>
{/* The media itself, clipped to the frame. */}
<div class:list={['relative overflow-hidden', radius]}>
<div class:list={["relative overflow-hidden", radius]}>
<slot />
</div>
{/* Corner star-burst flourishes (sit on the border corners). */}
<StarBurst
class="pointer-events-none absolute -left-2 -top-2 h-5 w-5 drop-shadow-[0_0_6px_color-mix(in_srgb,var(--color-accent)_60%,transparent)]"
class="pointer-events-none absolute -left-3 -top-3 h-6 w-6 drop-shadow-[0_0_6px_color-mix(in_srgb,var(--color-accent)_60%,transparent)]"
style="color: var(--color-accent);"
/>
<StarBurst
class="pointer-events-none absolute -bottom-2 -right-2 h-5 w-5 drop-shadow-[0_0_6px_color-mix(in_srgb,var(--color-accent)_60%,transparent)]"
class="pointer-events-none absolute -bottom-5 -right-5 h-7 w-7 drop-shadow-[0_0_6px_color-mix(in_srgb,var(--color-accent)_60%,transparent)]"
style="color: var(--color-accent);"
/>
<StarBurst
variant="outline"
class="pointer-events-none absolute -right-1 -top-1 h-3 w-3"
class="pointer-events-none absolute -right-2 -top-2 h-3 w-3"
style="color: var(--color-accent); opacity:.7;"
/>
<StarBurst
variant="outline"
class="pointer-events-none absolute -left-1 -bottom-1 h-3 w-3"
class="pointer-events-none absolute -left-2 -bottom-2 h-3 w-3"
style="color: var(--color-accent); opacity:.7;"
/>
</figure>
+21 -14
View File
@@ -14,8 +14,8 @@
* <CelestialImage src="/images/hero.jpg" alt="Greg at his desk" aspect="4/3" />
* <CelestialImage src="/images/about.jpg" alt="Greg" aspect="3/4" rounded="xl" />
*/
import CelestialFrame from './CelestialFrame.astro';
import StarField from './StarField.astro';
import CelestialFrame from "./CelestialFrame.astro";
import StarField from "./StarField.astro";
interface Props {
src?: string;
@@ -23,8 +23,8 @@ interface Props {
/** Aspect ratio, e.g. "4/3", "3/4", "16/9". */
aspect?: string;
/** Image fit. object-cover by default. */
fit?: 'cover' | 'contain';
rounded?: 'lg' | 'xl' | '2xl' | 'none';
fit?: "cover" | "contain";
rounded?: "lg" | "xl" | "2xl" | "none";
class?: string;
/** When true, render only the frame + placeholder (no <img>). */
placeholder?: boolean;
@@ -33,19 +33,21 @@ interface Props {
const {
src,
alt,
aspect = '4/3',
fit = 'cover',
rounded = '2xl',
class: className = '',
aspect = "4/3",
fit = "cover",
rounded = "2xl",
class: className = "",
placeholder = false,
} = Astro.props;
const showPlaceholder = placeholder || !src;
const objectFit = fit === 'contain' ? 'object-contain' : 'object-cover';
const objectFit = fit === "contain" ? "object-contain" : "object-cover";
---
<CelestialFrame rounded={rounded} class:list={['w-full', className]}>
<CelestialFrame rounded={rounded} class:list={["w-full", className]}>
<div class="w-full" style={`aspect-ratio: ${aspect};`}>
{showPlaceholder ? (
{
showPlaceholder ? (
// Themed SVG placeholder: starfield + moon glyph over the surface tone.
<div
class="relative h-full w-full"
@@ -70,7 +72,11 @@ const objectFit = fit === 'contain' ? 'object-contain' : 'object-cover';
>
{/* Crescent moon + small star as the placeholder glyph. */}
<path d="M16 12a7 7 0 1 1-6.4-7 5.5 5.5 0 0 0 0 14A7 7 0 0 0 16 12z" />
<path d="M18.5 5.5c.3 1.8 1 3.1 2.5 3.5-1.5.3-2.2 1.7-2.5 3.5-.3-1.8-1-3.2-2.5-3.5 1.5-.4 2.2-1.7 2.5-3.5z" fill="currentColor" stroke="none" />
<path
d="M18.5 5.5c.3 1.8 1 3.1 2.5 3.5-1.5.3-2.2 1.7-2.5 3.5-.3-1.8-1-3.2-2.5-3.5 1.5-.4 2.2-1.7 2.5-3.5z"
fill="currentColor"
stroke="none"
/>
</svg>
</div>
<span
@@ -86,8 +92,9 @@ const objectFit = fit === 'contain' ? 'object-contain' : 'object-cover';
alt={alt}
loading="lazy"
decoding="async"
class:list={['h-full w-full', objectFit]}
class:list={["h-full w-full", objectFit]}
/>
)}
)
}
</div>
</CelestialFrame>
+6 -6
View File
@@ -63,22 +63,22 @@ export const allNavItems: NavItem[] = navGroups.flatMap((g) => g.items);
export const socials = {
github: {
label: 'GitHub',
handle: '[Greg to fill]',
href: 'https://github.com/',
handle: 'Patiun',
href: 'https://github.com/patiun',
icon: 'github',
},
linkedin: {
label: 'LinkedIn',
handle: '[Greg to fill]',
href: 'https://www.linkedin.com/',
handle: 'Gregory Kilmer',
href: 'https://www.linkedin.com/in/gregory-kilmer-36646bb5/',
icon: 'linkedin',
},
discord: {
/*discord: {
label: 'Discord',
handle: '[Greg to fill]',
href: '#',
icon: 'discord',
},
},*/
} as const;
/** Spec footer microcopy. */
+67 -38
View File
@@ -7,23 +7,29 @@
* quick facts (spec emojis mapped to mdi icons). Ends with the SplitTransition
* fork into Professional / Interests.
*/
import BaseLayout from '@layouts/BaseLayout.astro';
import Reveal from '@components/motion/Reveal.astro';
import Icon from '@components/Icon.astro';
import SectionHeader from '@components/layout/SectionHeader.astro';
import SplitTransition from '@components/layout/SplitTransition.astro';
import CelestialImage from '@components/mystic/CelestialImage.astro';
import AlchemicalDivider from '@components/mystic/AlchemicalDivider.astro';
import ConstellationField from '@components/mystic/ConstellationField.tsx';
import BaseLayout from "@layouts/BaseLayout.astro";
import Reveal from "@components/motion/Reveal.astro";
import Icon from "@components/Icon.astro";
import SectionHeader from "@components/layout/SectionHeader.astro";
import SplitTransition from "@components/layout/SplitTransition.astro";
import CelestialImage from "@components/mystic/CelestialImage.astro";
import AlchemicalDivider from "@components/mystic/AlchemicalDivider.astro";
import ConstellationField from "@components/mystic/ConstellationField.tsx";
// Spec "Quick Facts", emojis -> mdi icons.
const quickFacts = [
{ icon: 'map-marker', text: 'Philadelphia, PA' },
{ icon: 'briefcase', text: 'Software Developer at Implico Inc.' },
{ icon: 'dog', text: 'Dog dad to Kashi the mini dachshund' },
{ icon: 'dice-multiple', text: 'Running D&D & Daggerheart campaigns' },
{ icon: 'book-open-variant', text: 'Currently reading: Heir of Fire by Sarah J. Maas' },
{ icon: 'tea', text: 'Fueled by loose-leaf Chinese tea and Mountain Dew' },
{ icon: "map-marker", text: "Philadelphia, PA" },
{
icon: "briefcase",
text: "BTP, Cloud, and Mobile App Lead at Implico Inc.",
},
{ icon: "dog", text: "Dog dad to Kashi the mini dachshund" },
{ icon: "dice-multiple", text: "Running D&D & Daggerheart campaigns" },
{
icon: "book-open-variant",
text: "Currently reading: Heir of Fire by Sarah J. Maas",
},
{ icon: "tea", text: "Fueled by loose-leaf Chinese tea and Mountain Dew" },
];
---
@@ -33,7 +39,12 @@ const quickFacts = [
>
<!-- Header -->
<section class="relative overflow-hidden pb-10 pt-28 sm:pt-32">
<ConstellationField client:idle className="opacity-40" tint="violet" transitionName="sky" />
<ConstellationField
client:idle
className="opacity-40"
tint="violet"
transitionName="sky"
/>
<div class="container-prose relative z-10 flex flex-col gap-10">
<Reveal>
<SectionHeader
@@ -53,10 +64,10 @@ const quickFacts = [
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);"
>
I am Greg &mdash; a software developer based in Philadelphia who spends
his free time running tabletop RPGs, making resin dice, 3D printing
miniatures and props, and reading his way through fantasy series at an
alarming rate. I live with my partner Krystina, our mini dachshund
I am Greg: a software developer based in Philadelphia who spends his
free time running tabletop RPGs, making resin dice and custom cards, 3D
printing anything I can, and reading his way through fantasy series at
an alarming rate. I live with my partner Krystina, our mini dachshund
Kashi, and more board games than any reasonable apartment should hold.
</p>
</Reveal>
@@ -66,22 +77,25 @@ const quickFacts = [
<section class="container-prose grid gap-10 py-10 lg:grid-cols-[1fr_18rem]">
<Reveal class="flex flex-col gap-5">
<h2>The longer arc</h2>
<div class="flex flex-col gap-5 text-lg leading-relaxed" style="color: var(--color-text-muted);">
<div
class="flex flex-col gap-5 text-lg leading-relaxed"
style="color: var(--color-text-muted);"
>
<p>
I grew up surrounded by books and games, and not much has changed.
Technology pulled me into a career building cloud and mobile
applications, but I never stopped collecting dice, designing
campaigns, or losing weekends to Sanderson rereads. I am currently
expanding into SAP's Business Technology Platform, exploring AI and
machine learning, and trying to convince my group that yes, we
<em>can</em> fit one more board game into game night.
I grew up loving books and games, and not much has changed. Technology
pulled me into a career building cloud and mobile applications, but I
never stopped collecting dice, designing campaigns, or losing weekends
to Sanderson reads. I am currently expanding into SAP's Business
Technology Platform, exploring AI and machine learning, and trying to
convince my group that yes, we
<em> can</em> fit one more board game into game night.
</p>
<p>
I believe the best work happens when curiosity isn't compartmentalized.
The same system-thinking that makes a good developer makes a good
gamemaster. The patience that produces a clean resin cast is the same
patience that debugs a stubborn deployment. This site is where I don't
have to pretend those things are separate.
I believe the best work happens when curiosity isn't
compartmentalized. The same system-thinking that makes a good
developer makes a good gamemaster. The patience that produces a clean
resin cast is the same patience that debugs a stubborn deployment.
This site is where I don't have to pretend those things are separate.
</p>
</div>
</Reveal>
@@ -97,16 +111,28 @@ const quickFacts = [
</Reveal>
<Reveal delay={120}>
<aside class="card flex flex-col gap-4 lg:sticky lg:top-24">
<h3 class="font-mono text-xs uppercase tracking-[0.22em]" style="color: var(--color-accent);">
<h3
class="font-mono text-xs uppercase tracking-[0.22em]"
style="color: var(--color-accent);"
>
Quick facts
</h3>
<ul class="flex flex-col gap-3">
{quickFacts.map((f) => (
<li class="flex items-start gap-3 text-sm" style="color: var(--color-text);">
<Icon name={f.icon} class="mt-0.5 h-5 w-5 shrink-0" style="color: var(--color-accent);" />
{
quickFacts.map((f) => (
<li
class="flex items-start gap-3 text-sm"
style="color: var(--color-text);"
>
<Icon
name={f.icon}
class="mt-0.5 h-5 w-5 shrink-0"
style="color: var(--color-accent);"
/>
<span>{f.text}</span>
</li>
))}
))
}
</ul>
</aside>
</Reveal>
@@ -115,7 +141,10 @@ const quickFacts = [
<Reveal>
<div class="container-prose">
<AlchemicalDivider class="mx-auto h-6 w-full max-w-sm" style="color: var(--color-accent);" />
<AlchemicalDivider
class="mx-auto h-6 w-full max-w-sm"
style="color: var(--color-accent);"
/>
</div>
</Reveal>
+85 -27
View File
@@ -10,24 +10,48 @@
* Spec items Greg still needs to fill (email, Discord, etc.) appear as
* Placeholder slots.
*/
import BaseLayout from '@layouts/BaseLayout.astro';
import Reveal from '@components/motion/Reveal.astro';
import Icon from '@components/Icon.astro';
import SectionHeader from '@components/layout/SectionHeader.astro';
import Placeholder from '@components/layout/Placeholder.astro';
import ContactForm from '@components/professional/ContactForm.tsx';
import BaseLayout from "@layouts/BaseLayout.astro";
import Reveal from "@components/motion/Reveal.astro";
import Icon from "@components/Icon.astro";
import SectionHeader from "@components/layout/SectionHeader.astro";
import Placeholder from "@components/layout/Placeholder.astro";
import ContactForm from "@components/professional/ContactForm.tsx";
const professional = [
{ icon: 'email-outline', label: 'Email', hint: 'Greg professional email address.' },
{ icon: 'linkedin', label: 'LinkedIn', hint: 'LinkedIn profile URL.' },
{ icon: 'github', label: 'GitHub', hint: 'GitHub profile URL.' },
{
icon: "email-outline",
label: "Email",
hint: '<a href="mailto:Kilmer.GM@Gmail.com">Kilmer.GM@Gmail.com</a>',
},
{
icon: "linkedin",
label: "LinkedIn",
hint: '<a href="https://www.linkedin.com/in/gregory-kilmer-36646bb5/" target="_blank" rel="noopener noreferrer">LinkedIn</a>',
},
{
icon: "github",
label: "GitHub",
hint: '<a href="https://github.com/patiun" target="_blank" rel="noopener noreferrer">GitHub</a>',
},
];
const social = [
{ icon: 'discord', label: 'Discord', hint: 'Handle or invite link.' },
{ icon: 'twitter', label: 'Twitter / X', hint: 'Profile link, if applicable.' },
{ icon: 'paw', label: 'Pokémon', hint: 'Showdown or Trainer name, if shared.' },
{ icon: 'cube-outline', label: 'Foundry VTT', hint: 'Server link, if public.' },
{ icon: "discord", label: "Discord", hint: "Handle or invite link." },
{
icon: "twitter",
label: "Twitter / X",
hint: "Profile link, if applicable.",
},
{
icon: "paw",
label: "Pokémon",
hint: "Showdown or Trainer name, if shared.",
},
{
icon: "cube-outline",
label: "Foundry VTT",
hint: "Server link, if public.",
},
];
---
@@ -52,11 +76,15 @@ const social = [
{/* Professional */}
<Reveal>
<section class="flex flex-col gap-4">
<h2 class="font-mono text-xs uppercase tracking-[0.22em]" style="color: var(--color-accent);">
<h2
class="font-mono text-xs uppercase tracking-[0.22em]"
style="color: var(--color-accent);"
>
Professional
</h2>
<ul class="flex flex-col gap-3">
{professional.map((item) => (
{
professional.map((item) => (
<li class="card flex items-center gap-3">
<span
class="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg"
@@ -66,24 +94,38 @@ const social = [
<Icon name={item.icon} class="h-5 w-5" />
</span>
<span class="flex flex-col gap-0.5">
<span class="font-sans text-sm font-semibold" style="color: var(--color-text);">{item.label}</span>
<span class="text-xs" style="color: var(--color-text-subtle);">{item.hint}</span>
<span
class="font-sans text-sm font-semibold"
style="color: var(--color-text);"
>
{item.label}
</span>
<span
class="text-xs"
style="color: var(--color-text-subtle);"
>
{item.hint}
</span>
</span>
</li>
))}
))
}
</ul>
<Placeholder label="Professional links" hint="Replace the slots above with your real email / LinkedIn / GitHub once ready." />
</section>
</Reveal>
{/* Social / personal */}
<Reveal delay={80}>
<section class="flex flex-col gap-4">
<h2 class="font-mono text-xs uppercase tracking-[0.22em]" style="color: var(--color-secondary);">
<h2
class="font-mono text-xs uppercase tracking-[0.22em]"
style="color: var(--color-secondary);"
>
Personal / Social
</h2>
<ul class="grid gap-3 sm:grid-cols-2">
{social.map((item) => (
{
social.map((item) => (
<li class="card flex items-center gap-3">
<span
class="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg"
@@ -93,11 +135,22 @@ const social = [
<Icon name={item.icon} class="h-5 w-5" />
</span>
<span class="flex flex-col gap-0.5">
<span class="font-sans text-sm font-semibold" style="color: var(--color-text);">{item.label}</span>
<span class="text-xs" style="color: var(--color-text-subtle);">{item.hint}</span>
<span
class="font-sans text-sm font-semibold"
style="color: var(--color-text);"
>
{item.label}
</span>
<span
class="text-xs"
style="color: var(--color-text-subtle);"
>
{item.hint}
</span>
</span>
</li>
))}
))
}
</ul>
</section>
</Reveal>
@@ -108,7 +161,9 @@ const social = [
class="rounded-xl border-l-2 p-4 text-sm"
style="border-color: var(--color-accent); background: color-mix(in srgb, var(--color-accent) 8%, transparent); color: var(--color-text-muted);"
>
<strong style="color: var(--color-text);">Availability:</strong> Open to opportunities in cloud development, SAP BTP, and technical leadership.
<strong style="color: var(--color-text);">Availability:</strong> Open to
opportunities in AI/ML, app and cloud development, SAP BTP, and technical
leadership.
</p>
</Reveal>
</div>
@@ -117,7 +172,9 @@ const social = [
<Reveal delay={100}>
<section class="card flex flex-col gap-5">
<div class="flex flex-col gap-1">
<h2 class="font-sans" style="color: var(--color-text);">Send a message</h2>
<h2 class="font-sans" style="color: var(--color-text);">
Send a message
</h2>
<p class="text-sm" style="color: var(--color-text-muted);">
Prefer a form? Drop a note and it will land in my inbox.
</p>
@@ -134,7 +191,8 @@ const social = [
class="mx-auto max-w-2xl border-l-2 pl-5 text-center font-sans text-lg italic"
style="border-color: var(--color-accent); color: var(--color-text-muted);"
>
Whether you want to talk cloud architecture, debate the best Gundam series, or need a fourth for game night, reach out.
Whether you want to talk cloud architecture, debate the best Gundam
series, or need a fourth for game night, reach out.
</blockquote>
</Reveal>
</section>
+152 -86
View File
@@ -1,84 +1,94 @@
---
/**
* index.astro — Home (Landing)
* ---------------------------------------------------------------------------
* First impression page. Full-width hero with the animated background, the spec
* headline/subheadline/intro paragraph, and the two CTA buttons. Below the fold
* is the 3-column snapshot grid (Professional, Tabletop, Making) plus the wider
* Reading & Media strip. All copy is verbatim from the spec.
*
* Emojis from the spec table are rendered as mdi icons (no emojis per spec).
*/
import BaseLayout from '@layouts/BaseLayout.astro';
import Reveal from '@components/motion/Reveal.astro';
import AnimatedBackground from '@components/hero/AnimatedBackground.tsx';
import Icon from '@components/Icon.astro';
import SectionHeader from '@components/layout/SectionHeader.astro';
import CelestialImage from '@components/mystic/CelestialImage.astro';
import AlchemicalDivider from '@components/mystic/AlchemicalDivider.astro';
import SacredGeometry from '@components/mystic/SacredGeometry.astro';
import ConstellationField from '@components/mystic/ConstellationField.tsx';
* index.astro — Home (Landing)
* ---------------------------------------------------------------------------
* First impression page. Full-width hero with the animated background, the spec
* headline/subheadline/intro paragraph, and the two CTA buttons. Below the fold
* is the 3-column snapshot grid (Professional, Tabletop, Making) plus the wider
* Reading & Media strip. All copy is verbatim from the spec.
*
* Emojis from the spec table are rendered as mdi icons (no emojis per spec).
*/
import BaseLayout from "@layouts/BaseLayout.astro";
import Reveal from "@components/motion/Reveal.astro";
import AnimatedBackground from "@components/hero/AnimatedBackground.tsx";
import Icon from "@components/Icon.astro";
import SectionHeader from "@components/layout/SectionHeader.astro";
import CelestialImage from "@components/mystic/CelestialImage.astro";
import AlchemicalDivider from "@components/mystic/AlchemicalDivider.astro";
import SacredGeometry from "@components/mystic/SacredGeometry.astro";
import ConstellationField from "@components/mystic/ConstellationField.tsx";
// Snapshot grid content from the spec, with spec emojis mapped to mdi icons.
// Each card now carries a vibe-matched stock image (swap for real photos later).
const cards = [
{
icon: 'briefcase',
title: 'Professional',
copy: 'From SAP-adjacent cloud apps to full-stack mobile — a decade of building software that businesses actually use.',
href: '/professional',
image: '/images/pro-workspace.jpg',
alt: 'Developer workspace with code on screen',
},
{
icon: 'dice-multiple',
title: 'Tabletop',
copy: "Currently running two campaigns in Foundry VTT and playtesting MCDM's Crows. Always rolling dice.",
href: '/interests/ttrpgs',
image: '/images/ttrpg-dice.jpg',
alt: 'Polyhedral dice on a dark surface',
},
{
icon: 'palette',
title: 'Making',
copy: 'Resin dice, 3D prints, and whatever else catches my eye. If it involves layers and precision, I am in.',
href: '/interests/making',
image: '/images/making-resin.jpg',
alt: 'Resin crafting in progress',
},
{
icon: "briefcase",
title: "Professional",
copy: "From SAP-adjacent cloud apps to full-stack mobile — a decade of building software that businesses actually use.",
href: "/professional",
image: "/images/pro-workspace.jpg",
alt: "Developer workspace with code on screen",
},
{
icon: "dice-multiple",
title: "Tabletop",
copy: "Currently running two campaigns in Foundry VTT and playtesting MCDM's Crows. Always rolling dice.",
href: "/interests/ttrpgs",
image: "/images/ttrpg-dice.jpg",
alt: "Polyhedral dice on a dark surface",
},
{
icon: "palette",
title: "Making",
copy: "Resin dice, 3D prints, and whatever else catches my eye. If it involves layers and precision, I am in.",
href: "/interests/making",
image: "/images/making-resin.jpg",
alt: "Resin crafting in progress",
},
];
---
<BaseLayout>
<!-- ===================== HERO ===================== -->
<section class="vt-hero-sky relative flex min-h-[88vh] items-center overflow-hidden pt-16">
<section
class="vt-hero-sky relative flex min-h-[88vh] items-center overflow-hidden pt-16"
>
<AnimatedBackground client:idle transitionName="sky" />
<div class="container-prose relative z-10 flex flex-col items-start gap-8 py-20">
<div
class="container-prose relative z-10 flex flex-col items-start gap-8 py-20"
>
<div class="flex w-full flex-col gap-10 lg:flex-row lg:items-center">
<Reveal as="div" class="max-w-2xl grow">
<span
class="mb-5 inline-flex items-center gap-2 rounded-full border px-3 py-1 font-mono text-xs uppercase tracking-[0.18em]"
style="border-color: var(--color-border); color: var(--color-text-muted); background: color-mix(in srgb, var(--color-surface-raised) 60%, transparent);">
<span class="h-2 w-2 rounded-full animate-pulse-glow" style="background: var(--color-accent);"></span>
style="border-color: var(--color-border); color: var(--color-text-muted); background: color-mix(in srgb, var(--color-surface-raised) 60%, transparent);"
>
<span
class="h-2 w-2 rounded-full animate-pulse-glow"
style="background: var(--color-accent);"></span>
Developer. Maker. Storyteller.
</span>
<h1 class="text-balance" style="color: var(--text);">
Greg Kilmer
</h1>
<h1 class="text-balance" style="color: var(--text);">Greg Kilmer</h1>
<p class="mt-3 font-sans text-xl font-medium sm:text-2xl" style="color: var(--color-accent);">
<p
class="mt-3 font-sans text-xl font-medium sm:text-2xl"
style="color: var(--color-accent);"
>
Software Developer &middot; Maker &middot; Tabletop Gamemaster
</p>
<blockquote class="mt-7 max-w-2xl border-l-2 pl-5 text-lg leading-relaxed"
style="border-color: var(--color-accent); color: var(--text-muted);">
<blockquote
class="mt-7 max-w-2xl border-l-2 pl-5 text-lg leading-relaxed"
style="border-color: var(--color-accent); color: var(--text-muted);"
>
I build things with code during the day and with resin, plastic, and
imagination at night. I lead cloud and mobile development at Implico,
run D&D campaigns that span years, and fill my shelves with Brandon
Sanderson novels, custom dice, and board games. This is the place
where all of those worlds meet.
imagination at night. I lead cloud and mobile development at
Implico, run D&D campaigns that span years, and fill my shelves with
Brandon Sanderson novels, custom dice, and board games. This is the
place where all of those worlds meet.
</blockquote>
<div class="mt-9 flex flex-wrap gap-4">
@@ -93,87 +103,143 @@ alt: 'Resin crafting in progress',
</div>
</Reveal>
{/* Hero portrait — framed in celestial ornament. Swap for a real photo of Greg. */}
{/* Hero portrait — framed in celestial ornament. */}
<Reveal as="div" delay={120} class="w-full max-w-sm shrink-0 lg:w-80">
<CelestialImage src="/images/hero-portrait.jpg" alt="Greg at his desk" aspect="4/5" />
<CelestialImage
src="/images/hero-portrait.jpg"
alt="Greg at his desk"
aspect="4/5"
/>
</Reveal>
</div>
</div>
<!-- Scroll cue -->
<div class="pointer-events-none absolute bottom-6 left-1/2 -translate-x-1/2 animate-float"
style="color: var(--color-text-subtle);" aria-hidden="true">
<div
class="pointer-events-none absolute bottom-6 left-1/2 -translate-x-1/2 animate-float"
style="color: var(--color-text-subtle);"
aria-hidden="true"
>
<Icon name="chevron-down" class="h-7 w-7" />
</div>
</section>
<!-- ===================== SNAPSHOT GRID ===================== -->
<section class="relative overflow-hidden py-20 sm:py-28">
{/* Slow-rotating constellation field behind the section — a slice of sky. */}
{
/* Slow-rotating constellation field behind the section — a slice of sky. */
}
<ConstellationField client:idle className="opacity-60" />
{/* Faint sacred-geometry ornament behind the section. */}
<SacredGeometry figure="flower" class="pointer-events-none absolute -right-40 top-10 z-0 h-[40rem] w-[40rem]"
style="color: var(--color-violet); opacity:.08;" />
<SacredGeometry
figure="flower"
class="pointer-events-none absolute -right-40 top-10 z-0 h-[40rem] w-[40rem]"
style="color: var(--color-violet); opacity:.08;"
/>
<div class="container-prose relative z-10 flex flex-col gap-12">
<Reveal>
<SectionHeader kicker="The two wings" icon="home-heart" title="A snapshot"
subtitle="One house, two wings. Polished and precise on the professional side; warm and inviting on the personal side." />
<SectionHeader
kicker="The two wings"
icon="home-heart"
title="A snapshot"
subtitle="One house, two wings. Polished and precise on the professional side; warm and inviting on the personal side."
/>
</Reveal>
<div class="grid gap-5 md:grid-cols-3">
{cards.map((card, i) => (
{
cards.map((card, i) => (
<Reveal delay={i * 90}>
<a href={card.href} class="card card-interactive group flex h-full flex-col gap-4 overflow-hidden p-0">
<a
href={card.href}
class="card card-interactive group flex h-full flex-col gap-4 overflow-hidden p-0"
>
<div class="aspect-[16/10] w-full overflow-hidden">
<img src={card.image} alt={card.alt} loading="lazy" decoding="async"
class="h-full w-full object-cover transition-transform duration-700 group-hover:scale-105" />
<img
src={card.image}
alt={card.alt}
loading="lazy"
decoding="async"
class="h-full w-full object-cover transition-transform duration-700 group-hover:scale-105"
/>
</div>
<div class="flex flex-col gap-4 px-5 pb-5">
<span class="inline-flex h-12 w-12 items-center justify-center rounded-xl"
<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">
aria-hidden="true"
>
<Icon name={card.icon} class="h-6 w-6" />
</span>
<h3 class="font-sans" style="color: var(--color-text);">{card.title}</h3>
<p class="grow text-sm leading-relaxed" style="color: var(--color-text-muted);">
<h3 class="font-sans" style="color: var(--color-text);">
{card.title}
</h3>
<p
class="grow text-sm leading-relaxed"
style="color: var(--color-text-muted);"
>
{card.copy}
</p>
<span
class="mt-1 inline-flex items-center gap-1 font-sans text-sm font-semibold transition-transform group-hover:translate-x-1"
style="color: var(--color-accent);">
style="color: var(--color-accent);"
>
Learn more
<Icon name="arrow-right" class="h-4 w-4" />
</span>
</div>
</a>
</Reveal>
))}
))
}
</div>
<Reveal>
<AlchemicalDivider class="mx-auto h-6 w-full max-w-sm" style="color: var(--color-accent);" />
<AlchemicalDivider
class="mx-auto h-6 w-full max-w-sm"
style="color: var(--color-accent);"
/>
</Reveal>
{/* Wider strip: Reading & Media */}
<Reveal delay={120}>
<a href="/interests/reading"
class="card card-interactive group flex flex-col gap-4 overflow-hidden p-0 sm:flex-row sm:items-center sm:justify-between">
<a
href="/interests/reading"
class="card card-interactive group flex flex-col gap-4 overflow-hidden p-0 sm:flex-row sm:items-center sm:justify-between"
>
<span class="flex items-center gap-4">
<span class="hidden aspect-square w-32 shrink-0 overflow-hidden sm:block">
<img src="/images/fantasy-books.jpg" alt="Fantasy bookshelf" loading="lazy" decoding="async"
class="h-full w-full object-cover" />
<span
class="hidden aspect-square w-32 shrink-0 overflow-hidden sm:block"
>
<img
src="/images/fantasy-books.jpg"
alt="Fantasy bookshelf"
loading="lazy"
decoding="async"
class="h-full w-full object-cover"
/>
</span>
<span class="flex flex-col gap-1 px-5 py-4 sm:px-0 sm:py-0">
<span class="font-mono text-xs uppercase tracking-[0.18em]" style="color: var(--color-text-subtle);">
<span
class="font-mono text-xs uppercase tracking-[0.18em]"
style="color: var(--color-text-subtle);"
>
Reading &amp; Media
</span>
<span class="font-sans text-lg font-semibold" style="color: var(--color-text);">
Deep in the Cosmere, catching up on anime, and always three books ahead on my to-read list.
<span
class="font-sans text-lg font-semibold"
style="color: var(--color-text);"
>
Deep in the Cosmere, catching up on anime, and always three
books ahead on my to-read list.
</span>
</span>
</span>
<Icon name="arrow-right" class="h-6 w-6 shrink-0 transition-transform group-hover:translate-x-1 sm:ml-4"
style="color: var(--color-accent);" />
<Icon
name="arrow-right"
class="h-6 w-6 shrink-0 transition-transform group-hover:translate-x-1 sm:ml-4"
style="color: var(--color-accent);"
/>
</a>
</Reveal>
</div>
+82 -32
View File
@@ -10,24 +10,24 @@
* `projects` collection. Both are sorted by `order`. Education is a placeholder
* block (spec leaves it to Greg).
*/
import BaseLayout from '@layouts/BaseLayout.astro';
import Reveal from '@components/motion/Reveal.astro';
import Icon from '@components/Icon.astro';
import SectionHeader from '@components/layout/SectionHeader.astro';
import Placeholder from '@components/layout/Placeholder.astro';
import SectionNav from '@components/professional/SectionNav.astro';
import Timeline from '@components/professional/Timeline.astro';
import SkillGrid from '@components/professional/SkillGrid.astro';
import ProjectCard from '@components/professional/ProjectCard.astro';
import AlchemicalDivider from '@components/mystic/AlchemicalDivider.astro';
import Astrolabe from '@components/mystic/Astrolabe.astro';
import CircuitField from '@components/mystic/CircuitField.tsx';
import { getCollection } from 'astro:content';
import BaseLayout from "@layouts/BaseLayout.astro";
import Reveal from "@components/motion/Reveal.astro";
import Icon from "@components/Icon.astro";
import SectionHeader from "@components/layout/SectionHeader.astro";
import Placeholder from "@components/layout/Placeholder.astro";
import SectionNav from "@components/professional/SectionNav.astro";
import Timeline from "@components/professional/Timeline.astro";
import SkillGrid from "@components/professional/SkillGrid.astro";
import ProjectCard from "@components/professional/ProjectCard.astro";
import AlchemicalDivider from "@components/mystic/AlchemicalDivider.astro";
import Astrolabe from "@components/mystic/Astrolabe.astro";
import CircuitField from "@components/mystic/CircuitField.tsx";
import { getCollection } from "astro:content";
const experience = (await getCollection('experience')).sort(
const experience = (await getCollection("experience")).sort(
(a, b) => a.data.order - b.data.order,
);
const projects = (await getCollection('projects')).sort(
const projects = (await getCollection("projects")).sort(
(a, b) => a.data.order - b.data.order,
);
---
@@ -39,9 +39,11 @@ const projects = (await getCollection('projects')).sort(
>
<!-- Page header -->
<section class="relative overflow-hidden pt-28 sm:pt-32">
{/* Circuit-board field. Shares view-transition-name "sky" with the star
{
/* Circuit-board field. Shares view-transition-name "sky" with the star
fields on other pages, so navigating here morphs constellations into
circuitry. */}
circuitry. */
}
<CircuitField client:idle className="opacity-70" transitionName="sky" />
{/* Slowly rotating astrolabe ornament behind the header. */}
<Astrolabe
@@ -53,7 +55,7 @@ const projects = (await getCollection('projects')).sort(
<SectionHeader
kicker="Professional"
icon="briefcase"
title="Resume &amp; portfolio"
title="Resume & portfolio"
subtitle="Clean, confident, skimmable. Browsable here, screenshot-able for a recruiter, printable if needed."
/>
</Reveal>
@@ -67,7 +69,11 @@ const projects = (await getCollection('projects')).sort(
{/* ===== 1. Summary ===== */}
<section id="summary" class="scroll-mt-28 flex flex-col gap-6">
<Reveal>
<SectionHeader kicker="01" icon="format-list-text" title="Professional summary" />
<SectionHeader
kicker="01"
icon="format-list-text"
title="Professional summary"
/>
</Reveal>
<Reveal delay={80}>
<blockquote
@@ -107,7 +113,10 @@ const projects = (await getCollection('projects')).sort(
</section>
<Reveal>
<AlchemicalDivider class="mx-auto h-6 w-full max-w-sm" style="color: var(--color-accent);" />
<AlchemicalDivider
class="mx-auto h-6 w-full max-w-sm"
style="color: var(--color-accent);"
/>
</Reveal>
{/* ===== 2. Experience ===== */}
@@ -129,7 +138,7 @@ const projects = (await getCollection('projects')).sort(
<SectionHeader
kicker="03"
icon="chart-bar"
title="Skills &amp; technologies"
title="Skills & technologies"
subtitle="What I bring to the table, organized into three tiers."
/>
</Reveal>
@@ -147,11 +156,13 @@ const projects = (await getCollection('projects')).sort(
/>
</Reveal>
<div class="grid gap-5 sm:grid-cols-2">
{projects.map((project, i) => (
{
projects.map((project, i) => (
<Reveal delay={i * 80}>
<ProjectCard project={project} />
</Reveal>
))}
))
}
</div>
<Placeholder
label="Add 3-6 projects"
@@ -165,13 +176,19 @@ const projects = (await getCollection('projects')).sort(
<SectionHeader
kicker="05"
icon="school"
title="Education &amp; certifications"
title="Education & certifications"
subtitle="My foundation."
/>
</Reveal>
<div class="grid gap-4 sm:grid-cols-2">
<Placeholder label="Education history" hint="Add degrees, institutions, and dates." />
<Placeholder label="Certifications" hint="SAP, cloud, or other relevant certifications." />
<Placeholder
label="Education history"
hint="Add degrees, institutions, and dates."
/>
<Placeholder
label="Certifications"
hint="SAP, cloud, or other relevant certifications."
/>
</div>
</section>
@@ -189,16 +206,49 @@ const projects = (await getCollection('projects')).sort(
<div class="card flex flex-col gap-6">
<ul class="flex flex-col gap-4 sm:flex-row sm:flex-wrap sm:gap-8">
<li class="flex items-center gap-2">
<Icon name="email-outline" class="h-5 w-5" style="color: var(--color-accent);" />
<span class="font-mono text-xs uppercase tracking-[0.18em]" style="color: var(--color-accent);">Email: [Greg to fill]</span>
<Icon
name="email-outline"
class="h-5 w-5"
style="color: var(--color-accent);"
/>
<span
class="font-mono text-xs uppercase tracking-[0.18em]"
style="color: var(--color-accent);"
>
<a href="mailto:Kilmer.GM@Gmail.com">Kilmer.GM@Gmail.com</a>
</span>
</li>
<li class="flex items-center gap-2">
<Icon name="linkedin" class="h-5 w-5" style="color: var(--color-accent);" />
<span class="font-mono text-xs uppercase tracking-[0.18em]" style="color: var(--color-accent);">LinkedIn: [Greg to fill]</span>
<Icon
name="linkedin"
class="h-5 w-5"
style="color: var(--color-accent);"
/>
<span
class="font-mono text-xs uppercase tracking-[0.18em]"
style="color: var(--color-accent);"
><a
href="https://www.linkedin.com/in/gregory-kilmer-36646bb5/"
target="_blank"
rel="noopener noreferrer">LinkedIn</a
></span
>
</li>
<li class="flex items-center gap-2">
<Icon name="github" class="h-5 w-5" style="color: var(--color-accent);" />
<span class="font-mono text-xs uppercase tracking-[0.18em]" style="color: var(--color-accent);">GitHub: [Greg to fill]</span>
<Icon
name="github"
class="h-5 w-5"
style="color: var(--color-accent);"
/>
<span
class="font-mono text-xs uppercase tracking-[0.18em]"
style="color: var(--color-accent);"
><a
href="https://github.com/patiun"
target="_blank"
rel="noopener noreferrer">GitHub</a
></span
>
</li>
</ul>
<a href="/contact" class="btn btn-primary self-start">