mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-08 04:08:24 +00:00
250 lines
8.7 KiB
Plaintext
250 lines
8.7 KiB
Plaintext
---
|
|
/**
|
|
* contact.astro — Contact / Connect
|
|
* ---------------------------------------------------------------------------
|
|
* A single place for all ways to reach Greg, professional and social, per the
|
|
* spec's two-column layout. Also hosts the contact form (a Node backend is
|
|
* available, so this is the natural place to use it). Closes with the spec's
|
|
* warm sign-off line.
|
|
*
|
|
* 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 ObfuscatedEmail from "@components/ObfuscatedEmail.astro";
|
|
import { turnstileSiteKey as getTurnstileSiteKey } from "@lib/env";
|
|
|
|
// Email is rendered separately via <ObfuscatedEmail> so the plaintext address
|
|
// never appears in the HTML source. The other entries are plain links.
|
|
const emailAddress = "contact@gregkilmer.com";
|
|
|
|
const professional = [
|
|
{
|
|
icon: "linkedin",
|
|
label: "LinkedIn",
|
|
href: "https://www.linkedin.com/in/gregory-kilmer-36646bb5/",
|
|
hint: "Gregory Kilmer",
|
|
},
|
|
{
|
|
icon: "github",
|
|
label: "GitHub",
|
|
href: "https://github.com/patiun",
|
|
hint: "patiun",
|
|
},
|
|
];
|
|
|
|
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.",
|
|
},
|
|
];
|
|
|
|
// Turnstile site key. Public per Cloudflare (safe to ship to the client).
|
|
// Read via the env helper so it resolves in BOTH `astro dev` (Vite loads `.env`
|
|
// into import.meta.env) AND the Docker runtime (env_file sets process.env).
|
|
// See src/lib/env.ts for why neither source alone covers both runtimes.
|
|
// Undefined when Turnstile is not configured, which makes ContactForm skip the
|
|
// widget entirely.
|
|
const turnstileSiteKey = getTurnstileSiteKey();
|
|
---
|
|
|
|
<BaseLayout
|
|
title="Contact"
|
|
description="Reach Greg Kilmer for cloud development, SAP BTP, technical leadership, or just to debate the best Gundam series or find a fourth for game night."
|
|
noindex={true}
|
|
>
|
|
<section class="container-prose flex flex-col gap-10 pt-28 sm:pt-32">
|
|
<Reveal>
|
|
<SectionHeader
|
|
kicker="Contact"
|
|
icon="email"
|
|
title="Let's connect"
|
|
subtitle="Whether you want to talk cloud architecture, debate the best Gundam series, or need a fourth for game night, reach out."
|
|
/>
|
|
</Reveal>
|
|
</section>
|
|
|
|
<div class="container-prose grid gap-8 py-12 lg:grid-cols-[1fr_1.1fr]">
|
|
{/* Left: contact methods */}
|
|
<div class="flex flex-col gap-10">
|
|
{/* Professional */}
|
|
<Reveal>
|
|
<section class="flex flex-col gap-4">
|
|
<h2
|
|
class="font-mono text-xs uppercase tracking-[0.22em]"
|
|
style="color: var(--color-accent);"
|
|
>
|
|
Professional
|
|
</h2>
|
|
<ul class="flex flex-col gap-3">
|
|
{
|
|
/* Email: rendered via ObfuscatedEmail so the address is not in
|
|
the HTML source. JS decodes it into a mailto: on click. */
|
|
}
|
|
<li class="card flex items-center gap-3">
|
|
<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="email-outline" 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);"
|
|
>
|
|
Email
|
|
</span>
|
|
<span class="text-xs" style="color: var(--color-text-subtle);">
|
|
<ObfuscatedEmail
|
|
address={emailAddress}
|
|
class="font-mono uppercase tracking-[0.1em]"
|
|
/>
|
|
</span>
|
|
</span>
|
|
</li>
|
|
{
|
|
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"
|
|
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="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);"
|
|
>
|
|
<a
|
|
href={item.href}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
{item.hint}
|
|
</a>
|
|
</span>
|
|
</span>
|
|
</li>
|
|
))
|
|
}
|
|
</ul>
|
|
</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);"
|
|
>
|
|
Personal / Social
|
|
</h2>
|
|
<ul class="grid gap-3 sm:grid-cols-2">
|
|
{
|
|
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"
|
|
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>
|
|
<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>
|
|
</li>
|
|
))
|
|
}
|
|
</ul>
|
|
</section>
|
|
</Reveal>
|
|
*/
|
|
}
|
|
|
|
{
|
|
/* Availability note
|
|
<Reveal delay={120}>
|
|
<p
|
|
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 AI/ML, app and cloud development, SAP BTP, and technical
|
|
leadership.
|
|
</p>
|
|
</Reveal>*/
|
|
}
|
|
</div>
|
|
|
|
{/* Right: contact form */}
|
|
<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>
|
|
<p class="text-sm" style="color: var(--color-text-muted);">
|
|
Prefer a form? Drop a note and it will land in my inbox.
|
|
</p>
|
|
</div>
|
|
<ContactForm client:idle turnstileSiteKey={turnstileSiteKey} />
|
|
</section>
|
|
</Reveal>
|
|
</div>
|
|
|
|
{/* Closing note */}
|
|
<section class="container-prose py-12">
|
|
<Reveal>
|
|
<blockquote
|
|
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.
|
|
</blockquote>
|
|
</Reveal>
|
|
</section>
|
|
</BaseLayout>
|