--- /** * 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 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(); ---
{/* Left: contact methods */}
{/* Professional */}

Professional

    { /* Email: rendered via ObfuscatedEmail so the address is not in the HTML source. JS decodes it into a mailto: on click. */ }
  • Email
  • { professional.map((item) => (
  • {item.label} {item.hint}
  • )) }
{ /* Social / personal

Personal / Social

    { social.map((item) => (
  • {item.label} {item.hint}
  • )) }
*/ } { /* Availability note

Availability: Open to opportunities in AI/ML, app and cloud development, SAP BTP, and technical leadership.

*/ }
{/* Right: contact form */}

Send a message

Prefer a form? Drop a note and it will land in my inbox.

{/* Closing note */}
Whether you want to talk cloud architecture, debate the best Gundam series, or need a fourth for game night, reach out.