diff --git a/docs/THEMING.md b/docs/THEMING.md index 9fa9cb2..5c1aa93 100644 --- a/docs/THEMING.md +++ b/docs/THEMING.md @@ -137,3 +137,20 @@ Self-hosted variable fonts via Fontsource (woff2, no external requests): Font sizes use `clamp()` for fluid scaling (see the `h1`-`h4` rules in `global.css`). + +--- + +## Icons and motifs + +- **Icons:** `` (Astro) and `` (React + islands) render the Patiun stroke icon set in `src/lib/brand-icons.ts`. Call + sites can use either the brand name (`dice`) or the mdi name (`dice-multiple`); + `brandIconAliases` maps one to the other. Only third-party logos (GitHub, + LinkedIn, Steam, ...) fall back to mdi. The gold star accent uses + `--icon-gold` (deep gold on Starlight, flat gold inside `.on-sky`). The file is + generated from the brand source (`newicons.py`); add icons there. +- **Motifs:** `` shows a brand motif from `public/motifs/`, + copied from the brand files with ring text outlined to paths. `-light` files + are the Starlight recolor. One focal motif per surface, kept faint. +- **Gold accents:** image frames, quote bars, dividers and rules use + `--color-gold`; teal stays for links, buttons and icons. diff --git a/public/brand/patiun-seal.svg b/public/brand/patiun-seal.svg new file mode 100644 index 0000000..60c4a59 --- /dev/null +++ b/public/brand/patiun-seal.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/motifs/astrolabe.svg b/public/motifs/astrolabe.svg new file mode 100644 index 0000000..9423cf6 --- /dev/null +++ b/public/motifs/astrolabe.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/public/motifs/layered-seal.svg b/public/motifs/layered-seal.svg new file mode 100644 index 0000000..9a8952d --- /dev/null +++ b/public/motifs/layered-seal.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/public/motifs/transmutation-seal-light.svg b/public/motifs/transmutation-seal-light.svg new file mode 100644 index 0000000..cdf9a07 --- /dev/null +++ b/public/motifs/transmutation-seal-light.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/public/motifs/tria-prima-array-seal-light.svg b/public/motifs/tria-prima-array-seal-light.svg new file mode 100644 index 0000000..723826e --- /dev/null +++ b/public/motifs/tria-prima-array-seal-light.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/components/Icon.astro b/src/components/Icon.astro index 15efe5a..3cfa314 100644 --- a/src/components/Icon.astro +++ b/src/components/Icon.astro @@ -2,17 +2,17 @@ /** * Icon.astro * --------------------------------------------------------------------------- - * Inline mdi (Material Design Icons) renderer. Pulls icon path data at build - * time from `@iconify-json/mdi/icons.json` and emits a single inline , so - * there is no runtime icon library and no extra requests. The svg inherits - * `currentColor`, so color is controlled via text-* utilities or inline style. - * - * The spec calls for mdi icons everywhere instead of emojis; all spec emoji - * references are mapped to mdi names at the call sites. + * Inline icon renderer. Names resolve to the Patiun brand icon set first + * (src/lib/brand-icons.ts: stroke icons with a gold star accent), via either a + * brand name ("dice") or the mdi name the call sites already use ("dice-multiple"). + * Anything not in the brand set, which is only third-party logos like GitHub or + * LinkedIn, falls back to mdi (Material Design Icons) path data. Either way the + * output is one inline that inherits `currentColor`. * * Usage: */ import mdi from '@iconify-json/mdi/icons.json'; +import { brandIconBody } from '@lib/brand-icons'; interface Props { /** mdi icon name WITHOUT prefix, e.g. "briefcase". */ @@ -27,8 +27,11 @@ interface Props { const { name, title, class: className, size, style } = Astro.props; -const icon = (mdi as { icons: Record }).icons[name]; -if (!icon) { +const brand = brandIconBody(name); +const icon = brand + ? undefined + : (mdi as { icons: Record }).icons[name]; +if (!brand && !icon) { // Surface missing-icon names during dev so typos are caught immediately. console.warn(`[Icon] unknown mdi icon: "${name}"`); } @@ -39,9 +42,14 @@ if (!icon) { width={size ?? '1em'} height={size ?? '1em'} viewBox="0 0 24 24" + fill={brand ? 'none' : undefined} + stroke={brand ? 'currentColor' : undefined} + stroke-width={brand ? 1.6 : undefined} + stroke-linecap={brand ? 'round' : undefined} + stroke-linejoin={brand ? 'round' : undefined} class={className} style={style} aria-hidden={title ? undefined : 'true'} role={title ? 'img' : undefined} - set:html={(title ? `${title}` : '') + (icon?.body ?? '')} + set:html={(title ? `${title}` : '') + (brand ?? icon?.body ?? '')} /> diff --git a/src/components/brand/BrandIcon.tsx b/src/components/brand/BrandIcon.tsx new file mode 100644 index 0000000..3b72c2c --- /dev/null +++ b/src/components/brand/BrandIcon.tsx @@ -0,0 +1,37 @@ +/** + * BrandIcon.tsx + * --------------------------------------------------------------------------- + * React twin of Icon.astro for islands: renders a Patiun brand icon (by brand + * name or mdi alias) as an inline stroke SVG that inherits currentColor. + */ +import { brandIconBody } from '@lib/brand-icons'; + +interface Props { + name: string; + size?: number; + className?: string; + /** Accessible label; omit when the icon sits beside visible text. */ + title?: string; +} + +export default function BrandIcon({ name, size = 20, className, title }: Props) { + const body = brandIconBody(name) ?? ''; + return ( + + ); +} diff --git a/src/components/brand/Motif.astro b/src/components/brand/Motif.astro new file mode 100644 index 0000000..dda292e --- /dev/null +++ b/src/components/brand/Motif.astro @@ -0,0 +1,39 @@ +--- +/** + * Motif.astro + * --------------------------------------------------------------------------- + * A Patiun brand motif ("The Alchemist's Instrument") as a decorative image. + * Files live in public/motifs/ and are copied from branding/motifs/svg with + * their ring text and symbols outlined to paths, so they render without the + * Cinzel or Noto Sans Symbols fonts. `-light` files are the Starlight-ground + * recolor (deep teal, deep gold, Midnight symbols); the rest are drawn for the + * night sky. + * + * Brand rules: one focal motif per surface, and keep motifs faint behind + * content (they are texture, not illustration). + * + * Usage: + * + */ +interface Props { + name: + | "layered-seal" + | "astrolabe" + | "transmutation-seal-light" + | "tria-prima-array-seal-light"; + class?: string; + style?: string; +} +const { name, class: className, style } = Astro.props; +--- + + diff --git a/src/components/brand/PatiunGlyph.astro b/src/components/brand/PatiunGlyph.astro deleted file mode 100644 index fe68e2f..0000000 --- a/src/components/brand/PatiunGlyph.astro +++ /dev/null @@ -1,40 +0,0 @@ ---- -/** - * 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: - */ -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; ---- - - - - - - - - diff --git a/src/components/hero/AnimatedBackground.tsx b/src/components/hero/AnimatedBackground.tsx index 644fa9e..7d1f4f1 100644 --- a/src/components/hero/AnimatedBackground.tsx +++ b/src/components/hero/AnimatedBackground.tsx @@ -6,8 +6,8 @@ * 1. A CSS-driven night-sky gradient (deep blue, darker at top → lighter near * horizon) plus a diagonal Milky Way galaxy band and teal/violet nebula * clouds. GPU-friendly, animated via `animate-gradient-shift` keyframes. - * 2. A faint static SVG ornament layer (astrolabe + sacred geometry) for - * esoteric texture. + * 2. The Patiun layered magic seal, faint and slowly turning in the top-left + * corner (the surface's one focal motif). * 3. An optional star field: a dense field of slowly-twinkling stars * with thin constellation lines between near neighbors, plus the occasional * shooting star. The WHOLE field rotates together very slowly around the @@ -359,55 +359,17 @@ export default function AnimatedBackground({ `, }} /> - {/* Faint astrolabe + sacred-geometry ornaments for esoteric texture. */} - - + decoding="async" + draggable={false} + className="pointer-events-none absolute -left-40 -top-40 h-[34rem] w-[34rem] animate-spin-slow select-none opacity-[0.2]" + /> {/* Subtle vignette for text legibility. */}
{/* Faint static starfield — carries the night-sky treatment into the footer. */}
- +

