Reskin GregKilmer.com in the Patiun brand

- Palette: Patiun Teal, Midnight, night sky, Starlight, flat Alchemist Gold;
  violet/indigo only as nebula light. Starlight content sections, night-sky
  nav and heroes (.on-sky scope re-points accent/gold to on-dark values).
- Type: Cinzel for headings, names and card titles; Montserrat for body, UI
  and eyebrows (font-eyebrow); JetBrains Mono kept for code-like blocks.
- Header lockup: Patiun glyph + Cinzel "GREG KILMER" + "Web Development ·
  a Patiun LLC company"; glyph also marks the footer and store nav.
- Teal filled buttons with Midnight text; gold eyebrows; small links use a
  darker teal so they pass WCAG AA on Starlight.
- Favicons, app icons and web manifest from the GregKilmer.com brand package.
- docs/THEMING.md updated for the new palette and fonts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
This commit is contained in:
Claude
2026-10-11 01:54:04 +00:00
parent 5edf0c6061
commit 01431468e4
71 changed files with 423 additions and 309 deletions
+40
View File
@@ -0,0 +1,40 @@
---
/**
* PatiunGlyph.astro
* ---------------------------------------------------------------------------
* The Patiun LLC main mark: the alchemical symbol for platinum (the moon of
* silver joined to the sun of gold). Used small as the maker's mark in the
* Greg Kilmer lockup. Geometry is copied unchanged from the brand file
* patiun-symbol-A-exact-teal.svg; only the color is themable.
*
* Brand rules: Patiun Teal by default (gold, black or white are the only
* other allowed colors), no rings around it at small sizes, minimum 16px, and
* text sits beside it, never over it.
*
* Usage: <PatiunGlyph class="h-6 w-auto" />
*/
interface Props {
class?: string;
style?: string;
/** Accessible name. Omit (default) when the mark sits next to visible text. */
title?: string;
}
const { class: className, style = "color: var(--color-teal);", title } = Astro.props;
---
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="20 33 152 127"
class={className}
style={style}
role={title ? "img" : undefined}
aria-hidden={title ? undefined : "true"}
aria-label={title}
focusable="false"
>
<g fill="none" stroke="currentColor" stroke-width="7.2" stroke-linejoin="round">
<path d="M31,46.75 A54.3,54.3 0 1 1 31,146.25 A53.4,53.4 0 0 0 31,46.75 Z" />
<circle cx="134" cy="96.5" r="26.9" />
</g>
<circle cx="134" cy="96.5" r="10.3" fill="currentColor" />
</svg>
+2 -2
View File
@@ -96,13 +96,13 @@ const updated = updatedAt ? new Date(updatedAt).toLocaleDateString('en-US', {
<div class="flex flex-col gap-2 p-4">
<h3
class="font-sans text-base leading-tight underline-offset-4 group-hover:underline"
class="font-display text-base leading-tight underline-offset-4 group-hover:underline"
style="color: var(--color-text);"
>
{name}
</h3>
<div
class="flex flex-wrap items-center gap-2 font-mono text-[0.65rem] uppercase tracking-[0.16em]"
class="flex flex-wrap items-center gap-2 font-eyebrow text-[0.65rem] uppercase tracking-[0.16em]"
style="color: var(--color-text-muted);"
>
{formatLabel && <span>{formatLabel}</span>}
+2 -2
View File
@@ -67,7 +67,7 @@ const alt = artAlt ?? title;
</span>
{year && (
<span
class="font-mono text-[0.65rem] uppercase tracking-[0.18em]"
class="font-eyebrow text-[0.65rem] uppercase tracking-[0.18em]"
style="color: var(--color-text-muted);"
>
{year}
@@ -75,7 +75,7 @@ const alt = artAlt ?? title;
)}
</div>
<h3 class="font-sans text-lg" style="color: var(--color-text);">{title}</h3>
<h3 class="font-display text-lg" style="color: var(--color-text);">{title}</h3>
<p class="grow text-sm leading-relaxed" style="color: var(--color-text-muted);">
{blurb}
+2 -2
View File
@@ -41,7 +41,7 @@ const { icon, title, status, currently = false, class: className = '' } = Astro.
<Icon name={icon} class="h-6 w-6" />
</span>
{currently && (
<span class="inline-flex items-center gap-1.5 font-mono text-[0.65rem] uppercase tracking-[0.18em]" style="color: var(--color-accent);">
<span class="inline-flex items-center gap-1.5 font-eyebrow text-[0.65rem] uppercase tracking-[0.18em]" style="color: var(--color-accent);">
<span class="h-2 w-2 rounded-full animate-pulse-glow" style="background: var(--color-accent);"></span>
Active
</span>
@@ -50,7 +50,7 @@ const { icon, title, status, currently = false, class: className = '' } = Astro.
<span class="pill">{status}</span>
)}
</div>
<h3 class="font-sans text-lg" style="color: var(--color-text);">{title}</h3>
<h3 class="font-display text-lg" style="color: var(--color-text);">{title}</h3>
<div class="grow text-sm leading-relaxed" style="color: var(--color-text-muted);">
<slot />
</div>
+2 -2
View File
@@ -81,7 +81,7 @@ const detail = [year ? String(year) : null, count].filter(Boolean).join(' \u00b7
</div>
<div class="flex flex-col gap-2 p-4">
<div class="flex items-start justify-between gap-2">
<h3 class="font-sans text-lg leading-tight" style="color: var(--color-text);">
<h3 class="font-display text-lg leading-tight" style="color: var(--color-text);">
{title}
</h3>
{status && <span class="pill shrink-0">{status}</span>}
@@ -89,7 +89,7 @@ const detail = [year ? String(year) : null, count].filter(Boolean).join(' \u00b7
{
detail && (
<span
class="font-mono text-[0.65rem] uppercase tracking-[0.18em]"
class="font-eyebrow text-[0.65rem] uppercase tracking-[0.18em]"
style="color: var(--color-text-muted);"
>
{detail}
+21 -21
View File
@@ -85,17 +85,17 @@ export default function AnimatedBackground({
// Resolve star colors from CSS variables so the field matches the theme.
const gold =
getComputedStyle(document.documentElement).getPropertyValue('--color-amber').trim() ||
'#e8b54a';
const violet =
getComputedStyle(document.documentElement).getPropertyValue('--color-violet').trim() ||
'#8b7bd8';
getComputedStyle(document.documentElement).getPropertyValue('--color-gold').trim() ||
'#c9a24a';
// Nebula Violet is background light only, so violet stars use a lifted
// tint of it; teal stars use the on-dark teal so they read at dot size.
const violet = '#8a63ee';
const teal =
getComputedStyle(document.documentElement).getPropertyValue('--color-teal').trim() ||
'#6fb6ad';
getComputedStyle(document.documentElement).getPropertyValue('--color-teal-bright').trim() ||
'#2bc2b7';
const white =
getComputedStyle(document.documentElement).getPropertyValue('--color-cream').trim() ||
'#f4f1e8';
getComputedStyle(document.documentElement).getPropertyValue('--color-starlight').trim() ||
'#f4f2ed';
const hueColor = (h: Star['hue']) => (h === 'gold' ? gold : h === 'violet' ? violet : h === 'teal' ? teal : white);
@@ -214,7 +214,7 @@ export default function AnimatedBackground({
const proximity = 1 - Math.sqrt(d2) / LINK_DIST;
const pulse = 0.5 + 0.5 * Math.sin(now / 1400 + (i + j) * 0.7);
const alpha = proximity * 0.22 * (0.55 + 0.45 * pulse);
ctx.strokeStyle = `rgba(232, 181, 74, ${alpha.toFixed(3)})`;
ctx.strokeStyle = `rgba(201, 162, 74, ${alpha.toFixed(3)})`;
ctx.beginPath();
ctx.moveTo(A.x, A.y);
ctx.lineTo(B.x, B.y);
@@ -270,8 +270,8 @@ export default function AnimatedBackground({
const grad = ctx.createLinearGradient(sh.x, sh.y, tailX, tailY);
const headA = Math.max(0, Math.min(1, sh.life));
grad.addColorStop(0, `rgba(244, 241, 232, ${headA.toFixed(3)})`);
grad.addColorStop(0.4, `rgba(232, 181, 74, ${(headA * 0.6).toFixed(3)})`);
grad.addColorStop(1, 'rgba(232, 181, 74, 0)');
grad.addColorStop(0.4, `rgba(201, 162, 74, ${(headA * 0.6).toFixed(3)})`);
grad.addColorStop(1, 'rgba(201, 162, 74, 0)');
ctx.strokeStyle = grad;
ctx.lineWidth = 1.6;
ctx.beginPath();
@@ -332,7 +332,7 @@ export default function AnimatedBackground({
className="absolute inset-0"
style={{
background:
'linear-gradient(180deg, #04061a 0%, #070b24 32%, #0a1230 58%, #0e1a3e 82%, #12224a 100%)',
'linear-gradient(180deg, #080c14 0%, #0b111c 32%, #0e1522 58%, #131c2b 82%, #1a2a3a 100%)',
}}
/>
{/* Milky Way galaxy band — a soft diagonal luminous streak with a denser
@@ -341,10 +341,10 @@ export default function AnimatedBackground({
className="animate-gradient-shift absolute inset-0 opacity-90"
style={{
backgroundImage: `
radial-gradient(38% 70% at 68% 38%, rgba(111, 182, 173, 0.22), transparent 62%),
radial-gradient(30% 55% at 72% 44%, rgba(139, 123, 216, 0.26), transparent 60%),
radial-gradient(22% 40% at 76% 50%, rgba(232, 181, 74, 0.12), transparent 60%),
linear-gradient(115deg, transparent 30%, rgba(236, 233, 255, 0.06) 45%, rgba(236, 233, 255, 0.09) 52%, rgba(236, 233, 255, 0.05) 60%, transparent 72%)
radial-gradient(38% 70% at 68% 38%, rgba(20, 163, 154, 0.22), transparent 62%),
radial-gradient(30% 55% at 72% 44%, rgba(106, 47, 224, 0.26), transparent 60%),
radial-gradient(22% 40% at 76% 50%, rgba(201, 162, 74, 0.12), transparent 60%),
linear-gradient(115deg, transparent 30%, rgba(236, 233, 226, 0.06) 45%, rgba(236, 233, 226, 0.09) 52%, rgba(236, 233, 226, 0.05) 60%, transparent 72%)
`,
backgroundSize: '200% 200%',
}}
@@ -354,14 +354,14 @@ export default function AnimatedBackground({
className="absolute inset-0"
style={{
backgroundImage: `
radial-gradient(45% 55% at 12% 12%, rgba(139, 123, 216, 0.18), transparent 60%),
radial-gradient(40% 50% at 88% 88%, rgba(111, 182, 173, 0.16), transparent 62%)
radial-gradient(45% 55% at 12% 12%, rgba(106, 47, 224, 0.18), transparent 60%),
radial-gradient(40% 50% at 88% 88%, rgba(20, 163, 154, 0.16), transparent 62%)
`,
}}
/>
{/* Faint astrolabe + sacred-geometry ornaments for esoteric texture. */}
<svg
className="absolute -left-40 -top-40 h-[34rem] w-[34rem] animate-spin-slow text-(--color-amber) opacity-[0.11]"
className="absolute -left-40 -top-40 h-[34rem] w-[34rem] animate-spin-slow text-(--color-gold) opacity-[0.11]"
viewBox="0 0 100 100"
fill="none"
stroke="currentColor"
@@ -414,7 +414,7 @@ export default function AnimatedBackground({
className="absolute inset-0"
style={{
background:
'radial-gradient(120% 80% at 50% 30%, transparent 40%, rgba(4, 6, 26, 0.7) 100%)',
'radial-gradient(120% 80% at 50% 30%, transparent 40%, rgba(8, 12, 20, 0.7) 100%)',
}}
/>
{/* Canvas star field (skipped under reduced motion). */}
+5 -5
View File
@@ -3,11 +3,11 @@
* Footer.astro
* ---------------------------------------------------------------------------
* Minimal footer on a deep night-sky surface: a faint static starfield accent,
* a StarBurst mark beside the name, social links (mdi icons), copyright, and
* the Patiun glyph as a maker's mark beside the name, social links (mdi icons), copyright, and
* the spec microcopy "Built with intention. Powered by caffeine." No emojis.
*/
import Icon from '@components/Icon.astro';
import StarBurst from '@components/mystic/StarBurst.astro';
import PatiunGlyph from '@components/brand/PatiunGlyph.astro';
import StarField from '@components/mystic/StarField.astro';
import { socials, footerMicrocopy, siteIdentity } from '@lib/site';
const year = new Date().getFullYear();
@@ -26,12 +26,12 @@ const socialList = Object.values(socials);
/>
<div class="container-prose relative z-10 flex flex-col items-center gap-6 py-10 text-center sm:flex-row sm:justify-between sm:text-left">
<div class="flex items-center gap-3">
<StarBurst class="h-5 w-5 shrink-0" style="color: var(--color-accent);" />
<PatiunGlyph class="h-6 w-auto shrink-0" />
<div class="flex flex-col gap-1">
<p class="font-sans text-sm font-semibold" style="color: var(--color-text);">
<p class="font-display text-sm font-bold uppercase tracking-[0.14em]" style="color: var(--color-text);">
{siteIdentity.name}
</p>
<p class="font-mono text-xs italic" style="color: var(--color-text-subtle);">
<p class="font-eyebrow text-xs italic" style="color: var(--color-text-subtle);">
{footerMicrocopy}
</p>
</div>
+7 -7
View File
@@ -6,7 +6,7 @@
* Milky Way field: a back-to-hub breadcrumb, a small icon mark, the title, and
* the italic tagline.
*
* Tuned for the dark starfield: text uses bright cream tones (not the dim
* Tuned for the dark starfield: text uses bright Starlight tones (not the dim
* --color-text-subtle/--color-text which vanish on the deep sky). The title is
* h2-sized (the page is a sub-page, not Home) so it doesn't dominate. An
* optional banner image sits above the heading.
@@ -39,7 +39,7 @@ const {
} = Astro.props;
---
<section class="relative overflow-hidden pb-8 pt-28 sm:pt-32">
<section class="on-sky relative overflow-hidden pb-8 pt-28 sm:pt-32">
<!-- Full animated Milky Way field + stars (matches the Home/Interests hero).
Carries the per-page morph name (one .vt-sky per page snapshot). -->
<AnimatedBackground client:idle className="vt-sky" />
@@ -54,8 +54,8 @@ const {
<Reveal>
<a
href="/interests"
class="inline-flex items-center gap-1.5 font-mono text-xs uppercase tracking-[0.18em] transition-colors"
style="color: var(--color-cream); opacity: 0.8;"
class="inline-flex items-center gap-1.5 font-eyebrow text-xs uppercase tracking-[0.18em] transition-colors"
style="color: var(--color-starlight); opacity: 0.8;"
>
<Icon name="arrow-left" class="h-4 w-4" />
{eyebrow}
@@ -74,8 +74,8 @@ const {
<Reveal delay={100}>
<h2
class="font-sans font-bold tracking-tight"
style="color: var(--color-cream); font-size: clamp(1.75rem, 2vw + 1rem, 2.5rem); line-height: 1.15;"
class="font-display font-bold"
style="color: var(--color-starlight); font-size: clamp(1.75rem, 2vw + 1rem, 2.5rem); line-height: 1.15;"
>
{title}
</h2>
@@ -84,7 +84,7 @@ const {
<Reveal delay={140}>
<p
class="max-w-xl border-l-2 pl-4 font-sans text-base italic sm:text-lg"
style="border-color: var(--color-accent); color: var(--color-cream); opacity: 0.92;"
style="border-color: var(--color-accent); color: var(--color-starlight); opacity: 0.92;"
>
{tagline}
</p>
+1 -1
View File
@@ -40,7 +40,7 @@ const { label, hint, class: className = '' } = Astro.props;
<Icon name="pencil" class="h-4 w-4" />
</span>
<div class="flex flex-col gap-1">
<span class="font-mono text-[0.65rem] font-bold uppercase tracking-[0.18em]" style="color: var(--color-accent);">
<span class="font-eyebrow text-[0.65rem] font-bold uppercase tracking-[0.18em]" style="color: var(--color-accent);">
TODO &middot; {label}
</span>
{hint && <p class="text-sm" style="color: var(--color-text-muted);">{hint}</p>}
+1 -1
View File
@@ -23,7 +23,7 @@ const { items } = Astro.props;
---
<section class="mt-16">
<h3 class="mb-5 font-mono text-xs uppercase tracking-[0.22em]" style="color: var(--color-accent);">
<h3 class="mb-5 font-eyebrow text-xs uppercase tracking-[0.22em]" style="color: var(--color-accent);">
You might also like
</h3>
<div class="grid gap-4 sm:grid-cols-2">
+4 -4
View File
@@ -39,8 +39,8 @@ const {
} = Astro.props;
const isHero = tone === "hero";
const titleColor = isHero ? "var(--color-cream)" : undefined;
const subtitleColor = isHero ? "var(--color-cream)" : "var(--color-text-muted)";
const titleColor = isHero ? "var(--color-starlight)" : undefined;
const subtitleColor = isHero ? "var(--color-starlight)" : "var(--color-text-muted)";
---
<div
@@ -53,11 +53,11 @@ const subtitleColor = isHero ? "var(--color-cream)" : "var(--color-text-muted)";
{
kicker && (
<span
class="inline-flex items-center gap-2 font-mono text-xs uppercase tracking-[0.22em]"
class="inline-flex items-center gap-2 font-eyebrow text-xs uppercase tracking-[0.22em]"
style={
isHero
? "color: var(--color-accent); text-shadow: 0 0 14px color-mix(in srgb, var(--color-accent) 45%, transparent);"
: "color: var(--color-accent);"
: "color: var(--color-secondary);"
}
>
{icon && <Icon name={icon} class="h-4 w-4" />}
+2 -2
View File
@@ -39,7 +39,7 @@ const {
/>
<span class="flex flex-col gap-1">
<span
class="font-mono text-xs uppercase tracking-[0.18em]"
class="font-eyebrow text-xs uppercase tracking-[0.18em]"
style="color: var(--color-text-subtle); text-align:right;"
>
The professional wing
@@ -59,7 +59,7 @@ const {
>
<span class="flex flex-col gap-1">
<span
class="font-mono text-xs uppercase tracking-[0.18em]"
class="font-eyebrow text-xs uppercase tracking-[0.18em]"
style="color: var(--color-text-subtle);"
>
The personal wing
+4 -4
View File
@@ -29,13 +29,13 @@ const { groups, pillIcon = 'play-circle' } = Astro.props;
<div class="flex flex-col gap-3">
<div class="flex items-baseline gap-2.5">
<h3
class="font-sans text-lg"
class="font-display text-lg"
style="color: var(--color-text);"
>
{group.category}
</h3>
<span
class="font-mono text-[0.65rem] uppercase tracking-[0.18em]"
class="font-eyebrow text-[0.65rem] uppercase tracking-[0.18em]"
style="color: var(--color-text-muted);"
>
{group.items.length}
@@ -51,7 +51,7 @@ const { groups, pillIcon = 'play-circle' } = Astro.props;
<span>{item.title}</span>
{item.status && (
<span
class="font-mono text-[0.6rem] uppercase tracking-[0.14em]"
class="font-eyebrow text-[0.6rem] uppercase tracking-[0.14em]"
style="color: var(--color-accent);"
>
{item.status}
@@ -59,7 +59,7 @@ const { groups, pillIcon = 'play-circle' } = Astro.props;
)}
{item.year && (
<span
class="font-mono text-[0.6rem] tracking-[0.04em]"
class="font-eyebrow text-[0.6rem] tracking-[0.04em]"
style="color: var(--color-text-muted);"
>
{item.year}
@@ -11,7 +11,7 @@
* --color-violet at the call site).
*
* Usage:
* <AlchemicalDivider class="my-4 h-8 w-full max-w-md mx-auto" style="color: var(--color-amber)" />
* <AlchemicalDivider class="my-4 h-8 w-full max-w-md mx-auto" style="color: var(--color-gold)" />
*/
interface Props {
class?: string;
+1 -1
View File
@@ -11,7 +11,7 @@
* opacity + color at the call site so it reads as a faint engraving.
*
* Usage:
* <Astrolabe class="pointer-events-none absolute -left-32 -top-32 h-[28rem] w-[28rem] animate-spin-slow" style="color: var(--color-amber); opacity:.12" />
* <Astrolabe class="pointer-events-none absolute -left-32 -top-32 h-[28rem] w-[28rem] animate-spin-slow" style="color: var(--color-gold); opacity:.12" />
*/
interface Props {
class?: string;
+1 -1
View File
@@ -80,7 +80,7 @@ const objectFit = fit === "contain" ? "object-contain" : "object-cover";
</svg>
</div>
<span
class="absolute bottom-3 left-1/2 -translate-x-1/2 font-mono text-[0.6rem] uppercase tracking-[0.2em]"
class="absolute bottom-3 left-1/2 -translate-x-1/2 font-eyebrow text-[0.6rem] uppercase tracking-[0.2em]"
style="color: var(--color-text-subtle); opacity:.8;"
>
{alt}
+7 -7
View File
@@ -56,9 +56,9 @@ export default function CircuitField({
let traces: Trace[] = [];
let nodes: Node[] = [];
const gold = '#e8b54a';
const violet = '#8b7bd8';
const teal = '#6fb6ad';
const gold = '#c9a24a';
const violet = '#8a63ee';
const teal = '#2bc2b7';
function build() {
if (!canvas || !ctx) return;
@@ -116,7 +116,7 @@ export default function CircuitField({
for (let i = 0; i < traces.length; i++) {
const t = traces[i];
// Base trace — clearly visible violet conductor line.
ctx.strokeStyle = 'rgba(168, 158, 220, 0.55)';
ctx.strokeStyle = 'rgba(43, 194, 183, 0.55)';
ctx.lineWidth = 1.4;
ctx.beginPath();
ctx.moveTo(t.x1, t.y1);
@@ -140,7 +140,7 @@ export default function CircuitField({
ctx.stroke();
// Bright pulse head dot.
ctx.globalAlpha = 1;
ctx.fillStyle = '#fff4d6';
ctx.fillStyle = '#fbf3dc';
ctx.beginPath();
ctx.arc(px, py, 1.8, 0, Math.PI * 2);
ctx.fill();
@@ -172,7 +172,7 @@ export default function CircuitField({
if (!ctx) return;
ctx.clearRect(0, 0, width, height);
for (const t of traces) {
ctx.strokeStyle = 'rgba(168, 158, 220, 0.5)';
ctx.strokeStyle = 'rgba(43, 194, 183, 0.5)';
ctx.lineWidth = 1.4;
ctx.beginPath();
ctx.moveTo(t.x1, t.y1);
@@ -243,7 +243,7 @@ export default function CircuitField({
className="absolute inset-0"
style={{
background:
'linear-gradient(180deg, #04061a 0%, #070b24 32%, #0a1230 58%, #0e1a3e 82%, #12224a 100%)',
'linear-gradient(180deg, #080c14 0%, #0b111c 32%, #0e1522 58%, #131c2b 82%, #1a2a3a 100%)',
}}
/>
{/* Circuit traces + nodes + signal pulses. */}
+5 -5
View File
@@ -67,10 +67,10 @@ export default function ConstellationField({
}
let stars: Star[] = [];
const gold = '#e8b54a';
const violet = '#8b7bd8';
const teal = '#6fb6ad';
const white = '#f4f1e8';
const gold = '#c9a24a';
const violet = '#8a63ee';
const teal = '#2bc2b7';
const white = '#f4f2ed';
const accent = tint === 'violet' ? violet : tint === 'teal' ? teal : gold;
// One full rotation every ~10 minutes — barely perceptible, "planet turning".
@@ -168,7 +168,7 @@ export default function ConstellationField({
const proximity = 1 - Math.sqrt(d2) / LINK_DIST;
const pulse = 0.5 + 0.5 * Math.sin(now / 1600 + (i + j) * 0.7);
const alpha = proximity * 0.24 * (0.55 + 0.45 * pulse);
ctx.strokeStyle = `rgba(232, 181, 74, ${alpha.toFixed(3)})`;
ctx.strokeStyle = `rgba(201, 162, 74, ${alpha.toFixed(3)})`;
ctx.beginPath();
ctx.moveTo(A.x, A.y);
ctx.lineTo(B.x, B.y);
+1 -1
View File
@@ -11,7 +11,7 @@
* Apply opacity + sizing + color at the call site.
*
* Usage:
* <SacredGeometry class="pointer-events-none absolute -right-24 -top-24 h-96 w-96" style="color: var(--color-amber); opacity:.08" />
* <SacredGeometry class="pointer-events-none absolute -right-24 -top-24 h-96 w-96" style="color: var(--color-gold); opacity:.08" />
*/
interface Props {
class?: string;
+1 -1
View File
@@ -11,7 +11,7 @@
* - `outline`: stroke-only, lighter
*
* Usage:
* <StarBurst class="h-4 w-4" style="color: var(--color-amber)" />
* <StarBurst class="h-4 w-4" style="color: var(--color-gold)" />
* <StarBurst variant="outline" class="h-6 w-6" />
*/
interface Props {
+1 -1
View File
@@ -10,7 +10,7 @@
* cheap. Pointer-events disabled; inherits `currentColor`.
*
* Usage:
* <StarField class="absolute inset-0 h-full w-full" style="color: var(--color-amber); opacity:.5" />
* <StarField class="absolute inset-0 h-full w-full" style="color: var(--color-gold); opacity:.5" />
*/
interface Props {
class?: string;
+18 -17
View File
@@ -12,7 +12,7 @@
*/
import Icon from "@components/Icon.astro";
import MobileMenu from "./MobileMenu.tsx";
import StarBurst from "@components/mystic/StarBurst.astro";
import PatiunGlyph from "@components/brand/PatiunGlyph.astro";
import { navGroups, siteIdentity } from "@lib/site";
const path = Astro.url.pathname;
@@ -33,7 +33,7 @@ const heroSkyClass = isHome ? "" : "vt-hero-sky";
id="site-nav"
data-nav
class:list={[
"fixed inset-x-0 top-0 z-50 transition-[background,box-shadow,border-color] duration-300",
"on-sky fixed inset-x-0 top-0 z-50 transition-[background,box-shadow,border-color] duration-300",
heroSkyClass,
]}
>
@@ -45,28 +45,29 @@ const heroSkyClass = isHome ? "" : "vt-hero-sky";
class="container-prose flex h-16 items-center justify-between gap-4"
aria-label="Primary"
>
<!-- Logo / identity -->
<!-- Logo / identity: the approved Greg Kilmer lockup. Cinzel wordmark,
the "a Patiun LLC company" line in Montserrat, and the Patiun glyph
as a small maker's mark beside it (never overlapping). -->
<a
href="/"
class="group flex items-center gap-2 leading-tight"
class="group flex items-center gap-2.5 leading-tight"
aria-label={`${siteIdentity.name} home`}
>
<StarBurst
class="h-4 w-4 shrink-0 transition-transform duration-700 group-hover:rotate-90"
style="color: var(--color-accent);"
<PatiunGlyph
class="h-5 w-auto shrink-0 transition-transform duration-700 group-hover:-rotate-12"
/>
<span class="flex flex-col">
<span
class="font-sans text-base font-bold tracking-tight"
style="color: var(--accent);"
class="font-display text-base font-bold uppercase tracking-[0.14em]"
style="color: var(--color-starlight);"
>
{siteIdentity.name}
</span>
<span
class="hidden font-mono text-[0.65rem] uppercase tracking-[0.18em] sm:block"
class="hidden font-eyebrow text-[0.62rem] font-medium tracking-[0.08em] sm:block"
style="color: var(--color-text-subtle);"
>
{siteIdentity.tagline}
{siteIdentity.lockupLine}
</span>
</span>
</a>
@@ -93,7 +94,7 @@ const heroSkyClass = isHome ? "" : "vt-hero-sky";
style={
active
? "color: var(--color-accent);"
: "color: var(--color-cream); opacity: 0.85;"
: "color: var(--color-starlight); opacity: 0.85;"
}
onmouseover="this.style.opacity='1'"
onmouseout="this.style.opacity='0.85'"
@@ -127,8 +128,8 @@ const heroSkyClass = isHome ? "" : "vt-hero-sky";
[data-nav-inner] {
background: linear-gradient(
180deg,
rgba(8, 10, 32, 0.92) 0%,
rgba(12, 14, 40, 0.86) 100%
rgba(14, 21, 34, 0.92) 0%,
rgba(19, 28, 43, 0.86) 100%
);
}
/* Scrolled state: stronger blur + hairline border + shadow. */
@@ -140,7 +141,7 @@ const heroSkyClass = isHome ? "" : "vt-hero-sky";
var(--color-accent) 30%,
var(--color-border)
);
box-shadow: 0 8px 30px -20px rgba(11, 16, 38, 0.7);
box-shadow: 0 8px 30px -20px rgba(8, 12, 20, 0.7);
}
/* A subtle violet/gold nebula glow behind the nav, fixed to the top. */
[data-nav]::before {
@@ -152,12 +153,12 @@ const heroSkyClass = isHome ? "" : "vt-hero-sky";
background:
radial-gradient(
60% 120% at 85% -20%,
rgba(139, 123, 216, 0.22),
rgba(106, 47, 224, 0.22),
transparent 70%
),
radial-gradient(
50% 120% at 10% -10%,
rgba(232, 181, 74, 0.1),
rgba(201, 162, 74, 0.1),
transparent 70%
);
}
@@ -58,14 +58,14 @@ const entries: CertEntry[] = [
{c.status && <span class="pill">{c.status}</span>}
</div>
<div class="flex flex-col gap-1">
<h3 class="font-sans text-base" style="color: var(--color-text);">
<h3 class="font-display text-base" style="color: var(--color-text);">
{c.name}
</h3>
<p class="text-sm font-medium" style="color: var(--color-accent);">
{c.issuer}
</p>
<span
class="font-mono text-xs uppercase tracking-[0.18em]"
class="font-eyebrow text-xs uppercase tracking-[0.18em]"
style="color: var(--color-text-subtle);"
>
{c.year}
+1 -1
View File
@@ -223,7 +223,7 @@ export default function ContactForm({ turnstileSiteKey }: Props) {
<path d="M20 6L9 17l-5-5" />
</svg>
</span>
<h3 className="font-sans text-lg font-semibold" style={{ color: 'var(--color-text)' }}>
<h3 className="font-display text-lg font-semibold" style={{ color: 'var(--color-text)' }}>
Message sent.
</h3>
<p className="text-sm" style={{ color: 'var(--color-text-muted)' }}>
@@ -45,7 +45,7 @@ const entries: EduEntry[] = [
<Icon name={e.icon} class="h-5 w-5" />
</span>
<div class="flex flex-col gap-1">
<h3 class="font-sans text-lg" style="color: var(--color-text);">
<h3 class="font-display text-lg" style="color: var(--color-text);">
{e.credential}
</h3>
<p
@@ -55,7 +55,7 @@ const entries: EduEntry[] = [
{e.institution}
</p>
<span
class="font-mono text-xs uppercase tracking-[0.18em]"
class="font-eyebrow text-xs uppercase tracking-[0.18em]"
style="color: var(--color-text-subtle);"
>
{e.period}
@@ -33,7 +33,7 @@ const links = [
<div class="flex items-start justify-between gap-3">
<div class="flex flex-col gap-1">
<h3 style="color: var(--color-text);">{d.title}</h3>
<p class="font-mono text-xs uppercase tracking-[0.18em]" style="color: var(--color-text-subtle);">
<p class="font-eyebrow text-xs uppercase tracking-[0.18em]" style="color: var(--color-text-subtle);">
{d.role}
</p>
</div>
+8 -8
View File
@@ -8,7 +8,7 @@
*
* Three bugs previously fixed here:
* 1. Contrast: the rail used surface-relative tokens (--color-surface-raised /
* --color-cream) which collapsed to the same tone on light surfaces (the
* --color-starlight) which collapsed to the same tone on light surfaces (the
* interest-surface cream made icons/text invisible). The rail now uses
* FIXED dark colors regardless of the page's surface tone.
* 2. Progress line clipping: the line sat at the icon column's horizontal
@@ -41,7 +41,7 @@ const sections = [
for the progress line, then the icon+label list. */}
<div
class="relative flex gap-2 rounded-2xl border p-3 shadow-2xl"
style="border-color: rgba(232, 181, 74, 0.22); background: linear-gradient(180deg, rgba(12, 14, 40, 0.97), rgba(8, 10, 32, 0.97)); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); box-shadow: 0 18px 40px -24px rgba(11, 16, 38, 0.7);"
style="border-color: rgba(201, 162, 74, 0.22); background: linear-gradient(180deg, rgba(19, 28, 43, 0.97), rgba(14, 21, 34, 0.97)); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); box-shadow: 0 18px 40px -24px rgba(8, 12, 20, 0.7);"
>
{/* --- Left gutter: the progress track + fill. Sits LEFT of the icons. --- */}
<div class="relative my-1 w-2 shrink-0">
@@ -69,11 +69,11 @@ const sections = [
href={`#${s.id}`}
data-section-link={s.id}
class="group relative flex items-center gap-2.5 rounded-lg px-2.5 py-2 font-sans text-sm font-semibold transition-all"
style="color: #e8e6f5;"
style="color: #ece9e2;"
>
<span
class="relative z-10 inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-lg transition-colors"
style="background: rgba(232, 181, 74, 0.14); color: #f4cd72;"
style="background: rgba(201, 162, 74, 0.14); color: #d8b35a;"
data-section-dot={s.id}
>
<Icon name={s.icon} class="h-[1.15rem] w-[1.15rem]" />
@@ -92,19 +92,19 @@ const sections = [
}
.pro-section-nav a[data-section-link]:hover {
opacity: 1;
background: rgba(232, 181, 74, 0.08);
background: rgba(201, 162, 74, 0.08);
}
/* Active link: bright accent text + accent wash. */
.pro-section-nav a[data-active="true"] {
opacity: 1 !important;
color: var(--color-accent) !important;
background: rgba(232, 181, 74, 0.14);
background: rgba(201, 162, 74, 0.14);
}
/* Active dot: filled accent, dark icon, glow ring. */
.pro-section-nav a[data-active="true"] [data-section-dot] {
background: var(--color-accent) !important;
color: #0b1026 !important;
box-shadow: 0 0 12px rgba(232, 181, 74, 0.65);
color: #131c2b !important;
box-shadow: 0 0 12px rgba(201, 162, 74, 0.65);
}
</style>
+3 -3
View File
@@ -72,7 +72,7 @@ const techStack = [
<div class="flex flex-col gap-8">
<!-- Core Competencies -->
<div class="flex flex-col gap-4">
<h3 class="font-sans" style="color: var(--color-text);">
<h3 class="font-display" style="color: var(--color-text);">
Core Competencies
</h3>
<ul class="grid gap-3 sm:grid-cols-2">
@@ -112,7 +112,7 @@ const techStack = [
<!-- Technical Stack -->
<div class="flex flex-col gap-4">
<h3 class="font-sans" style="color: var(--color-text);">Technical Stack</h3>
<h3 class="font-display" style="color: var(--color-text);">Technical Stack</h3>
<div class="grid gap-4 sm:grid-cols-2">
{
techStack.map((item) => (
@@ -151,7 +151,7 @@ const techStack = [
<!-- Emerging & Exploring -->
<div class="flex flex-col gap-4">
<h3 class="font-sans" style="color: var(--color-text);">
<h3 class="font-display" style="color: var(--color-text);">
Emerging &amp; Exploring
</h3>
<ul class="grid gap-3 sm:grid-cols-3">
+1 -1
View File
@@ -50,7 +50,7 @@ const rendered = await Promise.all(
</span>
{entry.data.company}
</h3>
<span class="font-mono text-xs uppercase tracking-[0.18em]" style="color: var(--color-text-subtle);">
<span class="font-eyebrow text-xs uppercase tracking-[0.18em]" style="color: var(--color-text-subtle);">
{entry.data.period}
</span>
</div>
+8 -8
View File
@@ -150,11 +150,11 @@ export default function Carousel({ slides, label = 'Product examples' }: Props)
{/* Caption gradient + label, always visible for the active slide. */}
<div
className="pointer-events-none absolute inset-x-0 bottom-0 flex items-end p-4"
style={{ background: 'linear-gradient(to top, rgba(7,10,28,0.85), transparent)' }}
style={{ background: 'linear-gradient(to top, rgba(14,21,34,0.85), transparent)' }}
>
<span
className="font-sans text-sm font-semibold sm:text-base"
style={{ color: 'var(--color-cream)' }}
style={{ color: 'var(--color-starlight)' }}
>
{s.label}
</span>
@@ -172,9 +172,9 @@ export default function Carousel({ slides, label = 'Product examples' }: Props)
aria-label="Previous slide"
className="absolute left-2 top-1/2 inline-flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full border backdrop-blur-sm transition-colors"
style={{
color: 'var(--color-cream)',
color: 'var(--color-starlight)',
borderColor: 'color-mix(in srgb, var(--color-accent) 40%, transparent)',
background: 'rgba(7,10,28,0.5)',
background: 'rgba(14,21,34,0.5)',
}}
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
@@ -187,9 +187,9 @@ export default function Carousel({ slides, label = 'Product examples' }: Props)
aria-label="Next slide"
className="absolute right-2 top-1/2 inline-flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full border backdrop-blur-sm transition-colors"
style={{
color: 'var(--color-cream)',
color: 'var(--color-starlight)',
borderColor: 'color-mix(in srgb, var(--color-accent) 40%, transparent)',
background: 'rgba(7,10,28,0.5)',
background: 'rgba(14,21,34,0.5)',
}}
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
@@ -201,8 +201,8 @@ export default function Carousel({ slides, label = 'Product examples' }: Props)
{/* Slide counter (top-right). */}
<span
className="pointer-events-none absolute right-3 top-3 rounded-full px-2.5 py-1 font-mono text-xs"
style={{ color: 'var(--color-cream)', background: 'rgba(7,10,28,0.6)' }}
className="pointer-events-none absolute right-3 top-3 rounded-full px-2.5 py-1 font-eyebrow text-xs"
style={{ color: 'var(--color-starlight)', background: 'rgba(14,21,34,0.6)' }}
>
{index + 1} / {count}
</span>
+1 -1
View File
@@ -27,7 +27,7 @@ const inStockCount = colors.filter((c) => c.inStock).length;
<span class="text-sm" style="color: var(--color-text-muted);">
Choose from the available filament colors.
</span>
<span class="font-mono text-xs" style="color: var(--color-text-subtle);">
<span class="font-eyebrow text-xs" style="color: var(--color-text-subtle);">
{inStockCount} of {colors.length} in stock
</span>
</div>
+1 -1
View File
@@ -210,7 +210,7 @@ export default function OrderForm({ turnstileSiteKey, colors = [] }: Props) {
<path d="M20 6L9 17l-5-5" />
</svg>
</span>
<h3 className="font-sans text-lg font-semibold" style={{ color: 'var(--color-text)' }}>
<h3 className="font-display text-lg font-semibold" style={{ color: 'var(--color-text)' }}>
Order request sent.
</h3>
<p className="text-sm" style={{ color: 'var(--color-text-muted)' }}>
+1 -1
View File
@@ -60,7 +60,7 @@ const { icon, caption, tiers, priceHeader = 'Price', subtitle } = Astro.props;
<span class="ml-2 text-xs" style="color: var(--color-text-subtle);">({tier.note})</span>
)}
</td>
<td class="px-4 py-3 text-right font-mono font-semibold" style="color: var(--color-accent);">
<td class="px-4 py-3 text-right font-semibold" style="color: var(--color-accent);">
{tier.price}
</td>
</tr>
+2 -2
View File
@@ -37,8 +37,8 @@ const cover = product.images[0];
/>
{product.images.length > 1 && (
<span
class="pointer-events-none absolute right-2 top-2 inline-flex items-center gap-1 rounded-full px-2 py-1 font-mono text-xs"
style="color: var(--color-cream); background: rgba(7,10,28,0.6);"
class="pointer-events-none absolute right-2 top-2 inline-flex items-center gap-1 rounded-full px-2 py-1 font-eyebrow text-xs"
style="color: var(--color-starlight); background: rgba(14,21,34,0.6);"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<rect x="3" y="5" width="18" height="14" rx="2" />
+2 -2
View File
@@ -24,7 +24,7 @@ const { items, heading } = Astro.props;
function toneColor(icon: Requirement['icon']): string {
switch (icon) {
case 'alert':
return '#e8b54a'; // amber — caution
return '#d8b35a'; // gold — caution
case 'close-circle':
return '#e5655a'; // red — hard no
case 'check-decagram':
@@ -36,7 +36,7 @@ function toneColor(icon: Requirement['icon']): string {
---
{heading && (
<h3 class="font-sans text-xl font-bold" style="color: var(--color-text);">{heading}</h3>
<h3 class="font-display text-xl font-bold" style="color: var(--color-text);">{heading}</h3>
)}
<ul class="flex flex-col gap-3">
+1 -1
View File
@@ -45,7 +45,7 @@ const pct = total > 0 ? Math.max(0, Math.min(100, (open / total) * 100)) : 0;
<div class="flex items-center justify-between text-sm" style="color: var(--color-text-muted);">
<span>{open} of {total} slots open</span>
<span class="font-mono text-xs" style="color: var(--color-text-subtle);">{closed ? '0%' : `${Math.round(pct)}%`}</span>
<span class="font-eyebrow text-xs" style="color: var(--color-text-subtle);">{closed ? '0%' : `${Math.round(pct)}%`}</span>
</div>
<div
+13 -14
View File
@@ -8,13 +8,13 @@
* two sites not be clearly linked yet.
*
* Visually it mirrors NavBar.astro's treatment — the dark nebula band, the
* scroll blur, and the StarBurst mark — so it reads as the same house while
* scroll blur, and the Patiun glyph mark — so it reads as the same house while
* being unmistakably a store (a "Store" wordmark + the store.gregkilmer.com
* domain label).
*/
import Icon from '@components/Icon.astro';
import MobileMenu from '@components/nav/MobileMenu.tsx';
import StarBurst from '@components/mystic/StarBurst.astro';
import PatiunGlyph from '@components/brand/PatiunGlyph.astro';
interface NavEntry {
label: string;
@@ -40,22 +40,21 @@ const groups = [{ items: storeNav }];
<header
id="store-nav"
data-nav
class="fixed inset-x-0 top-0 z-50 transition-[background,box-shadow,border-color] duration-300"
class="on-sky fixed inset-x-0 top-0 z-50 transition-[background,box-shadow,border-color] duration-300"
>
<div class="border-b border-transparent transition-colors duration-300" data-nav-inner>
<nav class="container-prose flex h-16 items-center justify-between gap-4" aria-label="Store">
<!-- Wordmark: "Greg Kilmer · Store" + the store domain label -->
<a href="/store" class="group flex items-center gap-2 leading-tight" aria-label="Greg Kilmer Store — home">
<StarBurst
class="h-4 w-4 shrink-0 transition-transform duration-700 group-hover:rotate-90"
style="color: var(--color-accent);"
<PatiunGlyph
class="h-5 w-auto shrink-0 transition-transform duration-700 group-hover:-rotate-12"
/>
<span class="flex flex-col">
<span class="font-sans text-base font-bold tracking-tight" style="color: var(--accent);">
<span class="font-display text-base font-bold uppercase tracking-[0.12em]" style="color: var(--color-starlight);">
Greg Kilmer · Store
</span>
<span
class="hidden font-mono text-[0.65rem] uppercase tracking-[0.18em] sm:block"
class="hidden font-eyebrow text-[0.65rem] uppercase tracking-[0.18em] sm:block"
style="color: var(--color-text-subtle);"
>
store.gregkilmer.com
@@ -71,7 +70,7 @@ const groups = [{ items: storeNav }];
<a
href={item.href}
class="inline-flex items-center gap-1.5 rounded-md px-3 py-2 text-sm font-medium transition-colors"
style={active ? 'color: var(--color-accent);' : 'color: var(--color-cream); opacity: 0.85;'}
style={active ? 'color: var(--color-accent);' : 'color: var(--color-starlight); opacity: 0.85;'}
onmouseover="this.style.opacity='1'"
onmouseout="this.style.opacity='0.85'"
aria-current={active ? 'page' : undefined}
@@ -100,8 +99,8 @@ const groups = [{ items: storeNav }];
[data-nav-inner] {
background: linear-gradient(
180deg,
rgb(8, 10, 32) 0%,
rgb(12, 14, 40) 100%
rgb(14, 21, 34) 0%,
rgb(19, 28, 43) 100%
);
border-bottom: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent);
}
@@ -113,7 +112,7 @@ const groups = [{ items: storeNav }];
var(--color-accent) 30%,
var(--color-border)
);
box-shadow: 0 8px 30px -20px rgba(11, 16, 38, 0.7);
box-shadow: 0 8px 30px -20px rgba(8, 12, 20, 0.7);
}
[data-nav]::before {
content: '';
@@ -122,8 +121,8 @@ const groups = [{ items: storeNav }];
z-index: -1;
pointer-events: none;
background:
radial-gradient(60% 120% at 85% -20%, rgba(139, 123, 216, 0.22), transparent 70%),
radial-gradient(50% 120% at 10% -10%, rgba(232, 181, 74, 0.1), transparent 70%);
radial-gradient(60% 120% at 85% -20%, rgba(106, 47, 224, 0.22), transparent 70%),
radial-gradient(50% 120% at 10% -10%, rgba(201, 162, 74, 0.1), transparent 70%);
}
</style>
+1 -1
View File
@@ -173,7 +173,7 @@ export default function BoardGameShelf({
<figcaption
className="absolute inset-x-0 bottom-0 flex flex-col gap-0.5 p-4"
style={{ color: 'var(--color-cream)' }}
style={{ color: 'var(--color-starlight)' }}
>
<span className="font-sans text-lg leading-tight drop-shadow">
{activeGame?.name ?? shelfCaption}
+12 -5
View File
@@ -15,8 +15,10 @@
import "@/styles/global.css";
// Self-hosted fonts (woff2, variable). Avoids external requests for perf + privacy.
import "@fontsource-variable/inter";
import "@fontsource-variable/plus-jakarta-sans";
// Patiun brand type: Cinzel (headings, names) + Montserrat (body, UI).
// JetBrains Mono stays for the rare code-like block.
import "@fontsource-variable/cinzel";
import "@fontsource-variable/montserrat";
import "@fontsource-variable/jetbrains-mono";
import { ClientRouter } from "astro:transitions";
@@ -104,7 +106,7 @@ const ogImageUrl = ogImage
<meta name="description" content={description} />
<link rel="canonical" href={canonical} />
{noindex && <meta name="robots" content="noindex, nofollow" />}
<meta name="theme-color" content="#0b1026" />
<meta name="theme-color" content="#131c2b" />
<!-- Open Graph / social cards -->
<meta property="og:type" content="website" />
@@ -115,7 +117,12 @@ const ogImageUrl = ogImage
{ogImageUrl && <meta property="og:image" content={ogImageUrl} />}
<meta name="twitter:card" content="summary_large_image" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<!-- Favicons and app icons: the Greg Kilmer package from the Patiun brand
files (GK monogram on Midnight). -->
<link rel="icon" href="/favicon.ico" sizes="48x48" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<!-- Theme boot: blocking inline script prevents flash of the wrong theme.
Dark mode is currently forced (light mode deferred), so this just pins
@@ -149,7 +156,7 @@ const ogImageUrl = ogImage
<body data-surface={surface} data-page={isHome ? "home" : "other"}>
<a
href="#main"
class="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[100] focus:rounded-lg focus:bg-(--color-amber) focus:px-4 focus:py-2 focus:text-(--color-ink)"
class="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[100] focus:rounded-lg focus:bg-(--color-teal) focus:px-4 focus:py-2 focus:text-(--color-midnight)"
>
Skip to content
</a>
+4 -2
View File
@@ -28,8 +28,10 @@ export const siteIdentity = {
name: 'Greg Kilmer',
/** Spec tagline; appears beside the logo. */
tagline: 'Developer. Maker. Storyteller.',
/** Spec domain. */
domain: 'gregkilmer.com',
/** Brand lockup line shown under the name in the header (Patiun brand). */
lockupLine: 'Web Development · a Patiun LLC company',
/** Domain, written capitalized per the Patiun brand. */
domain: 'GregKilmer.com',
} as const;
/**
+2 -2
View File
@@ -38,7 +38,7 @@ const quickFacts = [
description="The human story behind the resume: Greg Kilmer on curiosity, tabletop RPGs, resin dice, and a Philadelphia apartment with too many board games."
>
<!-- Hero — full animated Milky Way field, matching the Interests hero. -->
<section class="relative overflow-hidden pb-8 pt-28 sm:pt-32">
<section class="on-sky relative overflow-hidden pb-8 pt-28 sm:pt-32">
<AnimatedBackground client:idle className="vt-sky" />
<div class="container-prose relative z-10 flex flex-col gap-8 py-8">
<Reveal>
@@ -124,7 +124,7 @@ const quickFacts = [
<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]"
class="font-eyebrow text-xs uppercase tracking-[0.22em]"
style="color: var(--color-accent);"
>
Quick facts
+4 -4
View File
@@ -89,7 +89,7 @@ const turnstileSiteKey = getTurnstileSiteKey();
<Reveal>
<section class="flex flex-col gap-4">
<h2
class="font-mono text-xs uppercase tracking-[0.22em]"
class="font-eyebrow text-xs uppercase tracking-[0.22em]"
style="color: var(--color-accent);"
>
Professional
@@ -117,7 +117,7 @@ const turnstileSiteKey = getTurnstileSiteKey();
<span class="text-xs" style="color: var(--color-text-subtle);">
<ObfuscatedEmail
address={emailAddress}
class="font-mono uppercase tracking-[0.1em]"
class="font-eyebrow uppercase tracking-[0.1em]"
/>
</span>
</span>
@@ -164,7 +164,7 @@ const turnstileSiteKey = getTurnstileSiteKey();
<Reveal delay={80}>
<section class="flex flex-col gap-4">
<h2
class="font-mono text-xs uppercase tracking-[0.22em]"
class="font-eyebrow text-xs uppercase tracking-[0.22em]"
style="color: var(--color-secondary);"
>
Personal / Social
@@ -222,7 +222,7 @@ const turnstileSiteKey = getTurnstileSiteKey();
<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);">
<h2 class="font-display" style="color: var(--color-text);">
Send a message
</h2>
<p class="text-sm" style="color: var(--color-text-muted);">
+5 -5
View File
@@ -52,7 +52,7 @@ const cards = [
<BaseLayout>
<!-- ===================== HERO ===================== -->
<section
class="vt-hero-sky relative flex min-h-[88vh] items-center overflow-hidden pt-16"
class="on-sky vt-hero-sky relative flex min-h-[88vh] items-center overflow-hidden pt-16"
>
<AnimatedBackground client:idle className="vt-sky" />
@@ -62,8 +62,8 @@ const cards = [
<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(--accent-contrast); background: color-mix(in srgb, var(--color-surface-raised) 75%, transparent);"
class="mb-5 inline-flex items-center gap-2 rounded-full border px-3 py-1 font-eyebrow text-xs uppercase tracking-[0.18em]"
style="border-color: color-mix(in srgb, var(--color-gold) 40%, transparent); color: var(--color-gold-bright); background: color-mix(in srgb, var(--color-midnight) 75%, transparent);"
>
<span
class="h-2 w-2 rounded-full animate-pulse-glow"
@@ -171,7 +171,7 @@ const cards = [
>
<Icon name={card.icon} class="h-6 w-6" />
</span>
<h3 class="font-sans" style="color: var(--color-text);">
<h3 class="font-display" style="color: var(--color-text);">
{card.title}
</h3>
<p
@@ -221,7 +221,7 @@ const cards = [
</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]"
class="font-eyebrow text-xs uppercase tracking-[0.18em]"
style="color: var(--color-text-subtle);"
>
Reading &amp; Media
+1 -1
View File
@@ -188,7 +188,7 @@ const related = [
<Icon name={g.icon} class="h-5 w-5" />
</span>
<h3
class="font-sans text-base"
class="font-display text-base"
style="color: var(--color-text);"
>
{g.name}
+3 -3
View File
@@ -4,7 +4,7 @@
* ---------------------------------------------------------------------------
* The "fun wing" landing page. A warm grid of cards linking into each interest
* sub-page, using the spec's taglines. Sets surface="interest" so the whole
* page reads warm (cream/amber) regardless of the global light/dark toggle.
* page reads as the craft-side night sky regardless of the global light/dark toggle.
*/
import BaseLayout from "@layouts/BaseLayout.astro";
import Reveal from "@components/motion/Reveal.astro";
@@ -114,7 +114,7 @@ const interests = [
description="The fun wing: tabletop RPGs, gaming, reading, resin dice and 3D printing, and Pokémon. Where Greg's passions live as stories and collections."
>
<!-- Hero -->
<section class="relative overflow-hidden pb-8 pt-28 sm:pt-32">
<section class="on-sky relative overflow-hidden pb-8 pt-28 sm:pt-32">
{
/* Full animated Milky Way field + stars, matching the Home hero treatment. */
}
@@ -176,7 +176,7 @@ const interests = [
/>
</div>
<div class="flex flex-col gap-2">
<h3 class="font-sans" style="color: var(--color-text);">
<h3 class="font-display" style="color: var(--color-text);">
{item.title}
</h3>
<p
+1 -1
View File
@@ -58,7 +58,7 @@ const related = [
<Icon name="robot" class="h-10 w-10" />
</span>
<div class="flex flex-col gap-1">
<span class="font-mono text-xs uppercase tracking-[0.22em]" style="color: var(--color-accent);">Favorite Pokémon</span>
<span class="font-eyebrow text-xs uppercase tracking-[0.22em]" style="color: var(--color-accent);">Favorite Pokémon</span>
<h2 style="color: var(--color-text);">Metagross</h2>
<p class="text-sm" style="color: var(--color-text-muted);">Steel / Psychic. The iron leg Pokémon.</p>
</div>
+8 -8
View File
@@ -49,7 +49,7 @@ const projects = (await getCollection("projects")).sort(
puts its center on the boundary: half in the hero, half over the next
section, in front of both (z-50). */
}
<section class="relative overflow-visible pt-28 pb-28 sm:pt-32 sm:pb-36">
<section class="on-sky relative overflow-visible pt-28 pb-28 sm:pt-32 sm:pb-36">
{
/* Background layer — clipped to the hero so the circuit canvas/astrolabe
don't bleed into the content below. */
@@ -155,8 +155,8 @@ const projects = (await getCollection("projects")).sort(
class="aspect-[21/9] w-full object-cover"
/>
<figcaption
class="absolute bottom-0 left-0 m-3 rounded-md px-3 py-1 font-mono text-[0.65rem] uppercase tracking-[0.18em] backdrop-blur-sm"
style="background: color-mix(in srgb, var(--color-ink) 60%, transparent); color: var(--color-cream);"
class="absolute bottom-0 left-0 m-3 rounded-md px-3 py-1 font-eyebrow text-[0.65rem] uppercase tracking-[0.18em] backdrop-blur-sm"
style="background: color-mix(in srgb, var(--color-midnight) 60%, transparent); color: var(--color-starlight);"
>
Where the day-job work happens
</figcaption>
@@ -233,7 +233,7 @@ const projects = (await getCollection("projects")).sort(
<Reveal delay={60}>
<div class="flex flex-col gap-3">
<h3
class="font-mono text-xs uppercase tracking-[0.22em]"
class="font-eyebrow text-xs uppercase tracking-[0.22em]"
style="color: var(--color-accent);"
>
Education history
@@ -244,7 +244,7 @@ const projects = (await getCollection("projects")).sort(
<Reveal delay={120}>
<div class="flex flex-col gap-3">
<h3
class="font-mono text-xs uppercase tracking-[0.22em]"
class="font-eyebrow text-xs uppercase tracking-[0.22em]"
style="color: var(--color-accent);"
>
Certifications
@@ -275,7 +275,7 @@ const projects = (await getCollection("projects")).sort(
/>
<ObfuscatedEmail
address="contact@gregkilmer.com"
class="font-mono text-xs uppercase tracking-[0.18em]"
class="font-eyebrow text-xs uppercase tracking-[0.18em]"
style="color: var(--color-accent);"
/>
</li>
@@ -286,7 +286,7 @@ const projects = (await getCollection("projects")).sort(
style="color: var(--color-accent);"
/>
<span
class="font-mono text-xs uppercase tracking-[0.18em]"
class="font-eyebrow text-xs uppercase tracking-[0.18em]"
style="color: var(--color-accent);"
><a
href="https://www.linkedin.com/in/gregory-kilmer-36646bb5/"
@@ -302,7 +302,7 @@ const projects = (await getCollection("projects")).sort(
style="color: var(--color-accent);"
/>
<span
class="font-mono text-xs uppercase tracking-[0.18em]"
class="font-eyebrow text-xs uppercase tracking-[0.18em]"
style="color: var(--color-accent);"
><a
href="https://github.com/patiun"
+1 -1
View File
@@ -43,7 +43,7 @@ const keyReqs = requirements.filter((r) =>
<Reveal>
<a
href="/store"
class="inline-flex items-center gap-1.5 font-mono text-xs uppercase tracking-[0.18em]"
class="inline-flex items-center gap-1.5 font-eyebrow text-xs uppercase tracking-[0.18em]"
style="color: var(--color-accent);"
>
<Icon name="arrow-left" class="h-4 w-4" />
+1 -1
View File
@@ -42,7 +42,7 @@ const keyReqs = requirements.filter(
<Reveal>
<a
href="/store"
class="inline-flex items-center gap-1.5 font-mono text-xs uppercase tracking-[0.18em]"
class="inline-flex items-center gap-1.5 font-eyebrow text-xs uppercase tracking-[0.18em]"
style="color: var(--color-accent);"
>
<Icon name="arrow-left" class="h-4 w-4" />
+6 -6
View File
@@ -35,7 +35,7 @@ const orderHref = "/store/order";
{/* pt-36/sm:pt-40 gives the bright hero content clear breathing room below
the solid fixed StoreNav (64px tall), so the gold label and nav never
visually crowd each other. */}
<section class="relative overflow-hidden pb-8 pt-36 sm:pt-40">
<section class="on-sky relative overflow-hidden pb-8 pt-36 sm:pt-40">
<AnimatedBackground client:idle className="vt-sky" />
<div
@@ -44,7 +44,7 @@ const orderHref = "/store/order";
<div class="flex flex-col items-start gap-4">
<Reveal>
<span
class="inline-flex items-center gap-1.5 font-mono text-xs uppercase tracking-[0.18em]"
class="inline-flex items-center gap-1.5 font-eyebrow text-xs uppercase tracking-[0.18em]"
style="color: var(--color-accent);"
>
<Icon name="store" class="h-4 w-4" />
@@ -64,8 +64,8 @@ const orderHref = "/store/order";
<Reveal delay={100}>
<h2
class="font-sans font-bold tracking-tight"
style="color: var(--color-cream); font-size: clamp(1.75rem, 2vw + 1rem, 2.75rem); line-height: 1.15;"
class="font-display font-bold"
style="color: var(--color-starlight); font-size: clamp(1.75rem, 2vw + 1rem, 2.75rem); line-height: 1.15;"
>
Proxy MTG cards &amp; 3D-printed deck boxes
</h2>
@@ -74,7 +74,7 @@ const orderHref = "/store/order";
<Reveal delay={140}>
<p
class="max-w-xl border-l-2 pl-4 font-sans text-base italic sm:text-lg"
style="border-color: var(--color-accent); color: var(--color-cream); opacity: 0.92;"
style="border-color: var(--color-accent); color: var(--color-starlight); opacity: 0.92;"
>
Clearly-marked proxies for casual play, and matching 3D-printed
storage &amp; counters. Hand-made in small batches.
@@ -228,7 +228,7 @@ const orderHref = "/store/order";
>
<Reveal>
<h3
class="font-sans text-2xl font-bold"
class="font-display text-2xl font-bold"
style="color: var(--color-text);"
>
Ready to put in a request?
+4 -4
View File
@@ -36,7 +36,7 @@ const siteKey = turnstileSiteKey();
<Reveal>
<a
href="/store"
class="inline-flex items-center gap-1.5 font-mono text-xs uppercase tracking-[0.18em]"
class="inline-flex items-center gap-1.5 font-eyebrow text-xs uppercase tracking-[0.18em]"
style="color: var(--color-accent);"
>
<Icon name="arrow-left" class="h-4 w-4" />
@@ -79,7 +79,7 @@ const siteKey = turnstileSiteKey();
{open ? 'Orders are open. Slots are limited.' : 'Orders are currently closed.'}
</span>
</div>
<span class="font-mono text-xs" style="color: var(--color-text-subtle);">
<span class="font-eyebrow text-xs" style="color: var(--color-text-subtle);">
Updated {av.lastUpdated}
</span>
</div>
@@ -91,7 +91,7 @@ const siteKey = turnstileSiteKey();
<section class="flex flex-col gap-8 lg:grid lg:grid-cols-[1.6fr_1fr] lg:items-start">
<Reveal>
<div class="card flex flex-col gap-2">
<h3 class="font-sans text-lg font-bold" style="color: var(--color-text);">Your request</h3>
<h3 class="font-display text-lg font-bold" style="color: var(--color-text);">Your request</h3>
<p class="text-sm" style="color: var(--color-text-muted);">
Fill in the details below. Remember: the card list is final once received, and
double-sided cards count as 2 slots.
@@ -118,7 +118,7 @@ const siteKey = turnstileSiteKey();
<span class="inline-flex h-12 w-12 items-center justify-center rounded-full" style="background: color-mix(in srgb, #e55 20%, transparent); color: #e55;" aria-hidden="true">
<Icon name="lock" class="h-6 w-6" />
</span>
<h3 class="font-sans text-xl font-bold" style="color: var(--color-text);">Orders are not being taken right now</h3>
<h3 class="font-display text-xl font-bold" style="color: var(--color-text);">Orders are not being taken right now</h3>
<p class="max-w-md text-sm" style="color: var(--color-text-muted);">
I open a limited number of slots at a time. Check back here &mdash; the meters above
show live availability, and the order form appears as soon as orders reopen. You can
+49 -26
View File
@@ -28,17 +28,23 @@
* section wrapper (`data-surface`) instantly restyles every component.
* ------------------------------------------------------------------------- */
@theme {
/* Brand colors (fixed, do not change with theme). Night-sky mystical set:
* star gold, mystic violet, nebula teal, midnight indigo, star-white. */
--color-amber: #e8b54a; /* star gold */
--color-amber-soft: #f4cd72; /* brighter gold for hovers */
--color-violet: #8b7bd8; /* mystic violet */
--color-violet-deep: #6f5fb6;
--color-teal: #6fb6ad; /* nebula teal */
--color-teal-deep: #4f8c84;
--color-slate: #161b3a; /* deep indigo-slate */
--color-ink: #0b1026; /* midnight indigo */
--color-cream: #f4f1e8; /* star-white */
/* Brand colors (fixed, do not change with theme). The Patiun LLC palette,
* shared by Patiun (craft) and Greg Kilmer (web). Source of truth: the
* Patiun brand record / Claude Design tokens. Gold is structure and sparkle
* (stars, rules, small labels), never a large fill. Violet and indigo are
* background light only (nebula glows, gradient ends), never text. */
--color-teal: #14a39a; /* Patiun Teal, primary: buttons, fills, the glyph */
--color-teal-bright: #2bc2b7; /* teal for text/links on dark grounds */
--color-teal-deep: #0f8c84; /* teal on light grounds; hover/active icons */
--color-slate-teal: #2d4a58; /* dark end of the Tide gradient */
--color-gold: #c9a24a; /* Alchemist Gold, flat gold on screen */
--color-gold-bright: #d8b35a; /* gold text/eyebrows on dark grounds */
--color-gold-deep: #8a6516; /* gold text under 24px on Starlight */
--color-violet: #6a2fe0; /* Nebula Violet, background light only */
--color-indigo: #3b1f8f; /* Astral Indigo, gradient midpoints */
--color-midnight: #131c2b; /* Midnight: panels, tiles, theme color */
--color-sky: #0e1522; /* night-sky ground */
--color-starlight: #f4f2ed; /* Starlight: text on dark, paper ground */
/* Semantic tokens (resolve per theme/section via variables). */
--color-surface: var(--surface);
@@ -53,9 +59,16 @@
--color-secondary: var(--secondary);
/* Typography. */
--font-sans: "Plus Jakarta Sans Variable", "Inter Variable", ui-sans-serif,
system-ui, sans-serif;
--font-body: "Inter Variable", ui-sans-serif, system-ui, sans-serif;
/* Typography. Cinzel for names, wordmarks and headings (never paragraphs);
* Montserrat for body, UI, labels and uppercase eyebrows. JetBrains Mono is
* kept only for genuinely code-like blocks. */
--font-display: "Cinzel Variable", Cinzel, "Trajan Pro", Georgia, serif;
--font-sans: "Montserrat Variable", Montserrat, "Helvetica Neue", Arial,
ui-sans-serif, sans-serif;
--font-body: "Montserrat Variable", Montserrat, "Helvetica Neue", Arial,
ui-sans-serif, sans-serif;
--font-eyebrow: "Montserrat Variable", Montserrat, "Helvetica Neue", Arial,
ui-sans-serif, sans-serif;
--font-mono: "JetBrains Mono Variable", ui-monospace, "SFMono-Regular", monospace;
/* Motion tokens (used across reveal + transition classes). */
@@ -107,10 +120,11 @@
h2,
h3,
h4 {
font-family: var(--font-sans);
font-family: var(--font-display);
font-weight: 700;
line-height: 1.15;
letter-spacing: -0.02em;
/* Cinzel is a capital-led display face: open tracking, never negative. */
letter-spacing: 0.04em;
color: var(--color-text);
}
@@ -131,7 +145,7 @@
transition: color 0.2s var(--ease-out-soft);
}
a:hover {
color: var(--color-amber-soft);
color: var(--color-gold-bright);
}
/* Visible, on-brand focus ring for keyboard users. */
@@ -142,8 +156,8 @@
}
::selection {
background: var(--color-amber);
color: var(--color-ink);
background: var(--color-teal);
color: var(--color-midnight);
}
/* Better default for images: never overflow, lazy by default in components. */
@@ -184,7 +198,7 @@
}
.card-interactive:hover {
transform: translateY(-4px);
box-shadow: 0 18px 40px -24px rgba(11, 16, 38, 0.55);
box-shadow: 0 18px 40px -24px rgba(14, 21, 34, 0.6);
border-color: var(--color-accent);
}
@@ -202,7 +216,7 @@
border: 1px solid color-mix(in srgb, var(--color-accent) 35%, transparent);
}
/* Primary (amber) and secondary (outlined) CTA buttons. */
/* Primary (Patiun Teal) and secondary (outlined) CTA buttons. */
.btn {
display: inline-flex;
align-items: center;
@@ -224,14 +238,15 @@
.btn:active {
transform: translateY(1px);
}
/* Filled buttons: Patiun Teal with Midnight text, never white text. */
.btn-primary {
background: var(--color-amber);
color: var(--color-ink);
box-shadow: 0 10px 30px -12px rgba(245, 158, 11, 0.7);
background: var(--color-teal);
color: var(--color-midnight);
box-shadow: 0 10px 30px -12px rgba(20, 163, 154, 0.7);
}
.btn-primary:hover {
background: var(--color-amber-soft);
color: var(--color-ink);
background: var(--color-teal-bright);
color: var(--color-midnight);
transform: translateY(-2px);
}
.btn-secondary {
@@ -246,6 +261,14 @@
}
}
/* Night-sky bands (nav, page heroes). The semantic Tailwind tokens resolve at
* :root, where the Starlight values live, so anything sitting on the dark sky
* re-points accent and gold to their on-dark brand values here. */
.on-sky {
--color-accent: var(--color-teal-bright);
--color-secondary: var(--color-gold-bright);
}
/* Visually hidden but available to screen readers. */
.sr-only {
position: absolute;
+78 -67
View File
@@ -5,9 +5,14 @@
*
* 1. Global light/dark mode -> set on <html data-theme="light|dark">
* 2. Section surface tone -> set on <body data-surface="...">
* data-surface="neutral" Home / About / Contact (deep night-sky)
* data-surface="professional" Professional wing (obsidian + violet/gold)
* data-surface="interest" Interests wing (candlelit study)
* data-surface="neutral" Home / About / Contact (Patiun night sky)
* data-surface="professional" Professional wing (Tide: slate-teal night)
* data-surface="interest" Interests wing (Nebula: indigo night)
*
* PALETTE: every value here comes from the Patiun brand (Night sky theme for
* dark, Starlight theme for light). Text uses Starlight tones, links and
* accents use teal, small gold labels use the bright gold, and violet/indigo
* only tint the surfaces (never text).
*
* SELECTOR MODEL: data-theme lives on <html>, data-surface on <body>. We match
* the descendant combination `html[data-theme=...] body[data-surface=...]`.
@@ -26,93 +31,99 @@
* DEFAULT / NEUTRAL SURFACE (Home, About, Contact)
* ======================================================================== */
/* Light + neutral — fallback (not currently used; dark is forced). */
/* Light + neutral: Starlight paper (fallback; not currently used, dark is
* forced in theme-init, but these are the values the content sections
* actually render with, since the Tailwind tokens resolve at :root). The
* brand's deep teal #0F8C84 is 3.7:1 on Starlight, so --accent uses a
* slightly darker teal that passes AA for small text. */
:root,
html[data-theme='light'] body[data-surface='neutral'] {
--surface: #efe7d2;
--surface-raised: #f7f0dc;
--surface-sunken: #e3d9bd;
--text: #1b1730;
--text-muted: #4a445e;
--text-subtle: #6b6582;
--border: #d2c6a2;
--accent: #7e5512;
--accent-contrast: #f7f0dc;
--secondary: #5e4f9e;
--surface: #f4f2ed; /* Starlight */
--surface-raised: #ffffff;
--surface-sunken: #e9e5dc;
--text: #131c2b; /* Midnight */
--text-muted: #4a5f6b;
--text-subtle: #5c6e78;
--border: #d9d4c8;
--accent: #0a726b; /* deep teal, darkened to 5.2:1 so small links pass AA */
--accent-contrast: #f4f2ed;
--secondary: #8a6516; /* deep gold, 4.75:1: small gold text and eyebrows */
}
/* Dark + neutral — the canonical deep mystical nightscape. High-contrast text. */
/* Dark + neutral: the canonical Patiun night sky. High-contrast text. */
html[data-theme='dark'] body[data-surface='neutral'] {
--surface: #070a1c; /* near-void midnight indigo */
--surface-raised: #11163a; /* indigo panel, violet-edged */
--surface-sunken: #04061a; /* deep cosmic void */
--text: #f6f4ff; /* near-white for max legibility */
--text-muted: #d4d0f0; /* bright enough to read clearly on deep blue */
--text-subtle: #a8a2d8; /* lifted from the old #7e79b8 for contrast */
--border: #2e3470;
--accent: #f0c262; /* slightly lifted gold for pop on dark */
--accent-contrast: #070a1c;
--secondary: #b3a4ec; /* brighter violet, easier on the eye */
--surface: #0e1522; /* night sky */
--surface-raised: #131c2b; /* Midnight panel */
--surface-sunken: #0a1019; /* deeper void */
--text: #ece9e2; /* Starlight text, 15:1 */
--text-muted: #b9c8cf;
--text-subtle: #9fb3bd; /* brand muted, 7.9:1 on panels */
--border: #26344a;
--accent: #2bc2b7; /* teal on dark, 7.7:1 */
--accent-contrast: #0e1522;
--secondary: #d8b35a; /* gold accents and chips */
}
/* ===========================================================================
* PROFESSIONAL SURFACE — violet-dominant obsidian, dark by default.
* PROFESSIONAL SURFACE: the web side. A slate-teal night (the Tide gradient),
* dark by default.
* ======================================================================== */
body[data-surface='professional'],
html[data-theme='dark'] body[data-surface='professional'] {
--surface: #080a20;
--surface-raised: #121638;
--surface-sunken: #040517;
--text: #f6f4ff;
--text-muted: #cec9f2;
--text-subtle: #a59fd6;
--border: #2e3270;
--accent: #f0c262;
--accent-contrast: #080a20;
--secondary: #b3a4ec;
--surface: #0c1820;
--surface-raised: #12222c;
--surface-sunken: #081117;
--text: #ece9e2;
--text-muted: #b9c8cf;
--text-subtle: #9fb3bd;
--border: #23394a;
--accent: #2bc2b7;
--accent-contrast: #0c1820;
--secondary: #d8b35a;
}
html[data-theme='light'] body[data-surface='professional'] {
--surface: #21183f;
--surface-raised: #2c2152;
--surface-sunken: #18113a;
--text: #f1edff;
--text-muted: #d5cff0;
--text-subtle: #aaa3d6;
--border: #3c3170;
--accent: #7e5512;
--accent-contrast: #f1edff;
--secondary: #b6a6e8;
--surface: #2d4a58; /* Slate Teal: the professional wing stays dark */
--surface-raised: #355766;
--surface-sunken: #243c48;
--text: #f4f2ed;
--text-muted: #d9e2e6;
--text-subtle: #bccbd2;
--border: #46687a;
--accent: #7fdcd5;
--accent-contrast: #131c2b;
--secondary: #e2c57e;
}
/* ===========================================================================
* INTEREST SURFACE — warm plum-brown night, dark by default.
* INTEREST SURFACE: the craft side. An indigo night lit by the Nebula
* gradient, dark by default.
* ======================================================================== */
body[data-surface='interest'],
html[data-theme='dark'] body[data-surface='interest'] {
--surface: #170f24; /* warm plum-brown night */
--surface-raised: #221630;
--surface-sunken: #100920;
--text: #fbf2e4; /* warm near-white */
--text-muted: #ecdcc6; /* lifted for contrast */
--text-subtle: #c4ab8e; /* lifted from #a99a86 */
--border: #3e2f4a;
--accent: #f0c262;
--accent-contrast: #170f24;
--secondary: #c8abe0;
--surface: #110f22; /* indigo night */
--surface-raised: #1a1730;
--surface-sunken: #0b0a18;
--text: #ece9e2;
--text-muted: #c3bfd6;
--text-subtle: #a49fbd;
--border: #2e2a52;
--accent: #2bc2b7;
--accent-contrast: #110f22;
--secondary: #d8b35a;
}
html[data-theme='light'] body[data-surface='interest'] {
--surface: #f4e9cf;
--surface-raised: #fdf4d9;
--surface-sunken: #e8d8b2;
--text: #22152e;
--text-muted: #5c4733;
--text-subtle: #826a4f;
--border: #d8c290;
--accent: #7e5512;
--accent-contrast: #fdf4d9;
--secondary: #5e4f9e;
--surface: #f4f2ed;
--surface-raised: #ffffff;
--surface-sunken: #e9e5dc;
--text: #131c2b;
--text-muted: #4a5f6b;
--text-subtle: #5c6e78;
--border: #d9d4c8;
--accent: #0f8c84;
--accent-contrast: #131c2b;
--secondary: #8a6516;
}