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