{siteIdentity.name} @@ -56,6 +56,6 @@ const socialList = Object.values(socials);

- © {year} {siteIdentity.name}. All rights reserved. + © {year} {siteIdentity.name} · {siteIdentity.lockupLine}. All rights reserved.
diff --git a/src/components/layout/InterestHero.astro b/src/components/layout/InterestHero.astro index e1620b4..046f259 100644 --- a/src/components/layout/InterestHero.astro +++ b/src/components/layout/InterestHero.astro @@ -84,7 +84,7 @@ const {

{tagline}

@@ -97,7 +97,7 @@ const {
diff --git a/src/components/mystic/AlchemicalDivider.astro b/src/components/mystic/AlchemicalDivider.astro index 4326e23..d0ba5c5 100644 --- a/src/components/mystic/AlchemicalDivider.astro +++ b/src/components/mystic/AlchemicalDivider.astro @@ -2,16 +2,13 @@ /** * AlchemicalDivider.astro * --------------------------------------------------------------------------- - * A horizontal section divider built from a thin rule with alchemical / - * celestial symbols along it: a central sun/star flanked by a crescent moon - * and small sparkles, with hairlines tapering to the edges. Replaces plain - * vertical gaps between sections on Home / About / Interests. - * - * Decorative: aria-hidden, inherits currentColor (set to --color-accent or - * --color-violet at the call site). + * Section break in the Patiun star-line style: a dotted (faint) run leading + * into a solid (main) run on each side, each stopping short of the stars, with + * a gold four-point star at the center flanked by two small ones. Inherits + * currentColor; call sites pass Alchemist Gold. * * Usage: - * + * */ interface Props { class?: string; @@ -28,30 +25,12 @@ const { class: className = '', style = '' } = Astro.props; stroke="currentColor" stroke-linecap="round" > - {/* Tapered hairlines either side of the central cluster. */} - - - - - - {/* Crescent moon (left). */} - - - {/* Central radiant sun/star (alchemical sun: circle + rays). */} - - - - - - - - - - - - - {/* Small sparkles. */} - - + {/* Faint links: dotted runs at the outer ends. */} + + {/* Main figure: solid runs that stop short of the stars. */} + + + +
diff --git a/src/components/mystic/Astrolabe.astro b/src/components/mystic/Astrolabe.astro deleted file mode 100644 index c9b0c3b..0000000 --- a/src/components/mystic/Astrolabe.astro +++ /dev/null @@ -1,76 +0,0 @@ ---- -/** - * Astrolabe.astro - * --------------------------------------------------------------------------- - * Inline-SVG astrolabe / celestial-chart line art: concentric rings, tick - * marks like a degree scale, zodiac-like division lines, and a star at the - * pivot. Reads as a "mystic instrument" silhouette. Intended as a hero-corner - * ornament where it can slowly rotate (animate-spin-slow). - * - * Decorative: aria-hidden, pointer-events none, inherits currentColor. Apply - * opacity + color at the call site so it reads as a faint engraving. - * - * Usage: - * - */ -interface Props { - class?: string; - style?: string; -} -const { class: className = '', style = '' } = Astro.props; - -// Degree ticks every 10deg around the outer ring (r=48, on a 100x100 canvas). -const ticks: { x1: number; y1: number; x2: number; y2: number; major: boolean }[] = []; -const cx = 50; -const cy = 50; -for (let deg = 0; deg < 360; deg += 10) { - const rad = (deg * Math.PI) / 180; - const major = deg % 30 === 0; - const outer = 48; - const inner = major ? 43 : 45.5; - ticks.push({ - x1: cx + outer * Math.cos(rad), - y1: cy + outer * Math.sin(rad), - x2: cx + inner * Math.cos(rad), - y2: cy + inner * Math.sin(rad), - major, - }); -} -// Zodiac-like spokes (12 divisions). -const spokes: [number, number][] = []; -for (let i = 0; i < 12; i++) { - const rad = (i * 30 * Math.PI) / 180; - spokes.push([cx + 40 * Math.cos(rad), cy + 40 * Math.sin(rad)]); -} ---- - diff --git a/src/components/mystic/CelestialFrame.astro b/src/components/mystic/CelestialFrame.astro index ea0dcca..316315f 100644 --- a/src/components/mystic/CelestialFrame.astro +++ b/src/components/mystic/CelestialFrame.astro @@ -36,7 +36,7 @@ const innerPx = Math.max(0, outerPx - bw);
{/* Faint sacred-geometry tint behind the image (fills the full figure). */}
- {/* Corner star-burst flourishes (sit on the border corners). */} + {/* Corner four-point stars in Alchemist Gold (sit on the border corners). */}
diff --git a/src/components/mystic/SacredGeometry.astro b/src/components/mystic/SacredGeometry.astro deleted file mode 100644 index 591bc10..0000000 --- a/src/components/mystic/SacredGeometry.astro +++ /dev/null @@ -1,85 +0,0 @@ ---- -/** - * SacredGeometry.astro - * --------------------------------------------------------------------------- - * Inline-SVG sacred-geometry line art: a flower-of-life / metatron's-cube - * inspired figure rendered as thin gold strokes. Used as an extremely faint - * (low-opacity) decorative background behind hero blocks and section openers — - * it should read as texture, never compete with content. - * - * Purely decorative: aria-hidden, pointer-events none, inherits currentColor. - * Apply opacity + sizing + color at the call site. - * - * Usage: - * - */ -interface Props { - class?: string; - style?: string; - /** Which figure to draw. */ - figure?: 'flower' | 'metatron'; -} -const { class: className = '', style = '', figure = 'flower' } = Astro.props; - -// Flower of life: a central circle surrounded by 6, then 12, then edge circles -// of the same radius, all r=10 on a 100x100 canvas centered at 50,50. -const r = 10; -const cx = 50; -const cy = 50; -const rings: [number, number][] = []; -// Ring 1: 6 circles around center. -for (let i = 0; i < 6; i++) { - const a = (Math.PI / 3) * i; - rings.push([cx + r * Math.cos(a), cy + r * Math.sin(a)]); -} -// Ring 2: 6 more, one step further on alternating angles (the classic pattern). -for (let i = 0; i < 6; i++) { - const a = (Math.PI / 3) * i + Math.PI / 6; - rings.push([cx + r * Math.sqrt(3) * Math.cos(a), cy + r * Math.sqrt(3) * Math.sin(a)]); -} -// Ring 3 (outer partial): completes the visible flower. -for (let i = 0; i < 6; i++) { - const a = (Math.PI / 3) * i; - rings.push([cx + 2 * r * Math.cos(a), cy + 2 * r * Math.sin(a)]); -} -const flowerCircles: [number, number][] = [[cx, cy], ...rings]; - -// Metatron's cube: 13 circles on a triangular grid + all internal connecting lines. -const metatronCenters: [number, number][] = [[cx, cy]]; -for (let i = 0; i < 6; i++) { - const a = (Math.PI / 3) * i; - metatronCenters.push([cx + 2 * r * Math.cos(a), cy + 2 * r * Math.sin(a)]); -} -for (let i = 0; i < 6; i++) { - const a = (Math.PI / 3) * i + Math.PI / 6; - metatronCenters.push([cx + 2 * r * Math.sqrt(3) * Math.cos(a), cy + 2 * r * Math.sqrt(3) * Math.sin(a)]); -} ---- - diff --git a/src/components/mystic/StarBurst.astro b/src/components/mystic/StarBurst.astro index a70cfc8..c3505ec 100644 --- a/src/components/mystic/StarBurst.astro +++ b/src/components/mystic/StarBurst.astro @@ -34,6 +34,7 @@ const sw = variant === 'outline' ? 1.4 : 0; stroke-linejoin="round" aria-hidden="true" > - {/* Four-point sparkle: two perpendicular sharp diamonds + small inner core. */} - + {/* The Patiun four-point star: thin straight arms (inner radius 0.3 of the + outer), the same star used on the seal, the icons and the favicon. */} + diff --git a/src/components/nav/MobileMenu.tsx b/src/components/nav/MobileMenu.tsx index b976da7..f3ad27a 100644 --- a/src/components/nav/MobileMenu.tsx +++ b/src/components/nav/MobileMenu.tsx @@ -12,6 +12,7 @@ */ import { useEffect, useRef, useState } from 'react'; import { AnimatePresence, motion } from 'framer-motion'; +import BrandIcon from '@components/brand/BrandIcon'; export interface MobileNavItem { label: string; @@ -28,31 +29,9 @@ interface Props { currentPath: string; } -// Small inline mdi-like icons keyed by name (keeps the menu self-contained). -const ICON_PATHS: Record = { - home: 'M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z', - account: 'M12 4a4 4 0 1 0 0 8 4 4 0 0 0 0-8zm0 10c-4.4 0-8 2.2-8 5v1h16v-1c0-2.8-3.6-5-8-5z', - briefcase: - 'M10 16h4a1 1 0 0 0 1-1v-1H7a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h2V4a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v1h2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-1v1a3 3 0 0 1-3 3h-4a3 3 0 0 1-3-3v-1z', - 'dice-multiple': - 'M6 3h5v5H6V3zm7 0h5v5h-5V3zM3 11h5v5H3v-5zm7 0h5v5h-5v-5zm7 0h-5v5h5v-5z', - email: - 'M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2zm8 7L4 6v1l8 5 8-5V6l-8 5z', - // Store nav icons (kept here so the store mobile menu is self-contained). - store: 'M4 7h16l-1 13H5L4 7zm5-3a3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0 3 3', - 'cards-playing': 'M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm3 4v3m6 7v-3', - 'printer-3d-nozzle': - 'M7 2h10v4l3 3-4 2H8L4 9l3-3V2zm5 11v9', - 'clipboard-list': - 'M9 3h6v2H9V3zm-2 0v2H6a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-1V3M7 11h2m4 0h4M7 15h2m4 0h4', -}; - +// Patiun brand icons, resolved by the same names the nav config uses. function Icon({ name, className }: { name: string; className?: string }) { - return ( - - ); + return ; } function isActive(href: string, path: string): boolean { @@ -90,17 +69,13 @@ export default function MobileMenu({ groups, currentPath }: Props) { aria-expanded={open} aria-controls="mobile-menu" aria-label={open ? 'Close menu' : 'Open menu'} - className="inline-flex h-10 w-10 items-center justify-center rounded-lg border" - style={{ color: 'var(--color-text)', borderColor: 'var(--color-border)' }} + className="relative z-[60] inline-flex h-10 w-10 items-center justify-center rounded-lg border" + style={{ color: 'var(--color-starlight)', borderColor: 'color-mix(in srgb, var(--color-gold) 45%, transparent)' }} > {open ? ( - - - + ) : ( - - - + )} @@ -124,7 +99,11 @@ export default function MobileMenu({ groups, currentPath }: Props) { exit={{ x: '100%' }} transition={{ type: 'tween', duration: 0.28 }} className="fixed right-0 top-0 z-50 flex h-[100dvh] w-[min(82vw,22rem)] flex-col gap-6 overflow-y-auto p-6 pt-20 shadow-2xl" - style={{ backgroundColor: 'var(--color-surface-raised)' }} + style={{ + // Night-sky drawer to match the nav it opens from. + backgroundColor: 'var(--color-midnight)', + borderLeft: '1px solid color-mix(in srgb, var(--color-gold) 45%, transparent)', + }} > {groups.map((group, gi) => (