diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png new file mode 100644 index 0000000..eb0469d Binary files /dev/null and b/public/android-chrome-192x192.png differ diff --git a/public/android-chrome-512x512.png b/public/android-chrome-512x512.png new file mode 100644 index 0000000..9f5102f Binary files /dev/null and b/public/android-chrome-512x512.png differ diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..d235de2 Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 0000000..d500618 Binary files /dev/null and b/public/favicon-16x16.png differ diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png new file mode 100644 index 0000000..307e061 Binary files /dev/null and b/public/favicon-32x32.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..d698140 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/icon_2.png b/public/icon_2.png new file mode 100644 index 0000000..d2a4639 Binary files /dev/null and b/public/icon_2.png differ diff --git a/public/site.webmanifest b/public/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/public/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/src/components/hero/AnimatedBackground.tsx b/src/components/hero/AnimatedBackground.tsx index 3dc7601..ffc0280 100644 --- a/src/components/hero/AnimatedBackground.tsx +++ b/src/components/hero/AnimatedBackground.tsx @@ -134,12 +134,12 @@ export default function AnimatedBackground({ const cy = 0; const maxR = Math.sqrt(cx * cx + height * height) + 40; - // Dense starfield: ~3x the previous density for a richly star-packed sky. - // One star per ~1100 px^2 of the disk, capped for perf (~15% anchors). + // Ultra-dense starfield: ~5x the previous density for a richly packed sky. + // One star per ~220 px^2 of the disk, capped for perf (~17% anchors). const diskArea = Math.PI * maxR * maxR; - const count = Math.min(1500, Math.floor(diskArea / 1100)); + const count = Math.min(3000, Math.floor(diskArea / 220)); stars = Array.from({ length: count }, (_, i) => { - const anchor = i % 7 === 0; // ~15% anchors -> dense constellation backbone + const anchor = i % 6 === 0; // ~17% anchors -> very dense constellation backbone // Uniform-ish distribution across the disk via sqrt on radius. const rr = maxR * Math.sqrt(Math.random()); const a = Math.random() * Math.PI * 2; diff --git a/src/components/layout/InterestHero.astro b/src/components/layout/InterestHero.astro index 95aca1b..fc8f301 100644 --- a/src/components/layout/InterestHero.astro +++ b/src/components/layout/InterestHero.astro @@ -41,12 +41,12 @@ const { } = Astro.props; --- -
+
-
+
(or any media) passed via the default - * slot. + * An ornamental frame for imagery: rounded image container with a gold border + * + small star-burst flourishes in the corners and a faint sacred-geometry tint + * behind. Wraps an (or any media) passed via the default slot. * - * The frame is structural (not just decorative), so it owns the rounded - * corners + border + overflow. The corner sparkles are absolutely positioned - * SVGs that inherit the accent color. - * - * Usage: - * - * ... - * - * ... + * CORNER FIT: the gold border lives on the outer
. The media container + * is INSET by exactly the border width (via --frame-bw) and its corner radius + * is reduced by the same amount, so the image nests flush inside the border at + * every corner — no gap, no mismatch. Adjust --frame-bw here in one place if + * the border width changes. */ import StarBurst from "./StarBurst.astro"; @@ -32,30 +27,33 @@ const { emphasis = "subtle", } = Astro.props; -const radius = { - none: "rounded-none", - lg: "rounded-lg", - xl: "rounded-xl", - "2xl": "rounded-2xl", -}[rounded]; - -const borderStyle = emphasis === "strong" ? "border-2" : "border"; +// Outer radius (px) per option. The inner media radius is outer - border width +// so the image corner nests perfectly inside the border's corner. +const outerPx = { none: 0, lg: 8, xl: 12, "2xl": 16 }[rounded]; +const bw = emphasis === "strong" ? 4 : 3; // border width in px (matches border-2 / 3px) +const innerPx = Math.max(0, outerPx - bw); ---
- {/* Faint sacred-geometry tint behind the image. */} + {/* Faint sacred-geometry tint behind the image (fills the full figure). */} - {/* The media itself, clipped to the frame. */} -
+ {/* The media itself, in normal flow (defines the figure's height via the slot's + aspect-ratio div). Its radius = outer radius - border width, so the image's + rounded corner nests PERFECTLY inside the border's rounded corner. The + figure's `overflow: hidden` clips the tint layer to the outer radius. */} +
@@ -79,3 +77,5 @@ const borderStyle = emphasis === "strong" ? "border-2" : "border"; style="color: var(--color-accent); opacity:.7;" />
+ + diff --git a/src/components/mystic/CircuitField.tsx b/src/components/mystic/CircuitField.tsx index fecc480..388e806 100644 --- a/src/components/mystic/CircuitField.tsx +++ b/src/components/mystic/CircuitField.tsx @@ -69,9 +69,9 @@ export default function CircuitField({ canvas.height = Math.floor(height * dpr); ctx.setTransform(dpr, 0, 0, dpr, 0, 0); - // Build an orthogonal circuit on a grid: place nodes at grid intersections - // (with jitter + random omissions), then connect neighbors with right-angle - // traces. This reads as a PCB / schematic, not a star map. + // Build a clean, ALIGNED orthogonal circuit on a grid: pads sit exactly at + // grid intersections (no jitter) and traces connect them in straight + // right-angle lines. Reads as an orderly schematic / PCB layout. const step = 64; const cols = Math.ceil(width / step) + 1; const rows = Math.ceil(height / step) + 1; @@ -83,9 +83,8 @@ export default function CircuitField({ // ~62% of intersections get a pad. present[r][c] = Math.random() < 0.62; if (present[r][c]) { - const jx = (Math.random() - 0.5) * step * 0.25; - const jy = (Math.random() - 0.5) * step * 0.25; - nodes.push({ x: c * step + jx, y: r * step + jy }); + // Snapped to the grid — no jitter, so traces land flush on pads. + nodes.push({ x: c * step, y: r * step }); } } } @@ -236,21 +235,15 @@ export default function CircuitField({ className={`pointer-events-none absolute inset-0 -z-10 overflow-hidden ${className}`} aria-hidden="true" > - {/* Base dark purple/blue gradient — the professional hero's atmosphere. */} + {/* Base gradient — IDENTICAL colors to AnimatedBackground's night sky so + the morph between star field and circuit field is color-consistent. + No nebula radials here (the user asked to keep the circuit style only); + just the clean deep-blue sky base. */}
- {/* Animated violet/teal nebula clouds for depth + motion. */} -
{/* Circuit traces + nodes + signal pulses. */} diff --git a/src/components/mystic/ConstellationField.tsx b/src/components/mystic/ConstellationField.tsx index 10d4d73..44cd174 100644 --- a/src/components/mystic/ConstellationField.tsx +++ b/src/components/mystic/ConstellationField.tsx @@ -92,12 +92,11 @@ export default function ConstellationField({ const cx = width; const cy = 0; const maxR = Math.sqrt(cx * cx + height * height) + 40; - // Denser field for richer constellations. One star per ~4500 px^2, scaled - // ~3x density for a richly star-packed sky. One star per ~1500 px^2, - // scaled by density; ~15% anchors for a dense constellation backbone. + // Ultra-dense: ~5x. One star per ~300 px^2, scaled by density; ~17% + // anchors for a very dense constellation backbone. const count = Math.min( - 900, - Math.floor(((Math.PI * maxR * maxR) / 1500) * density), + 3000, + Math.floor(((Math.PI * maxR * maxR) / 300) * density), ); stars = Array.from({ length: count }, (_, i) => { const anchor = i % 7 === 0; diff --git a/src/components/professional/SectionNav.astro b/src/components/professional/SectionNav.astro index 3e924be..b735bcc 100644 --- a/src/components/professional/SectionNav.astro +++ b/src/components/professional/SectionNav.astro @@ -2,14 +2,15 @@ /** * SectionNav.astro * --------------------------------------------------------------------------- - * Sticky in-page sub-navigation for the Professional page. Jumps between the - * six anchor sections (Summary, Experience, Skills, Projects, Education, - * Contact) via smooth scroll. Hidden on small screens where the page reads as a - * simple scroll. Active state is computed client-side via IntersectionObserver. + * Fixed LEFT-SIDE vertical rail for the Professional page. Shows the six + * sections as a stacked icon+label list, highlights the section currently in + * view, and lets the user click any item to smooth-scroll to it. A vertical + * progress line beside the items fills as the user scrolls down the page. * - * Sits BELOW the hero and is OPAQUE (a solid dark nebula surface) so it remains - * readable while scrolling over content. Sticky so it tracks the viewport once - * the user scrolls past it. + * Stylistically it mirrors the page's `.card` language (same raised surface, + * border, radius) so it reads as a sibling of the content cards rather than a + * separate chrome element. Icons + labels use bright tokens so they pop on the + * dark surface. Hidden below `lg`. */ import Icon from "@components/Icon.astro"; const sections = [ @@ -22,28 +23,79 @@ const sections = [ ]; --- -