From babf5e73379f4cd80396bc4a371a03704ddaffe0 Mon Sep 17 00:00:00 2001 From: Pratyush Sharma <56130065+pratyush618@users.noreply.github.com> Date: Fri, 26 Jun 2026 18:06:42 +0530 Subject: [PATCH] feat: Midnight theme, IBM Plex, hero glow --- .vscode/settings.json | 6 + package.json | 1 - pnpm-lock.yaml | 44 +++++-- src/app/globals.css | 211 ++++++++++++++++++++++++++--- src/app/layout.tsx | 29 +++- src/components/docs-hero.tsx | 18 ++- src/components/hero-net.tsx | 220 +++++++++++++++++++++++++++++++ src/components/tools-section.tsx | 2 +- src/components/ui/section.tsx | 24 +++- 9 files changed, 513 insertions(+), 42 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/components/hero-net.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..fb1f903 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "// Tailwind v4 at-rules (@custom-variant, @theme, @apply, …) are unknown to": "the built-in CSS validator; silence its false positives.", + "css.lint.unknownAtRules": "ignore", + "scss.lint.unknownAtRules": "ignore", + "less.lint.unknownAtRules": "ignore" +} diff --git a/package.json b/package.json index cb62929..32a8571 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "sync-ui": "bash scripts/sync-ui.sh" }, "dependencies": { - "geist": "^1.7.0", "lucide-react": "^1.8.0", "next": "16.2.3", "next-themes": "^0.4.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ca944e..9c2104d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,6 @@ importers: .: dependencies: - geist: - specifier: ^1.7.0 - version: 1.7.0(next@16.2.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)) lucide-react: specifier: ^1.8.0 version: 1.8.0(react@19.2.5) @@ -77,24 +74,28 @@ packages: engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [musl] '@biomejs/cli-linux-arm64@2.4.11': resolution: {integrity: sha512-avdJaEElXrKceK0va9FkJ4P5ci3N01TGkc6ni3P8l3BElqbOz42Wg2IyX3gbh0ZLEd4HVKEIrmuVu/AMuSeFFA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [glibc] '@biomejs/cli-linux-x64-musl@2.4.11': resolution: {integrity: sha512-bexd2IklK7ZgPhrz6jXzpIL6dEAH9MlJU1xGTrypx+FICxrXUp4CqtwfiuoDKse+UlgAlWtzML3jrMqeEAHEhA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [musl] '@biomejs/cli-linux-x64@2.4.11': resolution: {integrity: sha512-TagWV0iomp5LnEnxWFg4nQO+e52Fow349vaX0Q/PIcX6Zhk4GGBgp3qqZ8PVkpC+cuehRctMf3+6+FgQ8jCEFQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [glibc] '@biomejs/cli-win32-arm64@2.4.11': resolution: {integrity: sha512-RJhaTnY8byzxDt4bDVb7AFPHkPcjOPK3xBip4ZRTrN3TEfyhjLRm3r3mqknqydgVTB74XG8l4jMLwEACEeihVg==} @@ -141,89 +142,105 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -284,24 +301,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-musl@16.2.3': resolution: {integrity: sha512-/YV0LgjHUmfhQpn9bVoGc4x4nan64pkhWR5wyEV8yCOfwwrH630KpvRg86olQHTwHIn1z59uh6JwKvHq1h4QEw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-x64-gnu@16.2.3': resolution: {integrity: sha512-/HiWEcp+WMZ7VajuiMEFGZ6cg0+aYZPqCJD3YJEfpVWQsKYSjXQG06vJP6F1rdA03COD9Fef4aODs3YxKx+RDQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-musl@16.2.3': resolution: {integrity: sha512-Kt44hGJfZSefebhk/7nIdivoDr3Ugp5+oNz9VvF3GUtfxutucUIHfIO0ZYO8QlOPDQloUVQn4NVC/9JvHRk9hw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-win32-arm64-msvc@16.2.3': resolution: {integrity: sha512-O2NZ9ie3Tq6xj5Z5CSwBT3+aWAMW2PIZ4egUi9MaWLkwaehgtB7YZjPm+UpcNpKOme0IQuqDcor7BsW6QBiQBw==} @@ -356,24 +377,28 @@ packages: engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.2.2': resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.2.2': resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.2.2': resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.2.2': resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} @@ -439,11 +464,6 @@ packages: resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} engines: {node: '>=10.13.0'} - geist@1.7.0: - resolution: {integrity: sha512-ZaoiZwkSf0DwwB1ncdLKp+ggAldqxl5L1+SXaNIBGkPAqcu+xjVJLxlf3/S8vLt9UHx1xu5fz3lbzKCj5iOVdQ==} - peerDependencies: - next: '>=13.2.0' - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -486,24 +506,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -914,10 +938,6 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.2 - geist@1.7.0(next@16.2.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)): - dependencies: - next: 16.2.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - graceful-fs@4.2.11: {} jiti@2.6.1: {} diff --git a/src/app/globals.css b/src/app/globals.css index 903d330..908d077 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -2,22 +2,29 @@ @custom-variant dark (&:where(.dark, .dark *)); +/* ============================================================ + Theme — Midnight (palette shared with byteveda.org) + Warm-paper light · near-black + electric green dark · IBM Plex. + ============================================================ */ + +/* light — warm paper */ :root { - --background: #ffffff; - --foreground: #0a0a0a; - --muted: #f4f4f5; - --muted-foreground: #52525b; - --border: #e4e4e7; - --accent: #10b981; + --background: #f6f4ee; + --foreground: #1b1a15; + --muted: #f2efe7; + --muted-foreground: #615d52; + --border: rgba(45, 38, 18, 0.1); + --accent: #10a152; } +/* dark — Midnight */ .dark { - --background: #09090b; - --foreground: #fafafa; - --muted: #18181b; - --muted-foreground: #a1a1aa; - --border: #27272a; - --accent: #34d399; + --background: #08080c; + --foreground: #ececf4; + --muted: #13131d; + --muted-foreground: #8c8ca3; + --border: rgba(255, 255, 255, 0.08); + --accent: #1f9d54; } @theme inline { @@ -27,8 +34,8 @@ --color-muted-foreground: var(--muted-foreground); --color-border: var(--border); --color-accent: var(--accent); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); + --font-sans: var(--font-plex-sans); + --font-mono: var(--font-plex-mono); } html { @@ -38,14 +45,14 @@ html { body { background: var(--background); color: var(--foreground); - font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif; + font-family: var(--font-plex-sans), ui-sans-serif, system-ui, sans-serif; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; } ::selection { background: var(--accent); - color: #0a0a0a; + color: #fff; } .dot-grid { @@ -53,6 +60,178 @@ body { background-size: 24px 24px; } +/* ---------- hero ambiance (green glow · blueprint grid · constellation) ---------- */ +:root { + --grid-line: rgba(16, 161, 82, 0.06); +} +.dark { + --grid-line: rgba(31, 157, 84, 0.07); +} + +.aurora { + position: absolute; + inset: 0; + z-index: 0; + pointer-events: none; + overflow: hidden; +} +.aurora b { + position: absolute; + display: block; + border-radius: 50%; + filter: blur(82px); + mix-blend-mode: multiply; + background: radial-gradient(circle at 50% 50%, var(--accent), transparent 66%); + will-change: transform; +} +.dark .aurora b { + mix-blend-mode: screen; + filter: blur(72px); +} +.aurora .a1 { + width: 52vw; + height: 52vw; + left: -8vw; + top: -18vw; + opacity: 0.14; +} +.aurora .a2 { + width: 40vw; + height: 40vw; + right: -6vw; + top: 4vh; + opacity: 0.1; +} +.aurora .a3 { + width: 34vw; + height: 34vw; + left: 26vw; + bottom: -22vw; + opacity: 0.08; +} +.dark .aurora .a1 { + opacity: 0.42; +} +.dark .aurora .a2 { + opacity: 0.3; +} +.dark .aurora .a3 { + opacity: 0.2; +} + +.blueprint { + position: absolute; + inset: 0; + z-index: 0; + pointer-events: none; + background-image: + linear-gradient(var(--grid-line) 1px, transparent 1px), + linear-gradient(90deg, var(--grid-line) 1px, transparent 1px); + background-size: 46px 46px; + mask-image: radial-gradient(120% 100% at 50% 0%, #000 30%, transparent 78%); +} +.hero-net { + position: absolute; + inset: 0; + z-index: 0; + pointer-events: none; +} + +/* subtler ambient glow for content sections (behind cards) */ +.section-glow { + position: absolute; + inset: 0; + z-index: 0; + pointer-events: none; + overflow: hidden; +} +.section-glow b { + position: absolute; + display: block; + border-radius: 50%; + filter: blur(90px); + mix-blend-mode: multiply; + background: radial-gradient(circle at 50% 50%, var(--accent), transparent 64%); + will-change: transform; +} +.dark .section-glow b { + mix-blend-mode: screen; +} +.section-glow .g1 { + width: 40vw; + height: 40vw; + left: -8vw; + top: 6%; + opacity: 0.07; +} +.section-glow .g2 { + width: 36vw; + height: 36vw; + right: -10vw; + bottom: -12%; + opacity: 0.06; +} +.dark .section-glow .g1 { + opacity: 0.16; +} +.dark .section-glow .g2 { + opacity: 0.13; +} +@media (prefers-reduced-motion: no-preference) { + .section-glow .g1 { + animation: bloom-2 24s ease-in-out infinite alternate; + } + .section-glow .g2 { + animation: bloom-3 19s ease-in-out infinite alternate; + } +} + +@media (prefers-reduced-motion: no-preference) { + .aurora .a1 { + animation: bloom-1 17s ease-in-out infinite alternate; + } + .aurora .a2 { + animation: bloom-2 21s ease-in-out infinite alternate; + } + .aurora .a3 { + animation: bloom-3 15s ease-in-out infinite alternate; + } + .blueprint { + animation: grid-drift 36s linear infinite; + } +} +@keyframes bloom-1 { + from { + transform: translate(-40px, -30px) scale(1); + } + to { + transform: translate(90px, 60px) scale(1.18); + } +} +@keyframes bloom-2 { + from { + transform: translate(50px, 30px) scale(1.1); + } + to { + transform: translate(-60px, -40px) scale(1); + } +} +@keyframes bloom-3 { + from { + transform: translate(-30px, 40px) scale(1.05); + } + to { + transform: translate(50px, -50px) scale(1.2); + } +} +@keyframes grid-drift { + to { + background-position: + 46px 46px, + 46px 46px; + } +} + @keyframes fade-up { from { opacity: 0; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index ba35cfd..b532790 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,10 +1,24 @@ -import { GeistMono } from "geist/font/mono"; -import { GeistSans } from "geist/font/sans"; -import type { Metadata } from "next"; +import type { Metadata, Viewport } from "next"; +import { IBM_Plex_Mono, IBM_Plex_Sans } from "next/font/google"; import { site } from "@/lib/site"; import { ThemeProvider } from "@/providers/theme-provider"; import "./globals.css"; +const plexSans = IBM_Plex_Sans({ + subsets: ["latin"], + weight: ["400", "500", "600", "700"], + style: ["normal", "italic"], + variable: "--font-plex-sans", + display: "swap", +}); + +const plexMono = IBM_Plex_Mono({ + subsets: ["latin"], + weight: ["400", "500"], + variable: "--font-plex-mono", + display: "swap", +}); + export const metadata: Metadata = { metadataBase: new URL(site.url), title: { @@ -41,12 +55,19 @@ export const metadata: Metadata = { alternates: { canonical: site.url }, }; +export const viewport: Viewport = { + themeColor: [ + { media: "(prefers-color-scheme: light)", color: "#f6f4ee" }, + { media: "(prefers-color-scheme: dark)", color: "#08080c" }, + ], +}; + export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) { return (
Documentation
diff --git a/src/components/hero-net.tsx b/src/components/hero-net.tsx
new file mode 100644
index 0000000..250225e
--- /dev/null
+++ b/src/components/hero-net.tsx
@@ -0,0 +1,220 @@
+"use client";
+
+import { useEffect, useRef } from "react";
+
+const LINK = 132; // px distance at which nodes connect
+const MOUSE_LINK = 168; // px distance for cursor links
+
+type Node = { x: number; y: number; vx: number; vy: number; r: number };
+
+function hexToRgb(hex: string): [number, number, number] | null {
+ let h = hex.trim().replace("#", "");
+ if (h.length === 3) h = h.replace(/(.)/g, "$1$1");
+ const n = Number.parseInt(h, 16);
+ if (Number.isNaN(n) || h.length < 6) return null;
+ return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
+}
+
+/**
+ * Hero connective network: drifting nodes + proximity links + gentle cursor
+ * pull. Accent- and theme-aware (reads `--accent`, re-reads on `.dark` toggle),
+ * pauses off-screen / on hidden tab, honours `prefers-reduced-motion`.
+ */
+export function HeroNet() {
+ const canvasRef = useRef