mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-08 04:08:24 +00:00
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:
@@ -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>
|
||||
@@ -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>}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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). */}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 · {label}
|
||||
</span>
|
||||
{hint && <p class="text-sm" style="color: var(--color-text-muted);">{hint}</p>}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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" />}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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. */}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
|
||||
@@ -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 & Exploring
|
||||
</h3>
|
||||
<ul class="grid gap-3 sm:grid-cols-3">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)' }}>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);">
|
||||
|
||||
@@ -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 & Media
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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 & 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 & 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?
|
||||
|
||||
@@ -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 — the meters above
|
||||
show live availability, and the order form appears as soon as orders reopen. You can
|
||||
|
||||
+49
-26
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user