From 959de3b809f79dba14ffe0631f4dd503d2a759e4 Mon Sep 17 00:00:00 2001 From: ares <285551516+New1Direction@users.noreply.github.com> Date: Mon, 15 Jun 2026 00:28:38 -0700 Subject: [PATCH 01/11] =?UTF-8?q?feat(site):=20bright=20"Inspector"=20rede?= =?UTF-8?q?sign=20=E2=80=94=20interactive=20verdict,=20lens=20sheen,=20the?= =?UTF-8?q?me-scan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the warm manila/amber "Case File" palette (kept reading as brown/Claude-ish) for a clean, bright direction the lens-mascot actually belongs in. - Palette: cool near-white base + near-black ink + vivid blue (#2563ff) lead, a cyan scanner glint, and a hot-coral pop (kicker dot, wide-feature lenses). Dark mode recast as a cool slate, not a warm noir. All driven by global.css tokens so marketing + docs reskin together; AA contrast preserved. - Verdict demo is now a live tablist: 7 clickable tabs crossfade the card body, real tab/tabpanel a11y + arrow-key nav, and the "Strong fit" stamp slams in on scroll (the previously-unused --ease-stamp curve). - Hero: bigger Fraunces type contrast, a soft specular lens sheen drifting over the mascot port (replaced a cheap cyan scan-line), CASE No. kicker. - Bento: accent edge-light + lens-icon tilt on hover; section titles wipe in. - Theme toggle: a top->bottom scanline "develops" the page into the new theme via the View Transitions API (flushSync + clip-path reveal, cyan glow edge), with an instant-flip fallback for unsupported browsers and reduced-motion. All motion is reduced-motion gated. Production build, typecheck, and lint pass. --- website/app/(home)/styles/home.css | 330 ++++++++++++++++++++++-- website/app/(home)/styles/shell.css | 6 +- website/app/global.css | 263 +++++++++++-------- website/app/icon.svg | 8 +- website/components/home/Hero.tsx | 4 +- website/components/home/SiteMotion.tsx | 21 ++ website/components/home/VerdictDemo.tsx | 255 ++++++++++++++++-- website/components/site/ThemeToggle.tsx | 26 +- 8 files changed, 747 insertions(+), 166 deletions(-) diff --git a/website/app/(home)/styles/home.css b/website/app/(home)/styles/home.css index 473c0bc..1fdf943 100644 --- a/website/app/(home)/styles/home.css +++ b/website/app/(home)/styles/home.css @@ -57,6 +57,49 @@ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35), inset 0 -56px 60px -34px var(--accent-glow); } +/* Lens sheen — a soft specular glint drifts across the glass, like light + catching a lens. No hard scan-line. Slow, with a long calm pause between + passes. Reduced-motion: nothing moves. */ +.hero-mascot-stage::before { + content: ''; + position: absolute; + inset: -30%; + z-index: 2; + pointer-events: none; + opacity: 0; + background: linear-gradient( + 100deg, + transparent 41%, + rgba(226, 246, 255, 0.1) 47%, + rgba(240, 251, 255, 0.34) 50%, + rgba(226, 246, 255, 0.1) 53%, + transparent 59% + ); + mix-blend-mode: screen; + transform: translateX(-58%); +} +@media (prefers-reduced-motion: no-preference) { + .hero-mascot-stage::before { + animation: lensSheen 7s var(--ease-out) infinite; + } +} +@keyframes lensSheen { + 0% { + transform: translateX(-58%); + opacity: 0; + } + 6% { + opacity: 1; + } + 19% { + opacity: 1; + } + 27%, + 100% { + transform: translateX(58%); + opacity: 0; + } +} .hero-say { font-size: 0.9rem; color: var(--muted); @@ -66,14 +109,21 @@ text-align: center; } .hero-title { - font-size: clamp(2.5rem, 1rem + 5vw, 4.6rem); + font-size: clamp(2.6rem, 0.9rem + 5.4vw, 5rem); font-weight: 800; - letter-spacing: -0.042em; - line-height: 0.98; + letter-spacing: -0.044em; + line-height: 0.94; color: var(--text-strong); margin: 18px 0 0; text-align: left; } + +/* The verdict keyword — vivid blue, the one word the headline turns on. */ +.hero-mark { + display: inline-block; + color: var(--warm); + white-space: nowrap; +} .hero-sub { max-width: 52ch; margin: 22px 0 0; @@ -158,6 +208,7 @@ } .vd-card { + position: relative; border-radius: 18px; background: linear-gradient(180deg, var(--surface-2), var(--surface)); border: 1px solid var(--border-2); @@ -229,28 +280,58 @@ color: var(--muted); margin-top: 5px; } +/* Tabs are a real tablist now — horizontally scrollable, with a fade mask on + the overflow edge so it reads as "more to the right". */ .vd-tabs { display: flex; gap: 16px; padding: 0 20px; border-bottom: 1px solid var(--border); - overflow: hidden; - mask-image: linear-gradient(90deg, #000 86%, transparent); + overflow-x: auto; + scrollbar-width: none; + scroll-snap-type: x proximity; + mask-image: linear-gradient(90deg, #000 92%, transparent); +} +.vd-tabs::-webkit-scrollbar { + display: none; } .vd-tab { - font-size: 0.82rem; - font-weight: 600; + font: 600 0.82rem/1 var(--sans); color: var(--muted); - padding: 11px 0; + padding: 11px 1px 10px; white-space: nowrap; + scroll-snap-align: start; + background: none; + border: none; border-bottom: 2px solid transparent; + cursor: pointer; + transition: color var(--dur-fast) var(--ease-out), + border-color var(--dur-fast) var(--ease-out); +} +.vd-tab:hover { + color: var(--text-strong); } .vd-tab.is-active { color: var(--text-strong); border-bottom-color: var(--warm); } +.vd-tab:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 3px; + border-radius: 4px; +} + +/* The swappable card body. A fixed min-height keeps the card from jumping as + panels of different lengths cross-fade. */ +.vd-panel { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px 20px 20px; + min-height: 178px; +} + .vd-verdict { - margin: 18px 20px 0; display: flex; align-items: center; gap: 16px; @@ -278,7 +359,6 @@ color: var(--sub); } .vd-bottom { - margin: 12px 20px 0; padding: 14px 16px; border-radius: 12px; background: var(--bg-2); @@ -299,13 +379,13 @@ display: flex; align-items: center; gap: 10px; - padding: 14px 20px 20px; + margin-top: auto; } .vd-lang-chip { font: 700 11px/1 var(--mono); - color: var(--warn); - background: color-mix(in srgb, var(--warn) 16%, transparent); - border: 1px solid color-mix(in srgb, var(--warn) 36%, transparent); + color: var(--accent); + background: var(--accent-weak); + border: 1px solid var(--accent-line); border-radius: 6px; padding: 6px 9px; } @@ -314,6 +394,189 @@ color: var(--faint); } +/* ── Verdict panel content (ELI5 / Technical / Use Cases / Health / etc.) ── */ +.vd-prose p { + font-size: 0.92rem; + color: var(--text); + margin: 0 0 10px; +} +.vd-prose p:last-child { + margin-bottom: 0; +} +.vd-prose code, +.vd-list code { + font: 600 0.84em/1 var(--mono); + color: var(--accent); + background: var(--accent-weak); + border-radius: 4px; + padding: 1px 5px; +} +.vd-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 12px; +} +.vd-list li { + position: relative; + padding-left: 22px; + font-size: 0.9rem; + color: var(--sub); +} +.vd-list li::before { + content: ''; + position: absolute; + left: 0; + top: 0.5em; + width: 8px; + height: 8px; + border-radius: 2px; + background: var(--warm); + transform: rotate(45deg); +} +.vd-list strong { + color: var(--text-strong); + font-weight: 700; +} +.vd-cases { + display: flex; + flex-wrap: wrap; + gap: 8px; +} +.vd-case { + font: 600 0.84rem/1 var(--sans); + color: var(--text); + background: var(--surface); + border: 1px solid var(--border-2); + border-radius: 999px; + padding: 9px 14px; +} +.vd-bars { + display: flex; + flex-direction: column; + gap: 12px; +} +.vd-bar { + display: grid; + grid-template-columns: 9.5ch 1fr 2.5ch; + align-items: center; + gap: 12px; +} +.vd-bar-k { + font: 600 0.8rem/1 var(--sans); + color: var(--sub); +} +.vd-bar-track { + height: 7px; + border-radius: 999px; + background: var(--bg-2); + border: 1px solid var(--border); + overflow: hidden; +} +.vd-bar-fill { + display: block; + height: 100%; + border-radius: inherit; + background: linear-gradient(90deg, var(--warm-2), var(--warm)); +} +.vd-bar-v { + font: 700 0.82rem/1 var(--mono); + color: var(--text-strong); + text-align: right; +} +.vd-clean { + display: flex; + align-items: center; + gap: 14px; + padding: 16px; + border-radius: 12px; + background: color-mix(in srgb, var(--ok) 9%, var(--bg-2)); + border: 1px solid color-mix(in srgb, var(--ok) 32%, transparent); +} +.vd-clean-mark { + display: inline-grid; + place-items: center; + width: 36px; + height: 36px; + flex-shrink: 0; + border-radius: 50%; + color: var(--ok); + background: color-mix(in srgb, var(--ok) 16%, transparent); +} +.vd-clean-k { + font: 700 0.95rem/1.2 var(--sans); + color: var(--text-strong); + margin-bottom: 4px; +} +.vd-clean p { + font-size: 0.88rem; + color: var(--sub); +} +.vd-stack { + margin: 0; + display: flex; + flex-direction: column; + gap: 11px; +} +.vd-stack > div { + display: grid; + grid-template-columns: 11ch 1fr; + gap: 14px; + padding-bottom: 11px; + border-bottom: 1px solid var(--border); +} +.vd-stack > div:last-child { + border-bottom: none; + padding-bottom: 0; +} +.vd-stack dt { + font: 700 10px/1.5 var(--mono); + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--muted); +} +.vd-stack dd { + margin: 0; + font-size: 0.9rem; + color: var(--text); +} + +/* Panel crossfade on tab switch + the verdict stamp's heavy slam on scroll-in. + Both gated — reduced-motion visitors get an instant swap and a static stamp. */ +@media (prefers-reduced-motion: no-preference) { + .vd-panel { + animation: vdPanelIn 260ms var(--ease-out); + } + .vd-panel.is-in .vd-stamp { + animation: vdStampSlam 460ms var(--ease-stamp) both; + } +} +@keyframes vdPanelIn { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: none; + } +} +@keyframes vdStampSlam { + 0% { + opacity: 0; + transform: rotate(11deg) scale(2.3); + } + 68% { + opacity: 0.9; + } + 100% { + opacity: 0.9; + transform: rotate(-7deg) scale(1); + } +} + /* ── Feature bento ───────────────────────────────────────────────────── */ .bento { margin-top: 32px; @@ -322,6 +585,8 @@ gap: 14px; } .feat { + position: relative; + overflow: hidden; grid-column: span 1; border-radius: 16px; padding: 22px 20px; @@ -330,11 +595,25 @@ transition: transform var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out); } +/* Accent edge-light — a brass hairline that draws across the top on hover. */ +.feat::after { + content: ''; + position: absolute; + inset: 0 0 auto 0; + height: 2px; + background: var(--grad-soft); + transform: scaleX(0); + transform-origin: left; + transition: transform var(--dur-slow) var(--ease-out); +} .feat:hover { - transform: translateY(-4px); - border-color: var(--border-2); + transform: translateY(-5px); + border-color: var(--accent-line); box-shadow: var(--shadow-md); } +.feat:hover::after { + transform: scaleX(1); +} .feat-wide { grid-column: span 2; background: radial-gradient(420px 200px at 100% 0%, var(--accent-weak), transparent 62%), @@ -349,15 +628,26 @@ color: var(--accent); background: var(--accent-weak); border: 1px solid var(--accent-line); + transition: transform var(--dur) var(--ease-spring), background var(--dur) var(--ease-out), + border-color var(--dur) var(--ease-out); +} +/* The lens tilts and lifts as the card is inspected. */ +.feat:hover .feat-ic { + transform: translateY(-2px) scale(1.07) rotate(-4deg); + background: var(--accent-weak); + border-color: var(--accent); +} +.feat-wide:hover .feat-ic { + border-color: var(--pop); } .feat-ic .icon { display: block; } -/* The wide "hero" feature tiles take the warm accent — the complementary pop. */ +/* The wide "hero" feature tiles take the hot-coral pop — the complementary jolt. */ .feat-wide .feat-ic { - color: var(--warm); - background: var(--warm-weak); - border-color: var(--warm-line); + color: var(--pop); + background: var(--pop-weak); + border-color: var(--pop-line); } /* Line icons are drawn in by GSAP on scroll (pathLength=1); fully drawn by default so they're visible without JS / under reduced-motion. */ diff --git a/website/app/(home)/styles/shell.css b/website/app/(home)/styles/shell.css index 111e06e..f47ec49 100644 --- a/website/app/(home)/styles/shell.css +++ b/website/app/(home)/styles/shell.css @@ -53,7 +53,7 @@ inset: 0; z-index: 60; pointer-events: none; - opacity: 0.04; + opacity: 0.02; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-size: 180px 180px; } @@ -332,8 +332,8 @@ width: 7px; height: 7px; border-radius: 50%; - background: var(--accent); - box-shadow: 0 0 10px var(--accent); + background: var(--pop); + box-shadow: 0 0 10px var(--pop); } .site-root .section-title { font-size: clamp(1.7rem, 1.1rem + 1.8vw, 2.6rem); diff --git a/website/app/global.css b/website/app/global.css index 4f55598..65e44c5 100644 --- a/website/app/global.css +++ b/website/app/global.css @@ -5,10 +5,11 @@ @source '../components'; /* ════════════════════════════════════════════════════════════════════════ - "The Case File" — a detective/inspector identity for RepoLens. - Leads LIGHT (warm manila + ink), with an amber/brass lens-glint accent and a - detective red for risk. Dark survives as a warm "night stakeout" (.dark), not - a cold terminal. next-themes flips .dark; tokens drive marketing + docs. + RepoLens — "Inspector" identity, bright & alive. + Leads LIGHT on a clean cool near-white (no warmth — brown reads cheap), with + near-black ink, a VIVID blue lead, a cyan scanner glint, and a hot-coral pop + for life. Green = FIT, red = RISK. Dark is a cool slate complement, not a + warm noir and not an AI terminal. next-themes flips .dark; tokens drive all. ──────────────────────────────────────────────────────────────────────── */ :root { @@ -25,120 +26,158 @@ /* the stamp's heavy "slam" */ --ease-stamp: cubic-bezier(0.5, 1.8, 0.4, 0.95); - /* Amber/brass lens-glint accent + detective red. One ramp tuned for paper; - .dark brightens it for the night-stakeout surfaces. */ - --accent: #9e530e; - --accent-2: #934a0c; - --accent-3: #d9822b; - --grad: linear-gradient(120deg, #d9822b 0%, #9e530e 60%, #934a0c 120%); - --grad-soft: linear-gradient(120deg, #d9822b, #9e530e); - --accent-glow: rgba(179, 94, 16, 0.35); - --accent-weak: rgba(179, 94, 16, 0.1); - --accent-line: rgba(179, 94, 16, 0.28); + /* Vivid blue lead + cyan scanner glint + hot-coral pop. Green=FIT, red=RISK. + .dark recasts these brighter on cool slate. */ + --accent: #2563ff; + --accent-2: #1d4ed8; + --accent-3: #06b6d4; + --glint: #06b6d4; + --grad: linear-gradient(120deg, #06b6d4 0%, #2563ff 58%, #1d4ed8 120%); + --grad-soft: linear-gradient(120deg, #3b82f6, #2563ff); + --accent-glow: rgba(37, 99, 255, 0.3); + --accent-weak: rgba(37, 99, 255, 0.08); + --accent-line: rgba(37, 99, 255, 0.22); - /* warm = the amber CTA; red = the RISKY stamp / red flags */ - --warm: #c2691c; - --warm-2: #d9822b; - --warm-ink: #2a1605; - --warm-glow: rgba(194, 105, 28, 0.4); - --warm-weak: rgba(194, 105, 28, 0.12); - --warm-line: rgba(194, 105, 28, 0.34); - --red: #c0392b; - --red-weak: rgba(192, 57, 43, 0.12); + /* warm = the primary CTA (vivid→deep blue, white text); pop = hot-coral life */ + --warm: #2563ff; + --warm-2: #1d4ed8; + --warm-ink: #ffffff; + --warm-glow: rgba(37, 99, 255, 0.38); + --warm-weak: rgba(37, 99, 255, 0.1); + --warm-line: rgba(37, 99, 255, 0.26); + --pop: #ff5a5f; + --pop-weak: rgba(255, 90, 95, 0.12); + --pop-line: rgba(255, 90, 95, 0.34); + --red: #e5484d; + --red-weak: rgba(229, 72, 77, 0.12); - /* Case File — warm manila paper, sepia ink */ - --bg: #f4ead4; - --bg-2: #ecdfc2; - --surface: #fbf5e7; - --surface-2: #f5ecd6; - --border: #ddccab; - --border-2: #c8b285; - --text: #2a2114; - --text-strong: #17110a; - --sub: #5d5034; - --muted: #756544; - --faint: #a89571; - --ok: #2c7631; - --info: #1d6fa3; - --warn: #b4690a; - --bad: #c0392b; - --shadow-lg: 0 26px 70px -34px rgba(60, 42, 16, 0.38); - --shadow-md: 0 16px 38px -22px rgba(60, 42, 16, 0.3); - --paper-grain: 0.5; + /* Clean paper — cool near-white, ink near-black */ + --bg: #f7f9fd; + --bg-2: #eef2f9; + --surface: #ffffff; + --surface-2: #f9fbfe; + --border: #e3e8f2; + --border-2: #ccd6e6; + --text: #1b2230; + --text-strong: #0b0e14; + --sub: #495264; + --muted: #6b7384; + --faint: #97a0b2; + --ok: #16a34a; + --info: #2563ff; + --warn: #06b6d4; + --bad: #e5484d; + --shadow-lg: 0 28px 64px -30px rgba(15, 30, 70, 0.3); + --shadow-md: 0 16px 38px -22px rgba(15, 30, 70, 0.22); + --paper-grain: 0; - /* fumadocs remap → Case File light */ - --color-fd-background: #f4ead4; - --color-fd-foreground: #2a2114; - --color-fd-muted: #ecdfc2; - --color-fd-muted-foreground: #6a5c3d; - --color-fd-popover: #fbf5e7; - --color-fd-popover-foreground: #17110a; - --color-fd-card: #fbf5e7; - --color-fd-card-foreground: #17110a; - --color-fd-border: #ddccab; - --color-fd-primary: #9e530e; - --color-fd-primary-foreground: #fbf5e7; - --color-fd-secondary: #efe3ca; - --color-fd-secondary-foreground: #2a2114; - --color-fd-accent: #ecdfc2; - --color-fd-accent-foreground: #17110a; - --color-fd-ring: #9e530e; + /* fumadocs remap → bright light */ + --color-fd-background: #f7f9fd; + --color-fd-foreground: #1b2230; + --color-fd-muted: #eef2f9; + --color-fd-muted-foreground: #6b7384; + --color-fd-popover: #ffffff; + --color-fd-popover-foreground: #0b0e14; + --color-fd-card: #ffffff; + --color-fd-card-foreground: #0b0e14; + --color-fd-border: #e3e8f2; + --color-fd-primary: #2563ff; + --color-fd-primary-foreground: #ffffff; + --color-fd-secondary: #eef2f9; + --color-fd-secondary-foreground: #1b2230; + --color-fd-accent: #e8eeff; + --color-fd-accent-foreground: #0b0e14; + --color-fd-ring: #2563ff; } .dark { - /* Night Stakeout — warm noir, not a cold terminal */ - --bg: #1b1610; - --bg-2: #221b12; - --surface: #2a2116; - --surface-2: #32281a; - --border: #43361f; - --border-2: #5e4b2c; - --text: #ece1cd; - --text-strong: #fff8ec; - --sub: #c3b094; - --muted: #9c8a6a; - --faint: #6f6045; + /* Cool slate complement — clean dark, not a warm noir, not an AI terminal */ + --bg: #0b0f17; + --bg-2: #121826; + --surface: #151c2b; + --surface-2: #1b2336; + --border: #283146; + --border-2: #3a4358; + --text: #e6ebf3; + --text-strong: #ffffff; + --sub: #aeb6c4; + --muted: #7f8799; + --faint: #596173; - /* brighter amber + red read better on the warm-dark surfaces */ - --accent: #e0934a; - --accent-2: #cf7a2a; - --accent-3: #f0aa63; - --grad: linear-gradient(120deg, #f0aa63 0%, #e0934a 60%, #cf7a2a 120%); - --grad-soft: linear-gradient(120deg, #f0aa63, #e0934a); - --accent-glow: rgba(224, 147, 74, 0.45); - --accent-weak: rgba(224, 147, 74, 0.14); - --accent-line: rgba(224, 147, 74, 0.32); - --warm: #e0934a; - --warm-2: #f0aa63; - --warm-ink: #2a1605; - --warm-glow: rgba(224, 147, 74, 0.45); - --warm-weak: rgba(224, 147, 74, 0.16); - --warm-line: rgba(224, 147, 74, 0.36); - --red: #e06a5c; - --red-weak: rgba(224, 106, 92, 0.16); - --ok: #5cc05f; - --info: #56b6e8; - --warn: #e0a341; - --bad: #e06a5c; - --shadow-lg: 0 26px 70px -34px rgba(0, 0, 0, 0.72); - --shadow-md: 0 16px 44px -26px rgba(0, 0, 0, 0.66); - --paper-grain: 0.7; + /* brighter blue + cyan + coral on the slate surfaces */ + --accent: #5a8cff; + --accent-2: #3b6ef0; + --accent-3: #34d6e8; + --glint: #34d6e8; + --grad: linear-gradient(120deg, #34d6e8 0%, #5a8cff 58%, #3b6ef0 120%); + --grad-soft: linear-gradient(120deg, #6f9bff, #5a8cff); + --accent-glow: rgba(90, 140, 255, 0.45); + --accent-weak: rgba(90, 140, 255, 0.16); + --accent-line: rgba(90, 140, 255, 0.32); + --warm: #5a8cff; + --warm-2: #4f86ff; + --warm-ink: #06122a; + --warm-glow: rgba(90, 140, 255, 0.5); + --warm-weak: rgba(90, 140, 255, 0.18); + --warm-line: rgba(90, 140, 255, 0.36); + --pop: #ff6b70; + --pop-weak: rgba(255, 107, 112, 0.16); + --pop-line: rgba(255, 107, 112, 0.4); + --red: #ff6b6b; + --red-weak: rgba(255, 107, 107, 0.16); + --ok: #3ecf7e; + --info: #5a8cff; + --warn: #34d6e8; + --bad: #ff6b6b; + --shadow-lg: 0 28px 64px -30px rgba(0, 0, 0, 0.72); + --shadow-md: 0 16px 44px -26px rgba(0, 0, 0, 0.62); + --paper-grain: 0; - /* fumadocs remap → night stakeout */ - --color-fd-background: #1b1610; - --color-fd-foreground: #ece1cd; - --color-fd-muted: #2a2116; - --color-fd-muted-foreground: #c3b094; - --color-fd-popover: #221b12; - --color-fd-popover-foreground: #ece1cd; - --color-fd-card: #2a2116; - --color-fd-card-foreground: #ece1cd; - --color-fd-border: #43361f; - --color-fd-primary: #e0934a; - --color-fd-primary-foreground: #1b1610; - --color-fd-secondary: #32281a; - --color-fd-secondary-foreground: #ece1cd; - --color-fd-accent: #322619; - --color-fd-accent-foreground: #fff8ec; - --color-fd-ring: #e0934a; + /* fumadocs remap → cool dark */ + --color-fd-background: #0b0f17; + --color-fd-foreground: #e6ebf3; + --color-fd-muted: #151c2b; + --color-fd-muted-foreground: #aeb6c4; + --color-fd-popover: #121826; + --color-fd-popover-foreground: #e6ebf3; + --color-fd-card: #151c2b; + --color-fd-card-foreground: #e6ebf3; + --color-fd-border: #283146; + --color-fd-primary: #5a8cff; + --color-fd-primary-foreground: #06122a; + --color-fd-secondary: #1b2336; + --color-fd-secondary-foreground: #e6ebf3; + --color-fd-accent: #1c2740; + --color-fd-accent-foreground: #ffffff; + --color-fd-ring: #5a8cff; +} + +/* ════════════════════════════════════════════════════════════════════════ + Theme scan — the View Transitions API "develops" the new theme down the page + top→bottom, with a glowing cyan scan edge leading the front. Triggered only + by the theme toggle (ThemeToggle.tsx); falls back to an instant flip where + the API or motion preference says no. + ──────────────────────────────────────────────────────────────────────── */ +@media (prefers-reduced-motion: no-preference) { + ::view-transition-old(root), + ::view-transition-new(root) { + /* drop the default cross-fade — we own the reveal */ + animation: none; + mix-blend-mode: normal; + } + /* old theme stays put underneath; the new theme is clipped in from the top */ + ::view-transition-new(root) { + /* steady, near-even travel so you actually watch the line sweep down */ + animation: theme-scan 1000ms cubic-bezier(0.45, 0.05, 0.55, 0.95); + filter: drop-shadow(0 8px 16px rgba(45, 212, 230, 0.7)) + drop-shadow(0 2px 4px rgba(45, 212, 230, 0.6)); + } +} +@keyframes theme-scan { + from { + clip-path: inset(0 0 100% 0); + } + to { + clip-path: inset(0 0 0 0); + } } diff --git a/website/app/icon.svg b/website/app/icon.svg index a6ac4a8..f982ffc 100644 --- a/website/app/icon.svg +++ b/website/app/icon.svg @@ -1,6 +1,6 @@ - - - - + + + + diff --git a/website/components/home/Hero.tsx b/website/components/home/Hero.tsx index 9e3fa61..f3d4643 100644 --- a/website/components/home/Hero.tsx +++ b/website/components/home/Hero.tsx @@ -8,13 +8,13 @@ export function Hero() {
- Chrome Extension · Manifest V3 · v3.0 + Case No. RL-3.0 · Chrome · Manifest V3

Read code before
- you trust it. + you trust it.

diff --git a/website/components/home/SiteMotion.tsx b/website/components/home/SiteMotion.tsx index 6c65e58..55dc816 100644 --- a/website/components/home/SiteMotion.tsx +++ b/website/components/home/SiteMotion.tsx @@ -56,6 +56,27 @@ export function SiteMotion() { }), }); + // 2b) Section titles wipe in left→right — a bolder editorial reveal + // layered over the section fade. Bento is excluded (own choreography). + const titles = gsap.utils.toArray( + '.reveal:not(.feature-section) .section-title', + ); + ScrollTrigger.batch(titles, { + start: 'top 88%', + onEnter: (batch) => + gsap.fromTo( + batch, + { clipPath: 'inset(0 100% 0 0)' }, + { + clipPath: 'inset(0 0% 0 0)', + duration: 0.7, + ease: 'power3.inOut', + stagger: 0.12, + overwrite: 'auto', + }, + ), + }); + // 3) Feature bento — header in, then tiles stagger. const bento = document.querySelector('.feature-section'); if (bento) { diff --git a/website/components/home/VerdictDemo.tsx b/website/components/home/VerdictDemo.tsx index e539544..c6e2082 100644 --- a/website/components/home/VerdictDemo.tsx +++ b/website/components/home/VerdictDemo.tsx @@ -1,10 +1,214 @@ -const TABS = ['Verdict', 'ELI5', 'Technical', 'Use Cases', 'Health', 'Red Flags', 'Tech Stack']; +'use client'; + +import { useEffect, useRef, useState, type ReactNode } from 'react'; /** - * A faithful, fully themeable recreation of the real scan output — so it - * re-skins with the toggle instead of being a screenshot that breaks in latte. + * A faithful, fully themeable recreation of the real scan output — now a LIVE + * demo: the tabs are a real tablist (click + arrow-key) that crossfades the + * card body, and the "Strong fit" verdict stamp slams in the first time the + * card scrolls into view. Everything re-skins with the theme toggle instead of + * being a screenshot that breaks in another palette. Motion is CSS-only and + * gated behind `prefers-reduced-motion` in home.css. */ + +type Tab = { id: string; label: string; panel: ReactNode }; + +const HealthBars = () => { + const rows: Array<[string, number]> = [ + ['Maintenance', 82], + ['Activity', 71], + ['Documentation', 95], + ['Adoption', 99], + ]; + return ( +

+ {rows.map(([label, v]) => ( +
+ {label} + + + + {v} +
+ ))} +
+ ); +}; + +const TABS: Tab[] = [ + { + id: 'verdict', + label: 'Verdict', + panel: ( + <> +
+ Strong fit + Health 88 · 0 flags · 3 pros / 1 cons +
+
+
AI bottom line
+

The default choice for Node HTTP services; boring in the best way.

+
+
+ JavaScript 99% + TypeScript · Shell +
+ + ), + }, + { + id: 'eli5', + label: 'ELI5', + panel: ( +
+

+ Express is the “hello world” of Node web servers. You hand it routes — “when someone + visits /users, run this function” — and it handles the plumbing in between. +

+

+ Minimal on purpose: no database, no folder structure, no opinions. Just the + request-in / response-out basics, and a way to stack little functions in the middle. +

+
+ ), + }, + { + id: 'technical', + label: 'Technical', + panel: ( +
    +
  • + Middleware pipeline — every request flows through a stack of + (req, res, next) functions you compose yourself. +
  • +
  • + Thin routing layer over Node’s native http module; ~16k LOC, + no framework runtime underneath. +
  • +
  • + Unbundled — no build step, composes directly with the npm ecosystem + rather than replacing it. +
  • +
+ ), + }, + { + id: 'usecases', + label: 'Use Cases', + panel: ( +
+ REST / JSON APIs + Server-rendered apps + Microservices + A base other frameworks build on + Quick prototypes +
+ ), + }, + { + id: 'health', + label: 'Health', + panel: , + }, + { + id: 'redflags', + label: 'Red Flags', + panel: ( +
+ +
+
No red flags
+

Permissive MIT license, active maintenance, no critical advisories in the manifest.

+
+
+ ), + }, + { + id: 'techstack', + label: 'Tech Stack', + panel: ( +
+
+
Runtime
+
Node.js
+
+
+
Language
+
JavaScript · TypeScript types shipped separately
+
+
+
Notable deps
+
finalhandler · qs · send · serve-static
+
+
+ ), + }, +]; + export function VerdictDemo() { + const [active, setActive] = useState(0); + const [inView, setInView] = useState(false); + const cardRef = useRef(null); + const tabRefs = useRef>([]); + + // Slam the stamp in the first time the card meets the viewport. + useEffect(() => { + const el = cardRef.current; + if (!el) return; + const obs = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setInView(true); + obs.disconnect(); + } + }, + { threshold: 0.4 }, + ); + obs.observe(el); + return () => obs.disconnect(); + }, []); + + const selectTab = (next: number) => { + const idx = (next + TABS.length) % TABS.length; + setActive(idx); + tabRefs.current[idx]?.focus(); + }; + + const onKeyDown = (e: React.KeyboardEvent) => { + switch (e.key) { + case 'ArrowRight': + e.preventDefault(); + selectTab(active + 1); + break; + case 'ArrowLeft': + e.preventDefault(); + selectTab(active - 1); + break; + case 'Home': + e.preventDefault(); + selectTab(0); + break; + case 'End': + e.preventDefault(); + selectTab(TABS.length - 1); + break; + default: + break; + } + }; + + const current = TABS[active]; + return (
@@ -34,7 +238,7 @@ export function VerdictDemo() {
-
+
- diff --git a/website/components/site/ThemeToggle.tsx b/website/components/site/ThemeToggle.tsx index 96dcc4c..1f5ab71 100644 --- a/website/components/site/ThemeToggle.tsx +++ b/website/components/site/ThemeToggle.tsx @@ -1,8 +1,13 @@ 'use client'; import { useEffect, useState } from 'react'; +import { flushSync } from 'react-dom'; import { useTheme } from 'next-themes'; +type ViewTransitionDocument = Document & { + startViewTransition?: (callback: () => void) => unknown; +}; + /** * Sun⇄moon toggle that *morphs*: in dark mode a cutout circle carves the orb * into a crescent and the rays retract; switching to light slides the cutout @@ -18,15 +23,28 @@ export function ThemeToggle() { useEffect(() => setMounted(true), []); const isLight = mounted && resolvedTheme !== 'dark'; - const label = mounted - ? `Switch to ${isLight ? 'midnight (dark)' : 'latte (light)'}` - : 'Toggle theme'; + const label = mounted ? `Switch to ${isLight ? 'dark' : 'light'} mode` : 'Toggle theme'; + + // A scanline "develops" the page into the new theme top→bottom via the View + // Transitions API (see ::view-transition-new(root) in global.css). flushSync + // makes next-themes apply the class synchronously so the snapshot captures the + // new theme. Instant flip where the API is missing or motion is reduced. + const runToggle = () => { + const next = isLight ? 'dark' : 'light'; + const doc = document as ViewTransitionDocument; + const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + if (reduce || typeof doc.startViewTransition !== 'function') { + setTheme(next); + return; + } + doc.startViewTransition(() => flushSync(() => setTheme(next))); + }; return (
-
+
+
+ +
diff --git a/website/components/site/ThemeToggle.tsx b/website/components/site/ThemeToggle.tsx index 1f5ab71..56f651b 100644 --- a/website/components/site/ThemeToggle.tsx +++ b/website/components/site/ThemeToggle.tsx @@ -1,12 +1,8 @@ 'use client'; import { useEffect, useState } from 'react'; -import { flushSync } from 'react-dom'; import { useTheme } from 'next-themes'; - -type ViewTransitionDocument = Document & { - startViewTransition?: (callback: () => void) => unknown; -}; +import { scanToTheme } from '@/lib/themeScan'; /** * Sun⇄moon toggle that *morphs*: in dark mode a cutout circle carves the orb @@ -29,16 +25,7 @@ export function ThemeToggle() { // Transitions API (see ::view-transition-new(root) in global.css). flushSync // makes next-themes apply the class synchronously so the snapshot captures the // new theme. Instant flip where the API is missing or motion is reduced. - const runToggle = () => { - const next = isLight ? 'dark' : 'light'; - const doc = document as ViewTransitionDocument; - const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; - if (reduce || typeof doc.startViewTransition !== 'function') { - setTheme(next); - return; - } - doc.startViewTransition(() => flushSync(() => setTheme(next))); - }; + const runToggle = () => scanToTheme(setTheme, isLight ? 'dark' : 'light'); return (
); diff --git a/website/package-lock.json b/website/package-lock.json index fc7268f..370b9a9 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -9,24 +9,43 @@ "version": "0.1.0", "hasInstallScript": true, "dependencies": { + "@react-three/drei": "^10.7.7", + "@react-three/fiber": "^9.6.1", "fumadocs-core": "^15.0.0", "fumadocs-mdx": "^11.0.0", "fumadocs-ui": "^15.0.0", "gsap": "^3.15.0", "next": "^15.3.0", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "three": "^0.184.0" }, "devDependencies": { "@eslint/eslintrc": "^3.3.5", "@types/node": "^22.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@types/three": "^0.184.1", "eslint": "^9.39.4", "eslint-config-next": "^15.5.19", "typescript": "^5.6.0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@dimforge/rapier3d-compat": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", + "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", + "license": "Apache-2.0" + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -1236,6 +1255,24 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/@mediapipe/tasks-vision": { + "version": "0.10.17", + "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.17.tgz", + "integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==", + "license": "Apache-2.0" + }, + "node_modules/@monogrid/gainmap-js": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@monogrid/gainmap-js/-/gainmap-js-3.4.0.tgz", + "integrity": "sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==", + "license": "MIT", + "dependencies": { + "promise-worker-transferable": "^1.0.4" + }, + "peerDependencies": { + "three": ">= 0.159.0" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", @@ -2194,6 +2231,94 @@ "integrity": "sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==", "license": "MIT" }, + "node_modules/@react-three/drei": { + "version": "10.7.7", + "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz", + "integrity": "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mediapipe/tasks-vision": "0.10.17", + "@monogrid/gainmap-js": "^3.0.6", + "@use-gesture/react": "^10.3.1", + "camera-controls": "^3.1.0", + "cross-env": "^7.0.3", + "detect-gpu": "^5.0.56", + "glsl-noise": "^0.0.0", + "hls.js": "^1.5.17", + "maath": "^0.10.8", + "meshline": "^3.3.1", + "stats-gl": "^2.2.8", + "stats.js": "^0.17.0", + "suspend-react": "^0.1.3", + "three-mesh-bvh": "^0.8.3", + "three-stdlib": "^2.35.6", + "troika-three-text": "^0.52.4", + "tunnel-rat": "^0.1.2", + "use-sync-external-store": "^1.4.0", + "utility-types": "^3.11.0", + "zustand": "^5.0.1" + }, + "peerDependencies": { + "@react-three/fiber": "^9.0.0", + "react": "^19", + "react-dom": "^19", + "three": ">=0.159" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/@react-three/fiber": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.6.1.tgz", + "integrity": "sha512-zF0rsKcVYpcJwbFEnv2HkHX9cvOEgsfQo/X8lwmR2dn13S4qEQJXir9fxf5js2LQFoXqxOY7MDkOkYx2uZ4gSg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@types/webxr": "*", + "base64-js": "^1.5.1", + "buffer": "^6.0.3", + "its-fine": "^2.0.0", + "react-use-measure": "^2.1.7", + "scheduler": "^0.27.0", + "suspend-react": "^0.1.3", + "use-sync-external-store": "^1.4.0", + "zustand": "^5.0.3" + }, + "peerDependencies": { + "expo": ">=43.0", + "expo-asset": ">=8.4", + "expo-file-system": ">=11.0", + "expo-gl": ">=11.0", + "react": ">=19 <19.3", + "react-dom": ">=19 <19.3", + "react-native": ">=0.78", + "three": ">=0.156" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + }, + "expo-asset": { + "optional": true + }, + "expo-file-system": { + "optional": true + }, + "expo-gl": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -2314,6 +2439,12 @@ "tslib": "^2.8.0" } }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "license": "MIT" + }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", @@ -2334,6 +2465,12 @@ "@types/ms": "*" } }, + "node_modules/@types/draco3d": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz", + "integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -2403,11 +2540,16 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/offscreencanvas": { + "version": "2019.7.3", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", + "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.2.17", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", - "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -2423,12 +2565,47 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/react-reconciler": { + "version": "0.28.9", + "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", + "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.184.1", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.184.1.tgz", + "integrity": "sha512-6q4VdiqVsrTRqmk62/BnlcAvIrnDM0zf2ZDVKI5kZiniWrSaOHaQzmbp+BNzoggc/8tgW412pL//wZIxu2PPTA==", + "license": "MIT", + "dependencies": { + "@dimforge/rapier3d-compat": "~0.12.0", + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": ">=0.5.17", + "fflate": "~0.8.2", + "meshoptimizer": "~1.1.1" + } + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "node_modules/@types/webxr": { + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.61.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.0.tgz", @@ -3041,6 +3218,24 @@ "win32" ] }, + "node_modules/@use-gesture/core": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", + "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", + "license": "MIT" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", + "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", + "license": "MIT", + "dependencies": { + "@use-gesture/core": "10.3.1" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -3362,6 +3557,35 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -3386,6 +3610,30 @@ "node": ">=8" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", @@ -3446,6 +3694,19 @@ "node": ">=6" } }, + "node_modules/camera-controls": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.2.tgz", + "integrity": "sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==", + "license": "MIT", + "engines": { + "node": ">=22.0.0", + "npm": ">=10.5.1" + }, + "peerDependencies": { + "three": ">=0.126.1" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001797", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz", @@ -3628,11 +3889,28 @@ "dev": true, "license": "MIT" }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -3659,7 +3937,6 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -3805,6 +4082,15 @@ "node": ">=6" } }, + "node_modules/detect-gpu": { + "version": "5.0.70", + "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.70.tgz", + "integrity": "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==", + "license": "MIT", + "dependencies": { + "webgl-constants": "^1.1.1" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -3847,6 +4133,12 @@ "node": ">=0.10.0" } }, + "node_modules/draco3d": { + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz", + "integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==", + "license": "Apache-2.0" + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -4738,6 +5030,12 @@ } } }, + "node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5165,6 +5463,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glsl-noise": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", + "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==", + "license": "MIT" + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -5382,6 +5686,12 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hls.js": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.16.tgz", + "integrity": "sha512-VSIRpLfRwlAAdGL4wiTucx2ScRipo0ed1FBatWkyt832jC4CReKstga6yIhYVwGu9LOBjuX9wzmRMeQdBJtzEA==", + "license": "Apache-2.0" + }, "node_modules/html-void-elements": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", @@ -5392,6 +5702,26 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5414,6 +5744,12 @@ "node": ">=16.x" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -5791,6 +6127,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -5947,7 +6289,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/iterator.prototype": { @@ -5968,6 +6309,18 @@ "node": ">= 0.4" } }, + "node_modules/its-fine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz", + "integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==", + "license": "MIT", + "dependencies": { + "@types/react-reconciler": "^0.28.9" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6091,6 +6444,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6145,6 +6507,16 @@ "node": "20 || >=22" } }, + "node_modules/maath": { + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz", + "integrity": "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==", + "license": "MIT", + "peerDependencies": { + "@types/three": ">=0.134.0", + "three": ">=0.134.0" + } + }, "node_modules/markdown-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", @@ -6474,6 +6846,21 @@ "node": ">= 8" } }, + "node_modules/meshline": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz", + "integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==", + "license": "MIT", + "peerDependencies": { + "three": ">=0.137" + } + }, + "node_modules/meshoptimizer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz", + "integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==", + "license": "MIT" + }, "node_modules/micromark": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", @@ -7660,7 +8047,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7752,6 +8138,12 @@ "node": ">=4" } }, + "node_modules/potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", + "license": "ISC" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7762,6 +8154,16 @@ "node": ">= 0.8.0" } }, + "node_modules/promise-worker-transferable": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz", + "integrity": "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==", + "license": "Apache-2.0", + "dependencies": { + "is-promise": "^2.1.0", + "lie": "^3.0.2" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -7928,6 +8330,21 @@ } } }, + "node_modules/react-use-measure": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz", + "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.13", + "react-dom": ">=16.13" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -8187,6 +8604,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "2.0.0-next.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", @@ -8447,7 +8873,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -8460,7 +8885,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8593,6 +9017,32 @@ "dev": true, "license": "MIT" }, + "node_modules/stats-gl": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-2.4.2.tgz", + "integrity": "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==", + "license": "MIT", + "dependencies": { + "@types/three": "*", + "three": "^0.170.0" + }, + "peerDependencies": { + "@types/three": "*", + "three": "*" + } + }, + "node_modules/stats-gl/node_modules/three": { + "version": "0.170.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.170.0.tgz", + "integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==", + "license": "MIT" + }, + "node_modules/stats.js": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz", + "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==", + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -8825,6 +9275,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/suspend-react": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", + "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=17.0" + } + }, "node_modules/tailwind-merge": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz", @@ -8835,6 +9294,44 @@ "url": "https://github.com/sponsors/dcastil" } }, + "node_modules/three": { + "version": "0.184.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.184.0.tgz", + "integrity": "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==", + "license": "MIT" + }, + "node_modules/three-mesh-bvh": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.8.3.tgz", + "integrity": "sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg==", + "license": "MIT", + "peerDependencies": { + "three": ">= 0.159.0" + } + }, + "node_modules/three-stdlib": { + "version": "2.36.1", + "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.36.1.tgz", + "integrity": "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==", + "license": "MIT", + "dependencies": { + "@types/draco3d": "^1.4.0", + "@types/offscreencanvas": "^2019.6.4", + "@types/webxr": "^0.5.2", + "draco3d": "^1.4.1", + "fflate": "^0.6.9", + "potpack": "^1.0.1" + }, + "peerDependencies": { + "three": ">=0.128.0" + } + }, + "node_modules/three-stdlib/node_modules/fflate": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", + "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==", + "license": "MIT" + }, "node_modules/tinyexec": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", @@ -8883,6 +9380,36 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/troika-three-text": { + "version": "0.52.4", + "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz", + "integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==", + "license": "MIT", + "dependencies": { + "bidi-js": "^1.0.2", + "troika-three-utils": "^0.52.4", + "troika-worker-utils": "^0.52.0", + "webgl-sdf-generator": "1.1.1" + }, + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-three-utils": { + "version": "0.52.4", + "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz", + "integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==", + "license": "MIT", + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-worker-utils": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz", + "integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==", + "license": "MIT" + }, "node_modules/trough": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", @@ -8925,6 +9452,43 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tunnel-rat": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz", + "integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==", + "license": "MIT", + "dependencies": { + "zustand": "^4.3.2" + } + }, + "node_modules/tunnel-rat/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -9247,12 +9811,30 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -9281,11 +9863,21 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/webgl-constants": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", + "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" + }, + "node_modules/webgl-sdf-generator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", + "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==", + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -9418,6 +10010,35 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zustand": { + "version": "5.0.14", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz", + "integrity": "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/website/package.json b/website/package.json index 0659561..27506cb 100644 --- a/website/package.json +++ b/website/package.json @@ -10,19 +10,23 @@ "postinstall": "fumadocs-mdx" }, "dependencies": { + "@react-three/drei": "^10.7.7", + "@react-three/fiber": "^9.6.1", "fumadocs-core": "^15.0.0", "fumadocs-mdx": "^11.0.0", "fumadocs-ui": "^15.0.0", "gsap": "^3.15.0", "next": "^15.3.0", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "three": "^0.184.0" }, "devDependencies": { "@eslint/eslintrc": "^3.3.5", "@types/node": "^22.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@types/three": "^0.184.1", "eslint": "^9.39.4", "eslint-config-next": "^15.5.19", "typescript": "^5.6.0" From 67552852bd55bd9d2ca421d216f15e2287c1c497 Mon Sep 17 00:00:00 2001 From: ares <285551516+New1Direction@users.noreply.github.com> Date: Mon, 15 Jun 2026 02:25:37 -0700 Subject: [PATCH 04/11] =?UTF-8?q?feat(site):=20glow=20up=20the=20hero=20le?= =?UTF-8?q?ns=20=E2=80=94=20drifting=20sparkles=20+=20stronger=20halo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add drei Sparkles: a cluster of cyan motes drifting around the lens for a bit of magic (especially against the dark theme). - Brighten the aperture rim and beef up the CSS radial glow behind the floating lens so it reads luminous without postprocessing. Tried @react-three/postprocessing Bloom for a real glow, but EffectComposer broke the transparent canvas (a visible box around the lens), so it's out — the emissive rim + Sparkles + CSS halo get the look with the float intact. First Load JS unchanged (114 kB); all 3D still lazy-loaded. --- website/app/(home)/styles/home.css | 10 ++++++---- website/components/home/HeroLens3D.tsx | 20 ++++++++++++++++++-- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/website/app/(home)/styles/home.css b/website/app/(home)/styles/home.css index e003e4e..27db9dc 100644 --- a/website/app/(home)/styles/home.css +++ b/website/app/(home)/styles/home.css @@ -122,12 +122,14 @@ .hero-lens-3d::before { content: ''; position: absolute; - inset: 6%; + inset: -8%; z-index: -1; border-radius: 50%; - background: radial-gradient(circle at 50% 46%, var(--accent-glow), transparent 64%); - opacity: 0.5; - filter: blur(10px); + background: + radial-gradient(circle at 50% 48%, color-mix(in srgb, var(--accent-3) 45%, transparent), transparent 58%), + radial-gradient(circle at 50% 52%, var(--accent-glow), transparent 70%); + opacity: 0.7; + filter: blur(16px); } .hero-lens-3d canvas { display: block; diff --git a/website/components/home/HeroLens3D.tsx b/website/components/home/HeroLens3D.tsx index 37df62d..5d082e9 100644 --- a/website/components/home/HeroLens3D.tsx +++ b/website/components/home/HeroLens3D.tsx @@ -2,7 +2,13 @@ import { Suspense, useRef } from 'react'; import { Canvas, useFrame } from '@react-three/fiber'; -import { Environment, Lightformer, MeshTransmissionMaterial, useTexture } from '@react-three/drei'; +import { + Environment, + Lightformer, + MeshTransmissionMaterial, + Sparkles, + useTexture, +} from '@react-three/drei'; import * as THREE from 'three'; // Static assets aren't auto-prefixed with the GitHub Pages basePath, so prefix by hand. @@ -67,7 +73,7 @@ function Lens() { + {/* drifting motes of light around the lens */} + {/* Procedural studio env — NO external HDR (keeps zero external calls). */} From d605f222264eef20bebd06b63cde399a64a17665 Mon Sep 17 00:00:00 2001 From: ares <285551516+New1Direction@users.noreply.github.com> Date: Mon, 15 Jun 2026 02:28:23 -0700 Subject: [PATCH 05/11] =?UTF-8?q?fix(site):=20clearer=20hero=20lens=20?= =?UTF-8?q?=E2=80=94=20dial=20down=20the=20glass=20distortion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vee was over-warped through the glass. Cut distortion 0.25→0.03, drop temporal distortion, ease thickness/ior/aberration/blur so he reads clearly with just a gentle lens refraction at the rim. --- website/components/home/HeroLens3D.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/website/components/home/HeroLens3D.tsx b/website/components/home/HeroLens3D.tsx index 5d082e9..9281052 100644 --- a/website/components/home/HeroLens3D.tsx +++ b/website/components/home/HeroLens3D.tsx @@ -55,14 +55,14 @@ function Lens() { From 789b63c28fe9379fc117b2766f9c5679f477e112 Mon Sep 17 00:00:00 2001 From: ares <285551516+New1Direction@users.noreply.github.com> Date: Mon, 15 Jun 2026 02:34:01 -0700 Subject: [PATCH 06/11] =?UTF-8?q?feat(site):=20hero=20lens=20=E2=80=94=20a?= =?UTF-8?q?nimate=20Vee=20+=20calm=20the=20rim?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Vee is now the live mascot clip (useVideoTexture on mascot.mp4) refracted through the glass, so the character actually moves instead of a still frame. - Rim was too cyan: shift it toward brand blue, dim and thin it; pull cyan out of the sparkles and the CSS glow halo too. --- website/app/(home)/styles/home.css | 6 ++-- website/components/home/HeroLens3D.tsx | 40 +++++++++++++++----------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/website/app/(home)/styles/home.css b/website/app/(home)/styles/home.css index 27db9dc..f8e7099 100644 --- a/website/app/(home)/styles/home.css +++ b/website/app/(home)/styles/home.css @@ -126,9 +126,9 @@ z-index: -1; border-radius: 50%; background: - radial-gradient(circle at 50% 48%, color-mix(in srgb, var(--accent-3) 45%, transparent), transparent 58%), - radial-gradient(circle at 50% 52%, var(--accent-glow), transparent 70%); - opacity: 0.7; + radial-gradient(circle at 50% 48%, color-mix(in srgb, var(--accent) 38%, transparent), transparent 60%), + radial-gradient(circle at 50% 52%, var(--accent-glow), transparent 72%); + opacity: 0.5; filter: blur(16px); } .hero-lens-3d canvas { diff --git a/website/components/home/HeroLens3D.tsx b/website/components/home/HeroLens3D.tsx index 9281052..c0e400c 100644 --- a/website/components/home/HeroLens3D.tsx +++ b/website/components/home/HeroLens3D.tsx @@ -7,7 +7,7 @@ import { Lightformer, MeshTransmissionMaterial, Sparkles, - useTexture, + useVideoTexture, } from '@react-three/drei'; import * as THREE from 'three'; @@ -15,13 +15,19 @@ import * as THREE from 'three'; const BASE = process.env.NEXT_PUBLIC_BASE_PATH ?? ''; /** - * Vee on a disc directly behind the lens — sized to sit fully within the lens - * silhouette, so he's only ever seen *through* the glass (refracted), never as a - * bare rectangle. Swap the poster for a transparent cutout later for an even - * cleaner float. + * Vee — the live mascot clip — on a disc directly behind the lens, sized to sit + * fully within the lens silhouette so he's only ever seen *through* the glass + * (refracted), never as a bare rectangle. The video texture means he actually + * moves. Swap mascot.mp4 for a transparent cutout/render later for a cleaner float. */ function VeeDisc() { - const tex = useTexture(`${BASE}/mascot-poster.jpg`); + const tex = useVideoTexture(`${BASE}/mascot.mp4`, { + muted: true, + loop: true, + start: true, + playsInline: true, + crossOrigin: 'anonymous', + }); tex.colorSpace = THREE.SRGBColorSpace; return ( @@ -70,13 +76,13 @@ function Lens() { {/* glowing aperture rim — a ring around the lens edge, facing the viewer (no rotation: TorusGeometry already lies in the camera-facing plane). */} - + @@ -100,12 +106,12 @@ export default function HeroLens3D() { {/* drifting motes of light around the lens */} {/* Procedural studio env — NO external HDR (keeps zero external calls). */} From 9846f8dc56959abec58f247f2eaa72fb99e92c80 Mon Sep 17 00:00:00 2001 From: ares <285551516+New1Direction@users.noreply.github.com> Date: Mon, 15 Jun 2026 02:39:04 -0700 Subject: [PATCH 07/11] =?UTF-8?q?feat(site):=20hero=20lens=20=E2=80=94=20s?= =?UTF-8?q?till=20until=20hovered,=20gentler=20loop,=20clearer=20glass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - The glass now rests perfectly still and only tilts while hovered (removed the idle drift/bob); eases back to rest on leave. - Slow the mascot clip (playbackRate 0.8) so Vee's loop reads gentler. - Cut the glass distortion further (distortion 0, lower ior/thickness/aberration) so Vee reads almost directly through near-clear glass. --- website/components/home/HeroLens3D.tsx | 42 +++++++++++++++++--------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/website/components/home/HeroLens3D.tsx b/website/components/home/HeroLens3D.tsx index c0e400c..f74862a 100644 --- a/website/components/home/HeroLens3D.tsx +++ b/website/components/home/HeroLens3D.tsx @@ -29,6 +29,9 @@ function VeeDisc() { crossOrigin: 'anonymous', }); tex.colorSpace = THREE.SRGBColorSpace; + // Ease the playback so the loop reads gentler and more continuous. + const video = tex.image as HTMLVideoElement | undefined; + if (video) video.playbackRate = 0.8; return ( @@ -41,33 +44,42 @@ function VeeDisc() { * idles with a slow drift. */ function Lens() { const group = useRef(null); + const hovered = useRef(false); useFrame((state, delta) => { const g = group.current; if (!g) return; - const t = state.clock.elapsedTime; - // cursor tilt + a slow idle sway - const targetY = state.pointer.x * 0.5 + Math.sin(t * 0.35) * 0.12; - const targetX = -state.pointer.y * 0.4 + Math.cos(t * 0.28) * 0.08; - const k = 1 - Math.pow(0.0015, delta); // frame-rate-independent damping + // The glass rests perfectly still until hovered; then it tilts toward the + // cursor and eases back to rest on leave. No idle motion. + const targetY = hovered.current ? state.pointer.x * 0.5 : 0; + const targetX = hovered.current ? -state.pointer.y * 0.4 : 0; + const k = 1 - Math.pow(0.0016, delta); // frame-rate-independent damping g.rotation.y = THREE.MathUtils.lerp(g.rotation.y, targetY, k); g.rotation.x = THREE.MathUtils.lerp(g.rotation.x, targetX, k); - g.position.y = Math.sin(t * 0.6) * 0.04; }); return ( - - {/* the lens: a flattened sphere → a thick magnifying disc */} + { + hovered.current = true; + }} + onPointerOut={() => { + hovered.current = false; + }} + > + {/* the lens: a flattened sphere → a thick magnifying disc. Near-clear glass + (minimal distortion) so Vee reads almost directly through it. */} Date: Mon, 15 Jun 2026 02:42:07 -0700 Subject: [PATCH 08/11] revert(site): remove the WebGL 3D hero lens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 3D glass lens didn't land — restore the original framed video mascot in the hero and drop three/@react-three/fiber/@react-three/drei. The bright palette, holographic verdict card, and theme-scan all stay. First Load JS back to 112 kB. --- website/app/(home)/styles/home.css | 28 -- website/components/home/HeroLens3D.tsx | 138 ------ website/components/home/HeroMascot.tsx | 100 +--- website/package-lock.json | 639 +------------------------ website/package.json | 6 +- 5 files changed, 36 insertions(+), 875 deletions(-) delete mode 100644 website/components/home/HeroLens3D.tsx diff --git a/website/app/(home)/styles/home.css b/website/app/(home)/styles/home.css index f8e7099..9b95926 100644 --- a/website/app/(home)/styles/home.css +++ b/website/app/(home)/styles/home.css @@ -108,34 +108,6 @@ max-width: 34ch; text-align: center; } - -/* Floating 3D glass lens (replaces the framed clip on capable desktops). The - canvas is transparent; a soft radial glow gives the lens some atmosphere so - it doesn't read as floating in a void. */ -.hero-lens-3d { - position: relative; - width: clamp(300px, 38vw, 440px); - aspect-ratio: 1 / 1; - isolation: isolate; - cursor: grab; -} -.hero-lens-3d::before { - content: ''; - position: absolute; - inset: -8%; - z-index: -1; - border-radius: 50%; - background: - radial-gradient(circle at 50% 48%, color-mix(in srgb, var(--accent) 38%, transparent), transparent 60%), - radial-gradient(circle at 50% 52%, var(--accent-glow), transparent 72%); - opacity: 0.5; - filter: blur(16px); -} -.hero-lens-3d canvas { - display: block; - width: 100% !important; - height: 100% !important; -} .hero-title { font-size: clamp(2.6rem, 0.9rem + 5.4vw, 5rem); font-weight: 800; diff --git a/website/components/home/HeroLens3D.tsx b/website/components/home/HeroLens3D.tsx deleted file mode 100644 index f74862a..0000000 --- a/website/components/home/HeroLens3D.tsx +++ /dev/null @@ -1,138 +0,0 @@ -'use client'; - -import { Suspense, useRef } from 'react'; -import { Canvas, useFrame } from '@react-three/fiber'; -import { - Environment, - Lightformer, - MeshTransmissionMaterial, - Sparkles, - useVideoTexture, -} from '@react-three/drei'; -import * as THREE from 'three'; - -// Static assets aren't auto-prefixed with the GitHub Pages basePath, so prefix by hand. -const BASE = process.env.NEXT_PUBLIC_BASE_PATH ?? ''; - -/** - * Vee — the live mascot clip — on a disc directly behind the lens, sized to sit - * fully within the lens silhouette so he's only ever seen *through* the glass - * (refracted), never as a bare rectangle. The video texture means he actually - * moves. Swap mascot.mp4 for a transparent cutout/render later for a cleaner float. - */ -function VeeDisc() { - const tex = useVideoTexture(`${BASE}/mascot.mp4`, { - muted: true, - loop: true, - start: true, - playsInline: true, - crossOrigin: 'anonymous', - }); - tex.colorSpace = THREE.SRGBColorSpace; - // Ease the playback so the loop reads gentler and more continuous. - const video = tex.image as HTMLVideoElement | undefined; - if (video) video.playbackRate = 0.8; - return ( - - - - - ); -} - -/** The glass lens + its glowing cyan aperture rim. Tilts toward the cursor and - * idles with a slow drift. */ -function Lens() { - const group = useRef(null); - const hovered = useRef(false); - useFrame((state, delta) => { - const g = group.current; - if (!g) return; - // The glass rests perfectly still until hovered; then it tilts toward the - // cursor and eases back to rest on leave. No idle motion. - const targetY = hovered.current ? state.pointer.x * 0.5 : 0; - const targetX = hovered.current ? -state.pointer.y * 0.4 : 0; - const k = 1 - Math.pow(0.0016, delta); // frame-rate-independent damping - g.rotation.y = THREE.MathUtils.lerp(g.rotation.y, targetY, k); - g.rotation.x = THREE.MathUtils.lerp(g.rotation.x, targetX, k); - }); - - return ( - { - hovered.current = true; - }} - onPointerOut={() => { - hovered.current = false; - }} - > - {/* the lens: a flattened sphere → a thick magnifying disc. Near-clear glass - (minimal distortion) so Vee reads almost directly through it. */} - - - - - {/* glowing aperture rim — a ring around the lens edge, facing the viewer - (no rotation: TorusGeometry already lies in the camera-facing plane). */} - - - - - - ); -} - -export default function HeroLens3D() { - return ( - - - - - - - - {/* drifting motes of light around the lens */} - - {/* Procedural studio env — NO external HDR (keeps zero external calls). */} - - - - - - - - ); -} diff --git a/website/components/home/HeroMascot.tsx b/website/components/home/HeroMascot.tsx index 5208a5e..2ed9697 100644 --- a/website/components/home/HeroMascot.tsx +++ b/website/components/home/HeroMascot.tsx @@ -1,10 +1,9 @@ 'use client'; -import { Component, useEffect, useState, type ReactNode } from 'react'; -import dynamic from 'next/dynamic'; +import { useEffect, useState } from 'react'; -// Static assets live in public/ and are NOT auto-prefixed with the GitHub Pages -// basePath the way next/link is — so we prefix the
); diff --git a/website/package-lock.json b/website/package-lock.json index 370b9a9..fc7268f 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -9,43 +9,24 @@ "version": "0.1.0", "hasInstallScript": true, "dependencies": { - "@react-three/drei": "^10.7.7", - "@react-three/fiber": "^9.6.1", "fumadocs-core": "^15.0.0", "fumadocs-mdx": "^11.0.0", "fumadocs-ui": "^15.0.0", "gsap": "^3.15.0", "next": "^15.3.0", "react": "^19.0.0", - "react-dom": "^19.0.0", - "three": "^0.184.0" + "react-dom": "^19.0.0" }, "devDependencies": { "@eslint/eslintrc": "^3.3.5", "@types/node": "^22.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", - "@types/three": "^0.184.1", "eslint": "^9.39.4", "eslint-config-next": "^15.5.19", "typescript": "^5.6.0" } }, - "node_modules/@babel/runtime": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", - "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@dimforge/rapier3d-compat": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", - "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", - "license": "Apache-2.0" - }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -1255,24 +1236,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/@mediapipe/tasks-vision": { - "version": "0.10.17", - "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.17.tgz", - "integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==", - "license": "Apache-2.0" - }, - "node_modules/@monogrid/gainmap-js": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@monogrid/gainmap-js/-/gainmap-js-3.4.0.tgz", - "integrity": "sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==", - "license": "MIT", - "dependencies": { - "promise-worker-transferable": "^1.0.4" - }, - "peerDependencies": { - "three": ">= 0.159.0" - } - }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", @@ -2231,94 +2194,6 @@ "integrity": "sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==", "license": "MIT" }, - "node_modules/@react-three/drei": { - "version": "10.7.7", - "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz", - "integrity": "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mediapipe/tasks-vision": "0.10.17", - "@monogrid/gainmap-js": "^3.0.6", - "@use-gesture/react": "^10.3.1", - "camera-controls": "^3.1.0", - "cross-env": "^7.0.3", - "detect-gpu": "^5.0.56", - "glsl-noise": "^0.0.0", - "hls.js": "^1.5.17", - "maath": "^0.10.8", - "meshline": "^3.3.1", - "stats-gl": "^2.2.8", - "stats.js": "^0.17.0", - "suspend-react": "^0.1.3", - "three-mesh-bvh": "^0.8.3", - "three-stdlib": "^2.35.6", - "troika-three-text": "^0.52.4", - "tunnel-rat": "^0.1.2", - "use-sync-external-store": "^1.4.0", - "utility-types": "^3.11.0", - "zustand": "^5.0.1" - }, - "peerDependencies": { - "@react-three/fiber": "^9.0.0", - "react": "^19", - "react-dom": "^19", - "three": ">=0.159" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/@react-three/fiber": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.6.1.tgz", - "integrity": "sha512-zF0rsKcVYpcJwbFEnv2HkHX9cvOEgsfQo/X8lwmR2dn13S4qEQJXir9fxf5js2LQFoXqxOY7MDkOkYx2uZ4gSg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.17.8", - "@types/webxr": "*", - "base64-js": "^1.5.1", - "buffer": "^6.0.3", - "its-fine": "^2.0.0", - "react-use-measure": "^2.1.7", - "scheduler": "^0.27.0", - "suspend-react": "^0.1.3", - "use-sync-external-store": "^1.4.0", - "zustand": "^5.0.3" - }, - "peerDependencies": { - "expo": ">=43.0", - "expo-asset": ">=8.4", - "expo-file-system": ">=11.0", - "expo-gl": ">=11.0", - "react": ">=19 <19.3", - "react-dom": ">=19 <19.3", - "react-native": ">=0.78", - "three": ">=0.156" - }, - "peerDependenciesMeta": { - "expo": { - "optional": true - }, - "expo-asset": { - "optional": true - }, - "expo-file-system": { - "optional": true - }, - "expo-gl": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -2439,12 +2314,6 @@ "tslib": "^2.8.0" } }, - "node_modules/@tweenjs/tween.js": { - "version": "23.1.3", - "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", - "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", - "license": "MIT" - }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", @@ -2465,12 +2334,6 @@ "@types/ms": "*" } }, - "node_modules/@types/draco3d": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz", - "integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==", - "license": "MIT" - }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -2540,16 +2403,11 @@ "undici-types": "~6.21.0" } }, - "node_modules/@types/offscreencanvas": { - "version": "2019.7.3", - "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", - "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==", - "license": "MIT" - }, "node_modules/@types/react": { "version": "19.2.17", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -2565,47 +2423,12 @@ "@types/react": "^19.2.0" } }, - "node_modules/@types/react-reconciler": { - "version": "0.28.9", - "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", - "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/stats.js": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", - "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", - "license": "MIT" - }, - "node_modules/@types/three": { - "version": "0.184.1", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.184.1.tgz", - "integrity": "sha512-6q4VdiqVsrTRqmk62/BnlcAvIrnDM0zf2ZDVKI5kZiniWrSaOHaQzmbp+BNzoggc/8tgW412pL//wZIxu2PPTA==", - "license": "MIT", - "dependencies": { - "@dimforge/rapier3d-compat": "~0.12.0", - "@tweenjs/tween.js": "~23.1.3", - "@types/stats.js": "*", - "@types/webxr": ">=0.5.17", - "fflate": "~0.8.2", - "meshoptimizer": "~1.1.1" - } - }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, - "node_modules/@types/webxr": { - "version": "0.5.24", - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", - "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", - "license": "MIT" - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.61.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.0.tgz", @@ -3218,24 +3041,6 @@ "win32" ] }, - "node_modules/@use-gesture/core": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", - "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", - "license": "MIT" - }, - "node_modules/@use-gesture/react": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", - "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", - "license": "MIT", - "dependencies": { - "@use-gesture/core": "10.3.1" - }, - "peerDependencies": { - "react": ">= 16.8.0" - } - }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -3557,35 +3362,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bidi-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", - "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", - "license": "MIT", - "dependencies": { - "require-from-string": "^2.0.2" - } - }, "node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -3610,30 +3386,6 @@ "node": ">=8" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", @@ -3694,19 +3446,6 @@ "node": ">=6" } }, - "node_modules/camera-controls": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.2.tgz", - "integrity": "sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==", - "license": "MIT", - "engines": { - "node": ">=22.0.0", - "npm": ">=10.5.1" - }, - "peerDependencies": { - "three": ">=0.126.1" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001797", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz", @@ -3889,28 +3628,11 @@ "dev": true, "license": "MIT" }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -3937,6 +3659,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -4082,15 +3805,6 @@ "node": ">=6" } }, - "node_modules/detect-gpu": { - "version": "5.0.70", - "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.70.tgz", - "integrity": "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==", - "license": "MIT", - "dependencies": { - "webgl-constants": "^1.1.1" - } - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -4133,12 +3847,6 @@ "node": ">=0.10.0" } }, - "node_modules/draco3d": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz", - "integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==", - "license": "Apache-2.0" - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -5030,12 +4738,6 @@ } } }, - "node_modules/fflate": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", - "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", - "license": "MIT" - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5463,12 +5165,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glsl-noise": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", - "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==", - "license": "MIT" - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -5686,12 +5382,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hls.js": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.16.tgz", - "integrity": "sha512-VSIRpLfRwlAAdGL4wiTucx2ScRipo0ed1FBatWkyt832jC4CReKstga6yIhYVwGu9LOBjuX9wzmRMeQdBJtzEA==", - "license": "Apache-2.0" - }, "node_modules/html-void-elements": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", @@ -5702,26 +5392,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5744,12 +5414,6 @@ "node": ">=16.x" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" - }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -6127,12 +5791,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "license": "MIT" - }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -6289,6 +5947,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, "node_modules/iterator.prototype": { @@ -6309,18 +5968,6 @@ "node": ">= 0.4" } }, - "node_modules/its-fine": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz", - "integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==", - "license": "MIT", - "dependencies": { - "@types/react-reconciler": "^0.28.9" - }, - "peerDependencies": { - "react": "^19.0.0" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6444,15 +6091,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6507,16 +6145,6 @@ "node": "20 || >=22" } }, - "node_modules/maath": { - "version": "0.10.8", - "resolved": "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz", - "integrity": "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==", - "license": "MIT", - "peerDependencies": { - "@types/three": ">=0.134.0", - "three": ">=0.134.0" - } - }, "node_modules/markdown-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", @@ -6846,21 +6474,6 @@ "node": ">= 8" } }, - "node_modules/meshline": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz", - "integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==", - "license": "MIT", - "peerDependencies": { - "three": ">=0.137" - } - }, - "node_modules/meshoptimizer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz", - "integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==", - "license": "MIT" - }, "node_modules/micromark": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", @@ -8047,6 +7660,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8138,12 +7752,6 @@ "node": ">=4" } }, - "node_modules/potpack": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", - "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", - "license": "ISC" - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -8154,16 +7762,6 @@ "node": ">= 0.8.0" } }, - "node_modules/promise-worker-transferable": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz", - "integrity": "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==", - "license": "Apache-2.0", - "dependencies": { - "is-promise": "^2.1.0", - "lie": "^3.0.2" - } - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -8330,21 +7928,6 @@ } } }, - "node_modules/react-use-measure": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz", - "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.13", - "react-dom": ">=16.13" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -8604,15 +8187,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "2.0.0-next.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", @@ -8873,6 +8447,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -8885,6 +8460,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -9017,32 +8593,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stats-gl": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-2.4.2.tgz", - "integrity": "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==", - "license": "MIT", - "dependencies": { - "@types/three": "*", - "three": "^0.170.0" - }, - "peerDependencies": { - "@types/three": "*", - "three": "*" - } - }, - "node_modules/stats-gl/node_modules/three": { - "version": "0.170.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.170.0.tgz", - "integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==", - "license": "MIT" - }, - "node_modules/stats.js": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz", - "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==", - "license": "MIT" - }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -9275,15 +8825,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/suspend-react": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", - "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==", - "license": "MIT", - "peerDependencies": { - "react": ">=17.0" - } - }, "node_modules/tailwind-merge": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz", @@ -9294,44 +8835,6 @@ "url": "https://github.com/sponsors/dcastil" } }, - "node_modules/three": { - "version": "0.184.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.184.0.tgz", - "integrity": "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==", - "license": "MIT" - }, - "node_modules/three-mesh-bvh": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.8.3.tgz", - "integrity": "sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg==", - "license": "MIT", - "peerDependencies": { - "three": ">= 0.159.0" - } - }, - "node_modules/three-stdlib": { - "version": "2.36.1", - "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.36.1.tgz", - "integrity": "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==", - "license": "MIT", - "dependencies": { - "@types/draco3d": "^1.4.0", - "@types/offscreencanvas": "^2019.6.4", - "@types/webxr": "^0.5.2", - "draco3d": "^1.4.1", - "fflate": "^0.6.9", - "potpack": "^1.0.1" - }, - "peerDependencies": { - "three": ">=0.128.0" - } - }, - "node_modules/three-stdlib/node_modules/fflate": { - "version": "0.6.10", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", - "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==", - "license": "MIT" - }, "node_modules/tinyexec": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", @@ -9380,36 +8883,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/troika-three-text": { - "version": "0.52.4", - "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz", - "integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==", - "license": "MIT", - "dependencies": { - "bidi-js": "^1.0.2", - "troika-three-utils": "^0.52.4", - "troika-worker-utils": "^0.52.0", - "webgl-sdf-generator": "1.1.1" - }, - "peerDependencies": { - "three": ">=0.125.0" - } - }, - "node_modules/troika-three-utils": { - "version": "0.52.4", - "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz", - "integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==", - "license": "MIT", - "peerDependencies": { - "three": ">=0.125.0" - } - }, - "node_modules/troika-worker-utils": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz", - "integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==", - "license": "MIT" - }, "node_modules/trough": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", @@ -9452,43 +8925,6 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "node_modules/tunnel-rat": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz", - "integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==", - "license": "MIT", - "dependencies": { - "zustand": "^4.3.2" - } - }, - "node_modules/tunnel-rat/node_modules/zustand": { - "version": "4.5.7", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", - "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", - "license": "MIT", - "dependencies": { - "use-sync-external-store": "^1.2.2" - }, - "engines": { - "node": ">=12.7.0" - }, - "peerDependencies": { - "@types/react": ">=16.8", - "immer": ">=9.0.6", - "react": ">=16.8" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "immer": { - "optional": true - }, - "react": { - "optional": true - } - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -9811,30 +9247,12 @@ } } }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, - "node_modules/utility-types": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", - "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -9863,21 +9281,11 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/webgl-constants": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", - "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" - }, - "node_modules/webgl-sdf-generator": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", - "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==", - "license": "MIT" - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -10010,35 +9418,6 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/zustand": { - "version": "5.0.14", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz", - "integrity": "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==", - "license": "MIT", - "engines": { - "node": ">=12.20.0" - }, - "peerDependencies": { - "@types/react": ">=18.0.0", - "immer": ">=9.0.6", - "react": ">=18.0.0", - "use-sync-external-store": ">=1.2.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "immer": { - "optional": true - }, - "react": { - "optional": true - }, - "use-sync-external-store": { - "optional": true - } - } - }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/website/package.json b/website/package.json index 27506cb..0659561 100644 --- a/website/package.json +++ b/website/package.json @@ -10,23 +10,19 @@ "postinstall": "fumadocs-mdx" }, "dependencies": { - "@react-three/drei": "^10.7.7", - "@react-three/fiber": "^9.6.1", "fumadocs-core": "^15.0.0", "fumadocs-mdx": "^11.0.0", "fumadocs-ui": "^15.0.0", "gsap": "^3.15.0", "next": "^15.3.0", "react": "^19.0.0", - "react-dom": "^19.0.0", - "three": "^0.184.0" + "react-dom": "^19.0.0" }, "devDependencies": { "@eslint/eslintrc": "^3.3.5", "@types/node": "^22.0.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", - "@types/three": "^0.184.1", "eslint": "^9.39.4", "eslint-config-next": "^15.5.19", "typescript": "^5.6.0" From a2c164161858e6b7babbca8713dd6e1170201405 Mon Sep 17 00:00:00 2001 From: ares <285551516+New1Direction@users.noreply.github.com> Date: Mon, 15 Jun 2026 03:01:33 -0700 Subject: [PATCH 09/11] feat(site): smoother, seamless hero mascot loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single clip had a hard cut at the loop point. Render a boomerang (forward → reversed, via ffmpeg, -tune animation) so it loops with no seam, and ease playback to 0.85x for a gentler feel. Replaces mascot.mp4 (185K) with mascot-loop.mp4 (403K, 12s). Reduced-motion still shows the static poster. --- website/components/home/HeroMascot.tsx | 14 +++++++++----- website/public/mascot-loop.mp4 | Bin 0 -> 413073 bytes website/public/mascot.mp4 | Bin 188961 -> 0 bytes 3 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 website/public/mascot-loop.mp4 delete mode 100644 website/public/mascot.mp4 diff --git a/website/components/home/HeroMascot.tsx b/website/components/home/HeroMascot.tsx index 2ed9697..a4c9339 100644 --- a/website/components/home/HeroMascot.tsx +++ b/website/components/home/HeroMascot.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; // Static assets live in public/ and are NOT auto-prefixed with the GitHub // Pages basePath the way next/link is — so we prefix the