diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5ef6a52
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,41 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env*
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e215bc4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,36 @@
+This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+# or
+bun dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
diff --git a/mdx-components.tsx b/mdx-components.tsx
new file mode 100644
index 0000000..d5b4219
--- /dev/null
+++ b/mdx-components.tsx
@@ -0,0 +1,13 @@
+import type { MDXComponents } from "mdx/types";
+import MetricCallout from "@/components/work/MetricCallout";
+import Annotation from "@/components/work/Annotation";
+import TokenTable from "@/components/work/TokenTable";
+
+export function useMDXComponents(components: MDXComponents): MDXComponents {
+ return {
+ MetricCallout,
+ Annotation,
+ TokenTable,
+ ...components,
+ };
+}
diff --git a/next.config.ts b/next.config.ts
new file mode 100644
index 0000000..4252099
--- /dev/null
+++ b/next.config.ts
@@ -0,0 +1,11 @@
+import type { NextConfig } from "next";
+import createMDX from "@next/mdx";
+
+const withMDX = createMDX({});
+
+const nextConfig: NextConfig = {
+ pageExtensions: ["ts", "tsx", "mdx"],
+ transpilePackages: ["@mdx-js/react"],
+};
+
+export default withMDX(nextConfig);
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..d3bc4f6
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2825 @@
+{
+ "name": "portfolio",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "portfolio",
+ "version": "0.1.0",
+ "dependencies": {
+ "@mdx-js/loader": "^3.1.1",
+ "@mdx-js/react": "^3.1.1",
+ "@next/mdx": "^15.5.15",
+ "gray-matter": "^4.0.3",
+ "next": "15.5.15",
+ "react": "19.1.0",
+ "react-dom": "19.1.0"
+ },
+ "devDependencies": {
+ "@types/mdx": "^2.0.13",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "typescript": "^5"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz",
+ "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@mdx-js/loader": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@mdx-js/loader/-/loader-3.1.1.tgz",
+ "integrity": "sha512-0TTacJyZ9mDmY+VefuthVshaNIyCGZHJG2fMnGaDttCt8HmjUF7SizlHJpaCDoGnN635nK1wpzfpx/Xx5S4WnQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@mdx-js/mdx": "^3.0.0",
+ "source-map": "^0.7.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "webpack": ">=5"
+ },
+ "peerDependenciesMeta": {
+ "webpack": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mdx-js/mdx": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz",
+ "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdx": "^2.0.0",
+ "acorn": "^8.0.0",
+ "collapse-white-space": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "estree-util-scope": "^1.0.0",
+ "estree-walker": "^3.0.0",
+ "hast-util-to-jsx-runtime": "^2.0.0",
+ "markdown-extensions": "^2.0.0",
+ "recma-build-jsx": "^1.0.0",
+ "recma-jsx": "^1.0.0",
+ "recma-stringify": "^1.0.0",
+ "rehype-recma": "^1.0.0",
+ "remark-mdx": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.0.0",
+ "source-map": "^0.7.0",
+ "unified": "^11.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/@mdx-js/react": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz",
+ "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdx": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16",
+ "react": ">=16"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "15.5.15",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.15.tgz",
+ "integrity": "sha512-vcmyu5/MyFzN7CdqRHO3uHO44p/QPCZkuTUXroeUmhNP8bL5PHFEhik22JUazt+CDDoD6EpBYRCaS2pISL+/hg==",
+ "license": "MIT"
+ },
+ "node_modules/@next/mdx": {
+ "version": "15.5.15",
+ "resolved": "https://registry.npmjs.org/@next/mdx/-/mdx-15.5.15.tgz",
+ "integrity": "sha512-4F4A89eL7ZoBjh4MEBQQzZvgKfS24hXHbqS9CsncwtLctgWYTilXlvDxTWYDokgq8jJxeJnBEooBLogZzW/vRw==",
+ "license": "MIT",
+ "dependencies": {
+ "source-map": "^0.7.0"
+ },
+ "peerDependencies": {
+ "@mdx-js/loader": ">=0.15.0",
+ "@mdx-js/react": ">=0.15.0"
+ },
+ "peerDependenciesMeta": {
+ "@mdx-js/loader": {
+ "optional": true
+ },
+ "@mdx-js/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "15.5.15",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.15.tgz",
+ "integrity": "sha512-6PvFO2Tzt10GFK2Ro9tAVEtacMqRmTarYMFKAnV2vYMdwWc73xzmDQyAV7SwEdMhzmiRoo7+m88DuiXlJlGeaw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "15.5.15",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.15.tgz",
+ "integrity": "sha512-G+YNV+z6FDZTp/+IdGyIMFqalBTaQSnvAA+X/hrt+eaTRFSznRMz9K7rTmzvM6tDmKegNtyzgufZW0HwVzEqaQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "15.5.15",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.15.tgz",
+ "integrity": "sha512-eVkrMcVIBqGfXB+QUC7jjZ94Z6uX/dNStbQFabewAnk13Uy18Igd1YZ/GtPRzdhtm7QwC0e6o7zOQecul4iC1w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "15.5.15",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.15.tgz",
+ "integrity": "sha512-RwSHKMQ7InLy5GfkY2/n5PcFycKA08qI1VST78n09nN36nUPqCvGSMiLXlfUmzmpQpF6XeBYP2KRWHi0UW3uNg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "15.5.15",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.15.tgz",
+ "integrity": "sha512-nplqvY86LakS+eeiuWsNWvfmK8pFcOEW7ZtVRt4QH70lL+0x6LG/m1OpJ/tvrbwjmR8HH9/fH2jzW1GlL03TIg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "15.5.15",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.15.tgz",
+ "integrity": "sha512-eAgl9NKQ84/sww0v81DQINl/vL2IBxD7sMybd0cWRw6wqgouVI53brVRBrggqBRP/NWeIAE1dm5cbKYoiMlqDQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "15.5.15",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.15.tgz",
+ "integrity": "sha512-GJVZC86lzSquh0MtvZT+L7G8+jMnJcldloOjA8Kf3wXvBrvb6OGe2MzPuALxFshSm/IpwUtD2mIoof39ymf52A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "15.5.15",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.15.tgz",
+ "integrity": "sha512-nFucjVdwlFqxh/JG3hWSJ4p8+YJV7Ii8aPDuBQULB6DzUF4UNZETXLfEUk+oI2zEznWWULPt7MeuTE6xtK1HSA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
+ "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/estree-jsx": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/mdx": {
+ "version": "2.0.13",
+ "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz",
+ "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.39",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz",
+ "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.14",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
+ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "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/@ungap/structured-clone": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
+ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
+ "license": "ISC"
+ },
+ "node_modules/acorn": {
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/astring": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz",
+ "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==",
+ "license": "MIT",
+ "bin": {
+ "astring": "bin/astring"
+ }
+ },
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001788",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz",
+ "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/collapse-white-space": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz",
+ "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decode-named-character-reference": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
+ "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/esast-util-from-estree": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz",
+ "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-visit": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/esast-util-from-js": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz",
+ "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "acorn": "^8.0.0",
+ "esast-util-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/estree-util-attach-comments": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz",
+ "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-build-jsx": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz",
+ "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "estree-walker": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-is-identifier-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
+ "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-scope": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz",
+ "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-to-js": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz",
+ "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "astring": "^1.8.0",
+ "source-map": "^0.7.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-visit": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz",
+ "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
+ "node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "license": "MIT",
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/gray-matter": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz",
+ "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-yaml": "^3.13.1",
+ "kind-of": "^6.0.2",
+ "section-matter": "^1.0.0",
+ "strip-bom-string": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/hast-util-to-estree": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz",
+ "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-attach-comments": "^3.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-js": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-jsx-runtime": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
+ "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-js": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/inline-style-parser": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
+ "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
+ "license": "MIT"
+ },
+ "node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+ "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/markdown-extensions": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz",
+ "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mdast-util-from-markdown": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz",
+ "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark": "^4.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz",
+ "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-expression": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
+ "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-jsx": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
+ "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdxjs-esm": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
+ "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-markdown": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
+ "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^4.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
+ "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
+ "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-destination": "^2.0.0",
+ "micromark-factory-label": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-title": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-mdx-expression": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz",
+ "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-mdx-expression": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-mdx-jsx": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz",
+ "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "micromark-factory-mdx-expression": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdx-md": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz",
+ "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdxjs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz",
+ "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.0.0",
+ "acorn-jsx": "^5.0.0",
+ "micromark-extension-mdx-expression": "^3.0.0",
+ "micromark-extension-mdx-jsx": "^3.0.0",
+ "micromark-extension-mdx-md": "^2.0.0",
+ "micromark-extension-mdxjs-esm": "^3.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-mdxjs-esm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz",
+ "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-mdx-expression": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz",
+ "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ }
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
+ "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-events-to-acorn": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz",
+ "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-visit": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ }
+ },
+ "node_modules/micromark-util-html-tag-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
+ "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/next": {
+ "version": "15.5.15",
+ "resolved": "https://registry.npmjs.org/next/-/next-15.5.15.tgz",
+ "integrity": "sha512-VSqCrJwtLVGwAVE0Sb/yikrQfkwkZW9p+lL/J4+xe+G3ZA+QnWPqgcfH1tDUEuk9y+pthzzVFp4L/U8JerMfMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "15.5.15",
+ "@swc/helpers": "0.5.15",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "15.5.15",
+ "@next/swc-darwin-x64": "15.5.15",
+ "@next/swc-linux-arm64-gnu": "15.5.15",
+ "@next/swc-linux-arm64-musl": "15.5.15",
+ "@next/swc-linux-x64-gnu": "15.5.15",
+ "@next/swc-linux-x64-musl": "15.5.15",
+ "@next/swc-win32-arm64-msvc": "15.5.15",
+ "@next/swc-win32-x64-msvc": "15.5.15",
+ "sharp": "^0.34.3"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.51.1",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/parse-entities": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/property-information": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
+ "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
+ "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
+ "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.26.0"
+ },
+ "peerDependencies": {
+ "react": "^19.1.0"
+ }
+ },
+ "node_modules/recma-build-jsx": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz",
+ "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-util-build-jsx": "^3.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/recma-jsx": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz",
+ "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn-jsx": "^5.0.0",
+ "estree-util-to-js": "^2.0.0",
+ "recma-parse": "^1.0.0",
+ "recma-stringify": "^1.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/recma-parse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz",
+ "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "esast-util-from-js": "^2.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/recma-stringify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz",
+ "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-util-to-js": "^2.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-recma": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz",
+ "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "hast-util-to-estree": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-mdx": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz",
+ "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-mdx": "^3.0.0",
+ "micromark-extension-mdxjs": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-parse": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
+ "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-rehype": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
+ "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.26.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
+ "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
+ "license": "MIT"
+ },
+ "node_modules/section-matter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
+ "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
+ "license": "MIT",
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.5",
+ "@img/sharp-darwin-x64": "0.34.5",
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
+ "@img/sharp-libvips-linux-arm": "1.2.4",
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
+ "@img/sharp-libvips-linux-x64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+ "@img/sharp-linux-arm": "0.34.5",
+ "@img/sharp-linux-arm64": "0.34.5",
+ "@img/sharp-linux-ppc64": "0.34.5",
+ "@img/sharp-linux-riscv64": "0.34.5",
+ "@img/sharp-linux-s390x": "0.34.5",
+ "@img/sharp-linux-x64": "0.34.5",
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
+ "@img/sharp-linuxmusl-x64": "0.34.5",
+ "@img/sharp-wasm32": "0.34.5",
+ "@img/sharp-win32-arm64": "0.34.5",
+ "@img/sharp-win32-ia32": "0.34.5",
+ "@img/sharp-win32-x64": "0.34.5"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
+ "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/stringify-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/strip-bom-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
+ "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/style-to-js": {
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
+ "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "style-to-object": "1.0.14"
+ }
+ },
+ "node_modules/style-to-object": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz",
+ "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==",
+ "license": "MIT",
+ "dependencies": {
+ "inline-style-parser": "0.2.7"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/trough": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unified": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
+ "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "bail": "^2.0.0",
+ "devlop": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
+ "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position-from-estree": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz",
+ "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
+ "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
+ "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
+ "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..774e364
--- /dev/null
+++ b/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "portfolio",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start"
+ },
+ "dependencies": {
+ "@mdx-js/loader": "^3.1.1",
+ "@mdx-js/react": "^3.1.1",
+ "@next/mdx": "^15.5.15",
+ "gray-matter": "^4.0.3",
+ "next": "15.5.15",
+ "react": "19.1.0",
+ "react-dom": "19.1.0"
+ },
+ "devDependencies": {
+ "@types/mdx": "^2.0.13",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "typescript": "^5"
+ }
+}
diff --git a/src/app/about/page.module.css b/src/app/about/page.module.css
new file mode 100644
index 0000000..cb9b13e
--- /dev/null
+++ b/src/app/about/page.module.css
@@ -0,0 +1,60 @@
+.main {
+ padding: var(--space-section) var(--space-gutter);
+ padding-bottom: var(--space-section);
+
+ @media (min-width: 768px) {
+ padding: var(--space-section) var(--space-gutter-lg);
+ }
+}
+
+.inner {
+ max-width: var(--max-width-wide);
+ margin: 0 auto;
+}
+
+.header {
+ max-width: var(--max-width-content);
+ margin-bottom: var(--space-section-sm);
+}
+
+.label {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-medium);
+ letter-spacing: var(--tracking-widest);
+ text-transform: uppercase;
+ color: var(--color-text-tertiary);
+ margin-bottom: var(--space-4);
+}
+
+.name {
+ font-size: var(--text-5xl);
+ font-weight: var(--weight-bold);
+ letter-spacing: var(--tracking-tight);
+ line-height: var(--leading-tight);
+ color: var(--color-text-primary);
+ margin-bottom: var(--space-2);
+
+ @media (max-width: 767px) {
+ font-size: var(--text-4xl);
+ }
+}
+
+.title {
+ font-size: var(--text-lg);
+ color: var(--color-text-secondary);
+}
+
+.bio {
+ max-width: var(--max-width-content);
+ margin-bottom: var(--space-section);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-6);
+}
+
+.bio p {
+ font-size: var(--text-lg);
+ line-height: var(--leading-relaxed);
+ color: var(--color-text-secondary);
+}
diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx
new file mode 100644
index 0000000..c48c4c8
--- /dev/null
+++ b/src/app/about/page.tsx
@@ -0,0 +1,51 @@
+import type { Metadata } from "next";
+import Philosophy from "@/components/about/Philosophy";
+import SkillsGrid from "@/components/about/SkillsGrid";
+import styles from "./page.module.css";
+
+export const metadata: Metadata = {
+ title: "About",
+ description:
+ "Design systems designer and developer based in San Francisco. Five years building the substrate — tokens, components, governance.",
+};
+
+export default function AboutPage() {
+ return (
+
+
+
+
+
+
+ I started as a product designer at a ten-person startup, building features and shipping screens.
+ It was good work, but I kept finding myself more interested in the substrate than the surface —
+ the token system, the component API, the documentation that lets a team of thirty designers
+ move as one. Five years later, that's my whole job.
+
+
+ I've built design systems from zero, inherited ones at scale, and migrated legacy products
+ through complete token overhauls. The common thread: the hardest parts are never the design
+ decisions. They're the organizational ones.
+
+
+ My view is that the best design systems are 30% design, 30% engineering, and 40%
+ organizational change management. I care about all three equally, which puts me in
+ an unusual and useful position between disciplines.
+
+
+ Based in San Francisco. Available for senior individual contributor or lead roles
+ focused on design systems. I write occasionally about tokens, governance, and the
+ organizational dynamics of cross-functional systems work.
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/favicon.ico b/src/app/favicon.ico
new file mode 100644
index 0000000..718d6fe
Binary files /dev/null and b/src/app/favicon.ico differ
diff --git a/src/app/globals.css b/src/app/globals.css
new file mode 100644
index 0000000..a509103
--- /dev/null
+++ b/src/app/globals.css
@@ -0,0 +1,268 @@
+/* ─────────────────────────────────────────────────────────
+ LAYER 1: Primitive Tokens
+ Raw values — never used directly in components.
+ Only referenced by semantic tokens.
+───────────────────────────────────────────────────────── */
+:root {
+ /* Grays — slightly warm cast */
+ --color-gray-0: #ffffff;
+ --color-gray-50: #f8f7f4;
+ --color-gray-100: #eeede9;
+ --color-gray-200: #dddcd8;
+ --color-gray-300: #c2c1bd;
+ --color-gray-400: #9b9a96;
+ --color-gray-500: #737270;
+ --color-gray-600: #545350;
+ --color-gray-700: #3a3937;
+ --color-gray-800: #272623;
+ --color-gray-900: #1a1916;
+ --color-gray-950: #0d0d0b;
+
+ /* Lime — the accent */
+ --color-lime-50: #f7fee7;
+ --color-lime-100: #ecfccb;
+ --color-lime-300: #bef264;
+ --color-lime-400: #a3e635;
+ --color-lime-500: #84cc16;
+ --color-lime-600: #65a30d;
+ --color-lime-700: #4d7c0f;
+ --color-lime-900: #1a2e05;
+
+ /* Blue (kept for info states) */
+ --color-blue-100: #dbeafe;
+ --color-blue-300: #93c5fd;
+ --color-blue-500: #3b82f6;
+ --color-blue-600: #2563eb;
+ --color-blue-700: #1d4ed8;
+ --color-blue-900: #1e3a8a;
+
+ /* Green (kept for success states) */
+ --color-green-100: #dcfce7;
+ --color-green-500: #22c55e;
+ --color-green-600: #16a34a;
+ --color-green-900: #14532d;
+
+ /* Amber (kept for warning states) */
+ --color-amber-100: #fef3c7;
+ --color-amber-500: #f59e0b;
+ --color-amber-600: #d97706;
+ --color-amber-900: #78350f;
+}
+
+/* ─────────────────────────────────────────────────────────
+ LAYER 2: Semantic Tokens — Light Mode (default)
+───────────────────────────────────────────────────────── */
+:root {
+ /* Backgrounds */
+ --color-bg-base: var(--color-gray-50);
+ --color-bg-subtle: var(--color-gray-100);
+ --color-bg-muted: var(--color-gray-200);
+ --color-bg-inverse: var(--color-gray-950);
+
+ /* Borders */
+ --color-border-default: var(--color-gray-200);
+ --color-border-strong: var(--color-gray-300);
+ --color-border-subtle: var(--color-gray-100);
+
+ /* Text */
+ --color-text-primary: var(--color-gray-900);
+ --color-text-secondary: var(--color-gray-600);
+ --color-text-tertiary: var(--color-gray-400);
+ --color-text-disabled: var(--color-gray-300);
+ --color-text-inverse: var(--color-gray-50);
+ --color-text-on-accent: var(--color-gray-0);
+
+ /* Accent — lime */
+ --color-accent-default: var(--color-lime-600);
+ --color-accent-hover: var(--color-lime-700);
+ --color-accent-subtle: var(--color-lime-100);
+ --color-accent-blue: var(--color-blue-600);
+ --color-accent-green: var(--color-green-600);
+ --color-accent-amber: var(--color-amber-600);
+
+ /* Focus */
+ --color-focus-ring: var(--color-lime-500);
+}
+
+/* ─────────────────────────────────────────────────────────
+ LAYER 2 (cont.): Semantic Tokens — Dark Mode Override
+───────────────────────────────────────────────────────── */
+[data-theme="dark"] {
+ /* Backgrounds */
+ --color-bg-base: var(--color-gray-950);
+ --color-bg-subtle: var(--color-gray-900);
+ --color-bg-muted: var(--color-gray-800);
+ --color-bg-inverse: var(--color-gray-50);
+
+ /* Borders */
+ --color-border-default: var(--color-gray-800);
+ --color-border-strong: var(--color-gray-700);
+ --color-border-subtle: var(--color-gray-900);
+
+ /* Text */
+ --color-text-primary: var(--color-gray-50);
+ --color-text-secondary: var(--color-gray-400);
+ --color-text-tertiary: var(--color-gray-600);
+ --color-text-disabled: var(--color-gray-700);
+ --color-text-inverse: var(--color-gray-950);
+ --color-text-on-accent: var(--color-gray-950);
+
+ /* Accent — bright lime on dark */
+ --color-accent-default: var(--color-lime-400);
+ --color-accent-hover: var(--color-lime-300);
+ --color-accent-subtle: var(--color-lime-900);
+ --color-accent-blue: var(--color-blue-500);
+ --color-accent-green: var(--color-green-500);
+ --color-accent-amber: var(--color-amber-500);
+
+ /* Focus */
+ --color-focus-ring: var(--color-lime-400);
+}
+
+/* ─────────────────────────────────────────────────────────
+ LAYER 2 (cont.): Typography Tokens
+───────────────────────────────────────────────────────── */
+:root {
+ /* Font families — CSS variables injected by next/font */
+ --font-body: var(--font-inter), system-ui, -apple-system, sans-serif;
+ --font-mono: var(--font-jetbrains-mono), "Courier New", monospace;
+
+ /* Display — fluid, hero-scale */
+ --text-display: clamp(3.5rem, 10vw, 8rem);
+
+ /* Type scale */
+ --text-xs: 0.75rem; /* 12px */
+ --text-sm: 0.875rem; /* 14px */
+ --text-base: 1rem; /* 16px */
+ --text-md: 1.125rem; /* 18px */
+ --text-lg: 1.25rem; /* 20px */
+ --text-xl: 1.5rem; /* 24px */
+ --text-2xl: 1.875rem; /* 30px */
+ --text-3xl: 2.25rem; /* 36px */
+ --text-4xl: 3rem; /* 48px */
+ --text-5xl: 3.75rem; /* 60px */
+
+ /* Line heights */
+ --leading-none: 1;
+ --leading-tight: 1.1;
+ --leading-snug: 1.3;
+ --leading-normal: 1.5;
+ --leading-relaxed: 1.65;
+
+ /* Letter spacing */
+ --tracking-display: -0.04em;
+ --tracking-tight: -0.03em;
+ --tracking-snug: -0.015em;
+ --tracking-normal: 0em;
+ --tracking-wide: 0.04em;
+ --tracking-widest: 0.1em;
+
+ /* Font weights */
+ --weight-regular: 400;
+ --weight-medium: 500;
+ --weight-semibold: 600;
+ --weight-bold: 700;
+}
+
+/* ─────────────────────────────────────────────────────────
+ LAYER 2 (cont.): Spacing Tokens — Base-4 Scale
+───────────────────────────────────────────────────────── */
+:root {
+ --space-0: 0;
+ --space-1: 0.25rem; /* 4px */
+ --space-2: 0.5rem; /* 8px */
+ --space-3: 0.75rem; /* 12px */
+ --space-4: 1rem; /* 16px */
+ --space-5: 1.25rem; /* 20px */
+ --space-6: 1.5rem; /* 24px */
+ --space-8: 2rem; /* 32px */
+ --space-10: 2.5rem; /* 40px */
+ --space-12: 3rem; /* 48px */
+ --space-16: 4rem; /* 64px */
+ --space-20: 5rem; /* 80px */
+ --space-24: 6rem; /* 96px */
+ --space-32: 8rem; /* 128px */
+ --space-40: 10rem; /* 160px */
+ --space-48: 12rem; /* 192px */
+
+ /* Named semantic spacing aliases */
+ --space-section: var(--space-24);
+ --space-section-sm: var(--space-16);
+ --space-gutter: var(--space-6);
+ --space-gutter-lg: var(--space-16);
+ --space-stack-sm: var(--space-3);
+ --space-stack-md: var(--space-6);
+ --space-stack-lg: var(--space-10);
+}
+
+/* ─────────────────────────────────────────────────────────
+ LAYER 2 (cont.): Layout Tokens
+───────────────────────────────────────────────────────── */
+:root {
+ --max-width-content: 680px;
+ --max-width-wide: 960px;
+ --max-width-full: 1280px;
+
+ --radius-sm: 3px;
+ --radius-md: 6px;
+ --radius-lg: 12px;
+ --radius-pill: 9999px;
+
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
+ --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.10);
+ --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.14);
+
+ --transition-base: 150ms ease;
+ --transition-slow: 300ms ease;
+}
+
+/* ─────────────────────────────────────────────────────────
+ Base Reset
+───────────────────────────────────────────────────────── */
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html {
+ font-size: 16px;
+ -webkit-text-size-adjust: 100%;
+}
+
+body {
+ background-color: var(--color-bg-base);
+ color: var(--color-text-primary);
+ font-family: var(--font-body);
+ font-size: var(--text-base);
+ line-height: var(--leading-normal);
+ transition: background-color var(--transition-slow), color var(--transition-slow);
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ overflow-x: hidden;
+}
+
+img,
+video {
+ display: block;
+ max-width: 100%;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+button {
+ background: none;
+ border: none;
+ cursor: pointer;
+ font: inherit;
+}
+
+ul,
+ol {
+ list-style: none;
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 0000000..d2741d8
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,48 @@
+import type { Metadata } from "next";
+import "./globals.css";
+import { inter, jetbrainsMono } from "@/lib/fonts";
+import Nav from "@/components/layout/Nav";
+import Footer from "@/components/layout/Footer";
+
+export const metadata: Metadata = {
+ title: {
+ template: "%s — Elara Reyes",
+ default: "Elara Reyes — Design Systems Designer",
+ },
+ description:
+ "Design systems designer and developer. I build the foundations that let teams ship consistent, accessible products at scale.",
+};
+
+const themeScript = `
+(function() {
+ try {
+ var stored = localStorage.getItem('theme');
+ var preferred = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+ var theme = stored || preferred;
+ document.documentElement.setAttribute('data-theme', theme);
+ } catch (e) {}
+})();
+`;
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
+
+
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
new file mode 100644
index 0000000..f129262
--- /dev/null
+++ b/src/app/page.tsx
@@ -0,0 +1,17 @@
+import Hero from "@/components/home/Hero";
+import WorkGrid from "@/components/home/WorkGrid";
+import AboutTeaser from "@/components/home/AboutTeaser";
+import SkillsStrip from "@/components/home/SkillsStrip";
+import { getCaseStudyList } from "@/lib/case-studies";
+
+export default function HomePage() {
+ const caseStudies = getCaseStudyList();
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/playground/page.module.css b/src/app/playground/page.module.css
new file mode 100644
index 0000000..24176d6
--- /dev/null
+++ b/src/app/playground/page.module.css
@@ -0,0 +1,88 @@
+.main {
+ padding: var(--space-section) var(--space-gutter);
+
+ @media (min-width: 768px) {
+ padding: var(--space-section) var(--space-gutter-lg);
+ }
+}
+
+.inner {
+ max-width: var(--max-width-wide);
+ margin: 0 auto;
+}
+
+.header {
+ max-width: var(--max-width-content);
+ margin-bottom: var(--space-section-sm);
+}
+
+.label {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-medium);
+ letter-spacing: var(--tracking-widest);
+ text-transform: uppercase;
+ color: var(--color-text-tertiary);
+ margin-bottom: var(--space-4);
+}
+
+.heading {
+ font-size: var(--text-4xl);
+ font-weight: var(--weight-bold);
+ letter-spacing: var(--tracking-tight);
+ line-height: var(--leading-tight);
+ color: var(--color-text-primary);
+ margin-bottom: var(--space-6);
+
+ @media (max-width: 767px) {
+ font-size: var(--text-3xl);
+ }
+}
+
+.description {
+ font-size: var(--text-lg);
+ line-height: var(--leading-relaxed);
+ color: var(--color-text-secondary);
+}
+
+.section {
+ margin-bottom: var(--space-4);
+}
+
+.sectionHeading {
+ font-size: var(--text-xl);
+ font-weight: var(--weight-semibold);
+ letter-spacing: var(--tracking-snug);
+ color: var(--color-text-primary);
+ margin-bottom: var(--space-3);
+}
+
+.sectionNote {
+ font-size: var(--text-sm);
+ color: var(--color-text-tertiary);
+ margin-bottom: var(--space-8);
+ font-family: var(--font-body);
+}
+
+.sectionNote code {
+ font-family: var(--font-mono);
+ font-size: 0.9em;
+ background-color: var(--color-bg-muted);
+ padding: 0.1em 0.35em;
+ border-radius: var(--radius-sm);
+ color: var(--color-accent-default);
+}
+
+.swatchGrid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: var(--space-6);
+
+ @media (min-width: 480px) {
+ grid-template-columns: repeat(3, 1fr);
+ }
+
+ @media (min-width: 768px) {
+ grid-template-columns: repeat(5, 1fr);
+ }
+}
diff --git a/src/app/playground/page.tsx b/src/app/playground/page.tsx
new file mode 100644
index 0000000..814fbb1
--- /dev/null
+++ b/src/app/playground/page.tsx
@@ -0,0 +1,89 @@
+import type { Metadata } from "next";
+import TokenSwatch from "@/components/playground/TokenSwatch";
+import TypeScale from "@/components/playground/TypeScale";
+import SpacingScale from "@/components/playground/SpacingScale";
+import ComponentShowcase from "@/components/playground/ComponentShowcase";
+import Divider from "@/components/ui/Divider";
+import styles from "./page.module.css";
+
+export const metadata: Metadata = {
+ title: "Playground",
+ description:
+ "A live showcase of the token system and components powering this portfolio. Inspect DevTools to see the full CSS custom property architecture.",
+};
+
+const colorTokens = [
+ { tokenName: "--color-bg-base", label: "Background Base", hex: "#ffffff", group: "bg" as const },
+ { tokenName: "--color-bg-subtle", label: "Background Subtle", hex: "#f9f9f9", group: "bg" as const },
+ { tokenName: "--color-bg-muted", label: "Background Muted", hex: "#f0f0f0", group: "bg" as const },
+ { tokenName: "--color-text-primary", label: "Text Primary", hex: "#1a1a1a", group: "text" as const },
+ { tokenName: "--color-text-secondary",label: "Text Secondary", hex: "#525252", group: "text" as const },
+ { tokenName: "--color-text-tertiary", label: "Text Tertiary", hex: "#a8a8a8", group: "text" as const },
+ { tokenName: "--color-border-default",label: "Border Default", hex: "#e4e4e4", group: "border" as const },
+ { tokenName: "--color-accent-default",label: "Accent Default", hex: "#2563eb", group: "accent" as const },
+ { tokenName: "--color-accent-green", label: "Accent Green", hex: "#16a34a", group: "accent" as const },
+ { tokenName: "--color-accent-amber", label: "Accent Amber", hex: "#d97706", group: "accent" as const },
+];
+
+export default function PlaygroundPage() {
+ return (
+
+
+
+
+
+ Semantic Color Tokens
+
+ Light mode values shown. Semantic tokens resolve to different primitives in dark mode.
+
+
+ {colorTokens.map((t) => (
+
+ ))}
+
+
+
+
+
+
+ Type Scale
+
+ Major Third ratio (1.25×) from a 16px base. Nine steps, all defined as CSS custom properties.
+
+
+
+
+
+
+
+ Spacing Scale
+
+ Base-4 system (4px increments). Named semantic aliases — --space-section,{" "}
+ --space-gutter — are what components actually use.
+
+
+
+
+
+
+
+ Components
+
+ Each component defines its own component-level tokens that reference semantic tokens.
+ No component ever reaches for a primitive value directly.
+
+
+
+
+
+ );
+}
diff --git a/src/app/work/[slug]/page.tsx b/src/app/work/[slug]/page.tsx
new file mode 100644
index 0000000..4a083c4
--- /dev/null
+++ b/src/app/work/[slug]/page.tsx
@@ -0,0 +1,47 @@
+import { notFound } from "next/navigation";
+import type { Metadata } from "next";
+import { getAllSlugs, getMdxContent, getFrontmatter } from "@/lib/mdx";
+import CaseStudyHeader from "@/components/work/CaseStudyHeader";
+import ProseWrapper from "@/components/ui/ProseWrapper";
+
+export async function generateStaticParams() {
+ return getAllSlugs().map((slug) => ({ slug }));
+}
+
+export async function generateMetadata({
+ params,
+}: {
+ params: Promise<{ slug: string }>;
+}): Promise {
+ const { slug } = await params;
+ try {
+ const frontmatter = getFrontmatter(slug);
+ return { title: frontmatter.title, description: frontmatter.tagline };
+ } catch {
+ return {};
+ }
+}
+
+export default async function CaseStudyPage({
+ params,
+}: {
+ params: Promise<{ slug: string }>;
+}) {
+ const { slug } = await params;
+
+ try {
+ const { frontmatter, Content } = await getMdxContent(slug);
+ return (
+
+
+
+
+
+
+ );
+ } catch (err) {
+ const message = err instanceof Error ? err.message : String(err);
+ if (message.includes("ENOENT")) notFound();
+ throw err;
+ }
+}
diff --git a/src/app/work/page.module.css b/src/app/work/page.module.css
new file mode 100644
index 0000000..4e1fb42
--- /dev/null
+++ b/src/app/work/page.module.css
@@ -0,0 +1,46 @@
+.main {
+ padding: var(--space-section) var(--space-gutter);
+
+ @media (min-width: 768px) {
+ padding: var(--space-section) var(--space-gutter-lg);
+ }
+}
+
+.header {
+ max-width: var(--max-width-content);
+ margin: 0 auto var(--space-section-sm);
+ padding: 0 var(--space-gutter);
+
+ @media (min-width: 768px) {
+ padding: 0 var(--space-gutter-lg);
+ }
+}
+
+.label {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-medium);
+ letter-spacing: var(--tracking-widest);
+ text-transform: uppercase;
+ color: var(--color-text-tertiary);
+ margin-bottom: var(--space-4);
+}
+
+.heading {
+ font-size: var(--text-4xl);
+ font-weight: var(--weight-bold);
+ letter-spacing: var(--tracking-tight);
+ line-height: var(--leading-tight);
+ color: var(--color-text-primary);
+ margin-bottom: var(--space-4);
+
+ @media (max-width: 767px) {
+ font-size: var(--text-3xl);
+ }
+}
+
+.sub {
+ font-size: var(--text-lg);
+ line-height: var(--leading-relaxed);
+ color: var(--color-text-secondary);
+}
diff --git a/src/app/work/page.tsx b/src/app/work/page.tsx
new file mode 100644
index 0000000..5cadbdc
--- /dev/null
+++ b/src/app/work/page.tsx
@@ -0,0 +1,26 @@
+import type { Metadata } from "next";
+import WorkGrid from "@/components/home/WorkGrid";
+import { getCaseStudyList } from "@/lib/case-studies";
+import styles from "./page.module.css";
+
+export const metadata: Metadata = {
+ title: "Work",
+ description: "Selected design systems case studies — tokens, governance, migration, and component architecture.",
+};
+
+export default function WorkPage() {
+ const caseStudies = getCaseStudyList();
+ return (
+
+
+
Work
+
Selected Case Studies
+
+ Design systems work across fintech, SaaS, and enterprise —
+ covering foundations, governance, and token migration.
+
+
+
+
+ );
+}
diff --git a/src/components/about/Philosophy.module.css b/src/components/about/Philosophy.module.css
new file mode 100644
index 0000000..32d65e9
--- /dev/null
+++ b/src/components/about/Philosophy.module.css
@@ -0,0 +1,42 @@
+.section {
+ padding-top: var(--space-section);
+ border-top: 1px solid var(--color-border-subtle);
+}
+
+.label {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-medium);
+ letter-spacing: var(--tracking-widest);
+ text-transform: uppercase;
+ color: var(--color-text-tertiary);
+ margin-bottom: var(--space-10);
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: var(--space-10);
+
+ @media (min-width: 640px) {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
+
+.principle {
+ padding-top: var(--space-6);
+ border-top: 1px solid var(--color-border-subtle);
+}
+
+.title {
+ font-size: var(--text-base);
+ font-weight: var(--weight-semibold);
+ color: var(--color-text-primary);
+ margin-bottom: var(--space-3);
+}
+
+.body {
+ font-size: var(--text-sm);
+ line-height: var(--leading-relaxed);
+ color: var(--color-text-secondary);
+}
diff --git a/src/components/about/Philosophy.tsx b/src/components/about/Philosophy.tsx
new file mode 100644
index 0000000..1199807
--- /dev/null
+++ b/src/components/about/Philosophy.tsx
@@ -0,0 +1,36 @@
+import styles from "./Philosophy.module.css";
+
+const principles = [
+ {
+ title: "Tokens are contracts",
+ body: "Before a single component is built, design and engineering must agree on what 'primary text' means, what 'interactive background' means, and what 'danger' means. Token naming is not a CSS concern — it's a shared vocabulary. A token system that designers don't understand is just a variable file. A token system that engineers don't contribute to is just Figma styles.",
+ },
+ {
+ title: "Governance is a product",
+ body: "A design system without a contribution model is just a library. Libraries go stale. Products evolve. The governance model — how things get proposed, reviewed, merged, and deprecated — determines whether the system stays alive after V1. I spend as much time designing the process as I do designing the components.",
+ },
+ {
+ title: "Consistency enables creativity",
+ body: "A common objection to design systems is that they constrain creativity. The opposite is true. When a team doesn't have to re-solve 'what color is this button' or 'how wide is this modal', they have more attention for the hard problems — the ones that actually require creative judgment. Constraints that are understood create freedom.",
+ },
+ {
+ title: "Documentation is the product",
+ body: "The component is what gets shipped. The documentation is what gets used. A well-built component with no guidance on when to use it, when not to use it, and what accessibility considerations apply is still a failure. I treat documentation as a first-class deliverable, not an afterthought. The usage guide gets written before the PR is opened.",
+ },
+];
+
+export default function Philosophy() {
+ return (
+
+ Philosophy
+
+ {principles.map((p) => (
+
+ ))}
+
+
+ );
+}
diff --git a/src/components/about/SkillsGrid.module.css b/src/components/about/SkillsGrid.module.css
new file mode 100644
index 0000000..7c52cdb
--- /dev/null
+++ b/src/components/about/SkillsGrid.module.css
@@ -0,0 +1,48 @@
+.section {
+ padding-top: var(--space-section);
+ border-top: 1px solid var(--color-border-subtle);
+}
+
+.label {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-medium);
+ letter-spacing: var(--tracking-widest);
+ text-transform: uppercase;
+ color: var(--color-text-tertiary);
+ margin-bottom: var(--space-10);
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: var(--space-10);
+
+ @media (min-width: 768px) {
+ grid-template-columns: repeat(4, 1fr);
+ }
+}
+
+.category {}
+
+.catName {
+ font-size: var(--text-xs);
+ font-family: var(--font-mono);
+ font-weight: var(--weight-semibold);
+ letter-spacing: var(--tracking-wide);
+ text-transform: uppercase;
+ color: var(--color-text-tertiary);
+ margin-bottom: var(--space-4);
+}
+
+.list {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+
+.skill {
+ font-size: var(--text-sm);
+ color: var(--color-text-secondary);
+ line-height: var(--leading-normal);
+}
diff --git a/src/components/about/SkillsGrid.tsx b/src/components/about/SkillsGrid.tsx
new file mode 100644
index 0000000..15666c0
--- /dev/null
+++ b/src/components/about/SkillsGrid.tsx
@@ -0,0 +1,68 @@
+import styles from "./SkillsGrid.module.css";
+
+const categories = [
+ {
+ name: "Design Systems",
+ skills: [
+ "Token Architecture",
+ "Component API Design",
+ "Figma Variables",
+ "Design–Dev Handoff",
+ "Storybook",
+ "Style Dictionary",
+ ],
+ },
+ {
+ name: "Governance & Process",
+ skills: [
+ "RFC Processes",
+ "Contribution Models",
+ "Semantic Versioning",
+ "Audit Methodology",
+ "Roadmapping",
+ "Stakeholder Alignment",
+ ],
+ },
+ {
+ name: "Code",
+ skills: [
+ "React",
+ "TypeScript",
+ "CSS Custom Properties",
+ "CSS Modules",
+ "HTML / Accessibility",
+ "Git",
+ ],
+ },
+ {
+ name: "Documentation",
+ skills: [
+ "MDX / Notion",
+ "Usage Guidelines",
+ "Decision Logs",
+ "Workshop Facilitation",
+ "Migration Guides",
+ "Deprecation Strategy",
+ ],
+ },
+];
+
+export default function SkillsGrid() {
+ return (
+
+ Skills
+
+ {categories.map((cat) => (
+
+
{cat.name}
+
+ {cat.skills.map((skill) => (
+ {skill}
+ ))}
+
+
+ ))}
+
+
+ );
+}
diff --git a/src/components/home/AboutTeaser.module.css b/src/components/home/AboutTeaser.module.css
new file mode 100644
index 0000000..6f43dc7
--- /dev/null
+++ b/src/components/home/AboutTeaser.module.css
@@ -0,0 +1,87 @@
+.section {
+ padding: var(--space-section) var(--space-gutter);
+ border-top: 1px solid var(--color-border-default);
+
+ @media (min-width: 768px) {
+ padding: var(--space-section) var(--space-gutter-lg);
+ }
+}
+
+.inner {
+ max-width: var(--max-width-full);
+ margin: 0 auto;
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: var(--space-12);
+
+ @media (min-width: 768px) {
+ grid-template-columns: 1fr 280px;
+ gap: var(--space-16);
+ align-items: start;
+ }
+}
+
+.content {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-6);
+}
+
+.text {
+ font-size: var(--text-lg);
+ line-height: var(--leading-relaxed);
+ color: var(--color-text-secondary);
+ max-width: 560px;
+}
+
+.link {
+ display: inline-block;
+ font-size: var(--text-sm);
+ font-weight: var(--weight-medium);
+ color: var(--color-text-tertiary);
+ transition: color var(--transition-base);
+}
+
+.link:hover {
+ color: var(--color-text-primary);
+}
+
+.link:focus-visible {
+ outline: 2px solid var(--color-focus-ring);
+ outline-offset: 2px;
+ border-radius: var(--radius-sm);
+}
+
+.stats {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-8);
+
+ @media (max-width: 767px) {
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: var(--space-8) var(--space-12);
+ }
+}
+
+.stat {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-1);
+}
+
+.statValue {
+ font-size: var(--text-4xl);
+ font-weight: var(--weight-bold);
+ letter-spacing: var(--tracking-tight);
+ line-height: var(--leading-tight);
+ color: var(--color-text-primary);
+}
+
+.statLabel {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-text-tertiary);
+ letter-spacing: var(--tracking-wide);
+ text-transform: uppercase;
+}
diff --git a/src/components/home/AboutTeaser.tsx b/src/components/home/AboutTeaser.tsx
new file mode 100644
index 0000000..6fde31a
--- /dev/null
+++ b/src/components/home/AboutTeaser.tsx
@@ -0,0 +1,40 @@
+import Link from "next/link";
+import styles from "./AboutTeaser.module.css";
+
+const stats = [
+ { value: "5 yrs", label: "design systems experience" },
+ { value: "3", label: "systems shipped end-to-end" },
+ { value: "47+", label: "production components" },
+];
+
+export default function AboutTeaser() {
+ return (
+
+
+
+
+ I started as a product designer, but kept finding myself more
+ interested in the substrate than the features — the token system,
+ the component API, the documentation that lets a team of 30
+ designers move as one.
+
+
+ The best design systems are 30% design, 30% engineering, and 40%
+ organizational change management. I care about all three.
+
+
+ More about me →
+
+
+
+ {stats.map((s) => (
+
+ {s.value}
+ {s.label}
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/home/CaseStudyCard.module.css b/src/components/home/CaseStudyCard.module.css
new file mode 100644
index 0000000..12c0888
--- /dev/null
+++ b/src/components/home/CaseStudyCard.module.css
@@ -0,0 +1,143 @@
+.item {
+ position: relative;
+ border-bottom: 1px solid var(--color-border-default);
+}
+
+/* Accent line reveal on hover */
+.item::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: var(--space-6);
+ bottom: var(--space-6);
+ width: 2px;
+ background-color: var(--accent-color, var(--color-accent-default));
+ transform: scaleY(0);
+ transform-origin: top center;
+ transition: transform 250ms cubic-bezier(0.16, 1, 0.3, 1);
+}
+
+.item:has(.link:hover)::before {
+ transform: scaleY(1);
+}
+
+.link {
+ display: grid;
+ grid-template-columns: 3rem 1fr auto;
+ gap: var(--space-8);
+ align-items: start;
+ padding: var(--space-8) 0 var(--space-8) var(--space-5);
+ color: inherit;
+
+ @media (max-width: 639px) {
+ grid-template-columns: 2.5rem 1fr;
+ gap: var(--space-4);
+ padding-left: var(--space-3);
+ }
+}
+
+.link:focus-visible {
+ outline: 2px solid var(--color-focus-ring);
+ outline-offset: 4px;
+ border-radius: var(--radius-sm);
+}
+
+.number {
+ font-family: var(--font-mono);
+ font-size: var(--text-sm);
+ font-weight: var(--weight-medium);
+ color: var(--color-text-disabled);
+ letter-spacing: var(--tracking-wide);
+ padding-top: 0.15em;
+ user-select: none;
+ transition: color var(--transition-base);
+}
+
+.item:has(.link:hover) .number {
+ color: var(--accent-color, var(--color-accent-default));
+}
+
+.body {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+
+.title {
+ font-size: var(--text-2xl);
+ font-weight: var(--weight-semibold);
+ letter-spacing: var(--tracking-tight);
+ line-height: var(--leading-snug);
+ color: var(--color-text-primary);
+ transition: color var(--transition-base);
+
+ @media (max-width: 639px) {
+ font-size: var(--text-xl);
+ }
+}
+
+.link:hover .title {
+ color: var(--accent-color, var(--color-accent-default));
+}
+
+.tagline {
+ font-size: var(--text-base);
+ line-height: var(--leading-relaxed);
+ color: var(--color-text-secondary);
+}
+
+.tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-2);
+ margin-top: var(--space-1);
+}
+
+.tag {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-text-tertiary);
+ letter-spacing: var(--tracking-wide);
+}
+
+.tag + .tag::before {
+ content: "·";
+ margin-right: var(--space-2);
+}
+
+.aside {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: var(--space-2);
+ padding-top: 0.1em;
+ min-width: 120px;
+
+ @media (max-width: 639px) {
+ display: none;
+ }
+}
+
+.year {
+ font-family: var(--font-mono);
+ font-size: var(--text-sm);
+ color: var(--color-text-tertiary);
+}
+
+.role {
+ font-size: var(--text-xs);
+ color: var(--color-text-disabled);
+ text-align: right;
+}
+
+.arrow {
+ font-size: var(--text-lg);
+ color: var(--color-text-tertiary);
+ margin-top: auto;
+ transition: transform var(--transition-base), color var(--transition-base);
+}
+
+.link:hover .arrow {
+ transform: translateX(5px);
+ color: var(--accent-color, var(--color-accent-default));
+}
diff --git a/src/components/home/CaseStudyCard.tsx b/src/components/home/CaseStudyCard.tsx
new file mode 100644
index 0000000..098aad3
--- /dev/null
+++ b/src/components/home/CaseStudyCard.tsx
@@ -0,0 +1,45 @@
+import Link from "next/link";
+import type { CaseStudy } from "@/types/case-study";
+import styles from "./CaseStudyCard.module.css";
+
+interface CaseStudyCardProps extends CaseStudy {
+ index: number;
+}
+
+export default function CaseStudyCard({
+ slug,
+ title,
+ tagline,
+ tags,
+ year,
+ role,
+ coverAccent,
+ index,
+}: CaseStudyCardProps) {
+ return (
+
+
+
+ {String(index).padStart(2, "0")}
+
+
+
{title}
+
{tagline}
+
+ {tags.slice(0, 3).map((tag) => (
+ {tag}
+ ))}
+
+
+
+ {year}
+ {role}
+ →
+
+
+
+ );
+}
diff --git a/src/components/home/Hero.module.css b/src/components/home/Hero.module.css
new file mode 100644
index 0000000..4b270ce
--- /dev/null
+++ b/src/components/home/Hero.module.css
@@ -0,0 +1,101 @@
+.hero {
+ padding: var(--space-20) var(--space-gutter) var(--space-section);
+
+ @media (min-width: 768px) {
+ padding: var(--space-32) var(--space-gutter-lg) var(--space-section);
+ }
+}
+
+.inner {
+ max-width: var(--max-width-full);
+ margin: 0 auto;
+}
+
+.label {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-medium);
+ letter-spacing: var(--tracking-widest);
+ text-transform: uppercase;
+ color: var(--color-text-tertiary);
+ margin-bottom: var(--space-6);
+}
+
+.name {
+ display: flex;
+ flex-direction: column;
+ font-size: var(--text-display);
+ letter-spacing: var(--tracking-display);
+ line-height: var(--leading-none);
+ border-top: 1px solid var(--color-border-default);
+ padding-top: var(--space-6);
+ margin-bottom: var(--space-12);
+}
+
+.firstName {
+ font-weight: var(--weight-regular);
+ color: var(--color-text-secondary);
+}
+
+.lastName {
+ font-weight: var(--weight-bold);
+ color: var(--color-text-primary);
+}
+
+.period {
+ color: var(--color-accent-default);
+}
+
+.tagline {
+ font-size: var(--text-xl);
+ line-height: var(--leading-relaxed);
+ color: var(--color-text-secondary);
+ max-width: 560px;
+ margin-bottom: var(--space-10);
+}
+
+.meta {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--space-3);
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-text-tertiary);
+ letter-spacing: var(--tracking-wide);
+}
+
+.sep {
+ color: var(--color-border-strong);
+}
+
+.available {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ color: var(--color-accent-default);
+}
+
+.dot {
+ display: inline-block;
+ width: 6px;
+ height: 6px;
+ border-radius: var(--radius-pill);
+ background-color: var(--color-accent-default);
+ flex-shrink: 0;
+}
+
+.cta {
+ color: var(--color-text-secondary);
+ transition: color var(--transition-base);
+}
+
+.cta:hover {
+ color: var(--color-text-primary);
+}
+
+.cta:focus-visible {
+ outline: 2px solid var(--color-focus-ring);
+ outline-offset: 2px;
+ border-radius: var(--radius-sm);
+}
diff --git a/src/components/home/Hero.tsx b/src/components/home/Hero.tsx
new file mode 100644
index 0000000..ef10db9
--- /dev/null
+++ b/src/components/home/Hero.tsx
@@ -0,0 +1,34 @@
+import Link from "next/link";
+import styles from "./Hero.module.css";
+
+export default function Hero() {
+ return (
+
+
+
Design Systems Designer & Developer
+
+ Elara
+
+ Reyes.
+
+
+
+ Five years building the substrate — tokens, components,
+ documentation, governance. The work that makes all other work possible.
+
+
+ San Francisco
+ ·
+
+
+ Available for new roles
+
+ ·
+
+ View work →
+
+
+
+
+ );
+}
diff --git a/src/components/home/SkillsStrip.module.css b/src/components/home/SkillsStrip.module.css
new file mode 100644
index 0000000..1e0d1ef
--- /dev/null
+++ b/src/components/home/SkillsStrip.module.css
@@ -0,0 +1,59 @@
+.section {
+ padding: var(--space-section) var(--space-gutter);
+ border-top: 1px solid var(--color-border-default);
+
+ @media (min-width: 768px) {
+ padding: var(--space-section) var(--space-gutter-lg);
+ }
+}
+
+.inner {
+ max-width: var(--max-width-full);
+ margin: 0 auto;
+}
+
+.label {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-medium);
+ letter-spacing: var(--tracking-widest);
+ text-transform: uppercase;
+ color: var(--color-text-tertiary);
+ margin-bottom: var(--space-8);
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: var(--space-10);
+
+ @media (min-width: 640px) {
+ grid-template-columns: repeat(3, 1fr);
+ gap: var(--space-8);
+ }
+}
+
+.group {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-4);
+}
+
+.category {
+ font-size: var(--text-sm);
+ font-weight: var(--weight-semibold);
+ color: var(--color-text-primary);
+ letter-spacing: var(--tracking-snug);
+}
+
+.list {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+
+.item {
+ font-size: var(--text-sm);
+ color: var(--color-text-secondary);
+ line-height: var(--leading-normal);
+}
diff --git a/src/components/home/SkillsStrip.tsx b/src/components/home/SkillsStrip.tsx
new file mode 100644
index 0000000..970c73b
--- /dev/null
+++ b/src/components/home/SkillsStrip.tsx
@@ -0,0 +1,38 @@
+import styles from "./SkillsStrip.module.css";
+
+const skillGroups = [
+ {
+ category: "Systems Design",
+ items: ["Token Architecture", "Component API Design", "Semantic Versioning", "Audit Methodology"],
+ },
+ {
+ category: "Tooling & Code",
+ items: ["React", "TypeScript", "CSS Custom Properties", "Storybook", "Style Dictionary"],
+ },
+ {
+ category: "Process & Governance",
+ items: ["Figma Variables", "Contribution Models", "RFC Triage", "Design–Dev Handoff", "Accessibility"],
+ },
+];
+
+export default function SkillsStrip() {
+ return (
+
+
+
Capabilities
+
+ {skillGroups.map((group) => (
+
+
{group.category}
+
+ {group.items.map((skill) => (
+ {skill}
+ ))}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/home/WorkGrid.module.css b/src/components/home/WorkGrid.module.css
new file mode 100644
index 0000000..14efda5
--- /dev/null
+++ b/src/components/home/WorkGrid.module.css
@@ -0,0 +1,42 @@
+.section {
+ padding: 0 var(--space-gutter) var(--space-section);
+
+ @media (min-width: 768px) {
+ padding: 0 var(--space-gutter-lg) var(--space-section);
+ }
+}
+
+.inner {
+ max-width: var(--max-width-full);
+ margin: 0 auto;
+}
+
+.header {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ padding-bottom: var(--space-4);
+ border-bottom: 1px solid var(--color-border-default);
+ margin-bottom: 0;
+}
+
+.label {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-medium);
+ letter-spacing: var(--tracking-widest);
+ text-transform: uppercase;
+ color: var(--color-text-tertiary);
+}
+
+.count {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-text-disabled);
+ letter-spacing: var(--tracking-wide);
+}
+
+.list {
+ display: flex;
+ flex-direction: column;
+}
diff --git a/src/components/home/WorkGrid.tsx b/src/components/home/WorkGrid.tsx
new file mode 100644
index 0000000..dfb7166
--- /dev/null
+++ b/src/components/home/WorkGrid.tsx
@@ -0,0 +1,25 @@
+import CaseStudyCard from "./CaseStudyCard";
+import type { CaseStudy } from "@/types/case-study";
+import styles from "./WorkGrid.module.css";
+
+interface WorkGridProps {
+ caseStudies: CaseStudy[];
+}
+
+export default function WorkGrid({ caseStudies }: WorkGridProps) {
+ return (
+
+
+
+ Selected Work
+ {String(caseStudies.length).padStart(2, "0")}
+
+
+ {caseStudies.map((cs, i) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/layout/Footer.module.css b/src/components/layout/Footer.module.css
new file mode 100644
index 0000000..5c3332f
--- /dev/null
+++ b/src/components/layout/Footer.module.css
@@ -0,0 +1,83 @@
+.footer {
+ border-top: 1px solid var(--color-border-default);
+ margin-top: var(--space-section);
+}
+
+.inner {
+ max-width: var(--max-width-full);
+ margin: 0 auto;
+}
+
+.top {
+ padding: var(--space-12) var(--space-gutter) var(--space-10);
+ border-bottom: 1px solid var(--color-border-subtle);
+
+ @media (min-width: 768px) {
+ padding: var(--space-16) var(--space-gutter-lg) var(--space-12);
+ }
+}
+
+.name {
+ font-size: var(--text-4xl);
+ font-weight: var(--weight-bold);
+ letter-spacing: var(--tracking-tight);
+ line-height: var(--leading-tight);
+ color: var(--color-text-primary);
+ margin-bottom: var(--space-3);
+
+ @media (min-width: 768px) {
+ font-size: var(--text-5xl);
+ }
+}
+
+.tagline {
+ font-size: var(--text-base);
+ color: var(--color-text-tertiary);
+}
+
+.bottom {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--space-4);
+ padding: var(--space-6) var(--space-gutter);
+
+ @media (min-width: 768px) {
+ padding: var(--space-6) var(--space-gutter-lg);
+ }
+}
+
+.copy {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-text-disabled);
+}
+
+.links {
+ display: flex;
+ align-items: center;
+ gap: var(--space-6);
+ margin-right: auto;
+}
+
+.link {
+ font-size: var(--text-xs);
+ color: var(--color-text-tertiary);
+ transition: color var(--transition-base);
+}
+
+.link:hover {
+ color: var(--color-text-primary);
+}
+
+.link:focus-visible {
+ outline: 2px solid var(--color-focus-ring);
+ outline-offset: 2px;
+ border-radius: var(--radius-sm);
+}
+
+.built {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-text-disabled);
+}
diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx
new file mode 100644
index 0000000..5886fd3
--- /dev/null
+++ b/src/components/layout/Footer.tsx
@@ -0,0 +1,56 @@
+import styles from "./Footer.module.css";
+
+export default function Footer() {
+ return (
+
+
+
+
Elara Reyes
+
+ Building the systems that let teams ship.
+
+
+
+
+ © {new Date().getFullYear()}
+
+
+
+ Next.js · No Tailwind
+
+
+
+
+ );
+}
diff --git a/src/components/layout/Nav.module.css b/src/components/layout/Nav.module.css
new file mode 100644
index 0000000..62dbe2b
--- /dev/null
+++ b/src/components/layout/Nav.module.css
@@ -0,0 +1,72 @@
+.header {
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ background-color: color-mix(in srgb, var(--color-bg-base) 80%, transparent);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+ border-bottom: 1px solid var(--color-border-subtle);
+ transition: background-color var(--transition-slow), border-color var(--transition-slow);
+}
+
+.nav {
+ display: flex;
+ align-items: center;
+ gap: var(--space-8);
+ max-width: var(--max-width-full);
+ margin: 0 auto;
+ padding: var(--space-5) var(--space-gutter);
+
+ @media (min-width: 768px) {
+ padding: var(--space-5) var(--space-gutter-lg);
+ }
+}
+
+.logo {
+ font-family: var(--font-mono);
+ font-size: var(--text-sm);
+ font-weight: var(--weight-bold);
+ letter-spacing: var(--tracking-widest);
+ color: var(--color-text-primary);
+ text-transform: uppercase;
+ margin-right: auto;
+ transition: color var(--transition-base);
+}
+
+.logo:hover {
+ color: var(--color-accent-default);
+}
+
+.logo:focus-visible {
+ outline: 2px solid var(--color-focus-ring);
+ outline-offset: 2px;
+ border-radius: var(--radius-sm);
+}
+
+.links {
+ display: flex;
+ align-items: center;
+ gap: var(--space-1);
+}
+
+.link {
+ font-size: var(--text-sm);
+ font-weight: var(--weight-medium);
+ color: var(--color-text-tertiary);
+ padding: var(--space-2) var(--space-3);
+ border-radius: var(--radius-md);
+ transition: color var(--transition-base);
+}
+
+.link:hover {
+ color: var(--color-text-primary);
+}
+
+.link:focus-visible {
+ outline: 2px solid var(--color-focus-ring);
+ outline-offset: 2px;
+}
+
+.active {
+ color: var(--color-text-primary);
+}
diff --git a/src/components/layout/Nav.tsx b/src/components/layout/Nav.tsx
new file mode 100644
index 0000000..cf40a41
--- /dev/null
+++ b/src/components/layout/Nav.tsx
@@ -0,0 +1,39 @@
+"use client";
+
+import Link from "next/link";
+import { usePathname } from "next/navigation";
+import ThemeToggle from "@/components/ui/ThemeToggle";
+import styles from "./Nav.module.css";
+
+const links = [
+ { href: "/work", label: "Work" },
+ { href: "/playground", label: "Playground" },
+ { href: "/about", label: "About" },
+];
+
+export default function Nav() {
+ const pathname = usePathname();
+
+ return (
+
+
+
+ Elara Reyes
+
+
+ {links.map(({ href, label }) => (
+
+
+ {label}
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/components/playground/ComponentShowcase.module.css b/src/components/playground/ComponentShowcase.module.css
new file mode 100644
index 0000000..285dfd1
--- /dev/null
+++ b/src/components/playground/ComponentShowcase.module.css
@@ -0,0 +1,32 @@
+.showcase {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-10);
+}
+
+.group {}
+
+.groupLabel {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-medium);
+ letter-spacing: var(--tracking-wide);
+ color: var(--color-text-tertiary);
+ margin-bottom: var(--space-4);
+}
+
+.row {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--space-3);
+ padding: var(--space-6);
+ background-color: var(--color-bg-subtle);
+ border: 1px solid var(--color-border-subtle);
+ border-radius: var(--radius-md);
+}
+
+.hint {
+ font-size: var(--text-xs);
+ color: var(--color-text-tertiary);
+}
diff --git a/src/components/playground/ComponentShowcase.tsx b/src/components/playground/ComponentShowcase.tsx
new file mode 100644
index 0000000..37c02a9
--- /dev/null
+++ b/src/components/playground/ComponentShowcase.tsx
@@ -0,0 +1,65 @@
+"use client";
+
+import Button from "@/components/ui/Button";
+import Tag from "@/components/ui/Tag";
+import ThemeToggle from "@/components/ui/ThemeToggle";
+import styles from "./ComponentShowcase.module.css";
+
+export default function ComponentShowcase() {
+ return (
+
+
+
Button — variant
+
+ Primary
+ Secondary
+ Ghost
+
+
+
+
+
Button — size
+
+ Small
+ Medium
+ Large
+
+
+
+
+
Button — state
+
+ Enabled
+ Disabled
+ Disabled
+
+
+
+
+
Tag — color
+
+
+
+
+
+
+
+
+
+
+
+
Theme Toggle
+
+
+ Toggles the entire page theme
+
+
+
+ );
+}
diff --git a/src/components/playground/SpacingScale.module.css b/src/components/playground/SpacingScale.module.css
new file mode 100644
index 0000000..c66f06d
--- /dev/null
+++ b/src/components/playground/SpacingScale.module.css
@@ -0,0 +1,48 @@
+.scale {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
+}
+
+.step {
+ display: flex;
+ align-items: center;
+ gap: var(--space-8);
+}
+
+.meta {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-1);
+ min-width: 160px;
+ flex-shrink: 0;
+}
+
+.token {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-accent-default);
+}
+
+.values {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-text-tertiary);
+}
+
+.barTrack {
+ flex: 1;
+ height: 12px;
+ background-color: var(--color-bg-muted);
+ border-radius: var(--radius-pill);
+ overflow: hidden;
+}
+
+.bar {
+ height: 100%;
+ background-color: var(--color-accent-default);
+ border-radius: var(--radius-pill);
+ min-width: 4px;
+ max-width: 100%;
+ opacity: 0.7;
+}
diff --git a/src/components/playground/SpacingScale.tsx b/src/components/playground/SpacingScale.tsx
new file mode 100644
index 0000000..865c158
--- /dev/null
+++ b/src/components/playground/SpacingScale.tsx
@@ -0,0 +1,37 @@
+import styles from "./SpacingScale.module.css";
+
+const steps = [
+ { token: "--space-1", px: "4px", rem: "0.25rem" },
+ { token: "--space-2", px: "8px", rem: "0.5rem" },
+ { token: "--space-3", px: "12px", rem: "0.75rem" },
+ { token: "--space-4", px: "16px", rem: "1rem" },
+ { token: "--space-5", px: "20px", rem: "1.25rem" },
+ { token: "--space-6", px: "24px", rem: "1.5rem" },
+ { token: "--space-8", px: "32px", rem: "2rem" },
+ { token: "--space-10", px: "40px", rem: "2.5rem" },
+ { token: "--space-12", px: "48px", rem: "3rem" },
+ { token: "--space-16", px: "64px", rem: "4rem" },
+ { token: "--space-20", px: "80px", rem: "5rem" },
+ { token: "--space-24", px: "96px", rem: "6rem" },
+];
+
+export default function SpacingScale() {
+ return (
+
+ {steps.map(({ token, px, rem }) => (
+
+
+ {token}
+ {px} / {rem}
+
+
+
+ ))}
+
+ );
+}
diff --git a/src/components/playground/TokenSwatch.module.css b/src/components/playground/TokenSwatch.module.css
new file mode 100644
index 0000000..5025cd6
--- /dev/null
+++ b/src/components/playground/TokenSwatch.module.css
@@ -0,0 +1,35 @@
+.swatch {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
+}
+
+.color {
+ height: 48px;
+ border-radius: var(--radius-md);
+ border: 1px solid var(--color-border-default);
+}
+
+.info {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-1);
+}
+
+.token {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-accent-default);
+}
+
+.label {
+ font-size: var(--text-xs);
+ color: var(--color-text-primary);
+ font-weight: var(--weight-medium);
+}
+
+.hex {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-text-tertiary);
+}
diff --git a/src/components/playground/TokenSwatch.tsx b/src/components/playground/TokenSwatch.tsx
new file mode 100644
index 0000000..b5f1f7b
--- /dev/null
+++ b/src/components/playground/TokenSwatch.tsx
@@ -0,0 +1,25 @@
+import styles from "./TokenSwatch.module.css";
+
+interface TokenSwatchProps {
+ tokenName: string;
+ label: string;
+ hex: string;
+ group: "bg" | "text" | "border" | "accent";
+}
+
+export default function TokenSwatch({ tokenName, label, hex, group }: TokenSwatchProps) {
+ return (
+
+
+
+ {tokenName}
+ {label}
+ {hex}
+
+
+ );
+}
diff --git a/src/components/playground/TypeScale.module.css b/src/components/playground/TypeScale.module.css
new file mode 100644
index 0000000..de067f8
--- /dev/null
+++ b/src/components/playground/TypeScale.module.css
@@ -0,0 +1,42 @@
+.scale {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+}
+
+.step {
+ display: flex;
+ align-items: baseline;
+ gap: var(--space-8);
+ padding: var(--space-4) 0;
+ border-bottom: 1px solid var(--color-border-subtle);
+}
+
+.meta {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-1);
+ min-width: 160px;
+ flex-shrink: 0;
+}
+
+.token {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-accent-default);
+}
+
+.size {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-text-tertiary);
+}
+
+.sample {
+ line-height: var(--leading-tight);
+ color: var(--color-text-primary);
+ font-weight: var(--weight-regular);
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
diff --git a/src/components/playground/TypeScale.tsx b/src/components/playground/TypeScale.tsx
new file mode 100644
index 0000000..be14bd2
--- /dev/null
+++ b/src/components/playground/TypeScale.tsx
@@ -0,0 +1,32 @@
+import styles from "./TypeScale.module.css";
+
+const steps = [
+ { token: "--text-xs", size: "12px", sample: "Caption text, token labels, metadata" },
+ { token: "--text-sm", size: "14px", sample: "Secondary body copy, nav links, tags" },
+ { token: "--text-base", size: "16px", sample: "Primary body copy" },
+ { token: "--text-md", size: "18px", sample: "Lead paragraph, intro text" },
+ { token: "--text-lg", size: "20px", sample: "Section tagline, subheading" },
+ { token: "--text-xl", size: "24px", sample: "Card title, section heading" },
+ { token: "--text-2xl", size: "30px", sample: "Page subheading" },
+ { token: "--text-3xl", size: "36px", sample: "Page heading" },
+ { token: "--text-4xl", size: "48px", sample: "Hero heading" },
+ { token: "--text-5xl", size: "60px", sample: "Display / name" },
+];
+
+export default function TypeScale() {
+ return (
+
+ {steps.map(({ token, size, sample }) => (
+
+
+ {token}
+ {size}
+
+
+ {sample}
+
+
+ ))}
+
+ );
+}
diff --git a/src/components/ui/Button.module.css b/src/components/ui/Button.module.css
new file mode 100644
index 0000000..716471a
--- /dev/null
+++ b/src/components/ui/Button.module.css
@@ -0,0 +1,73 @@
+.button {
+ --btn-radius: var(--radius-md);
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-2);
+ border-radius: var(--btn-radius);
+ font-weight: var(--weight-medium);
+ transition: background-color var(--transition-base), color var(--transition-base),
+ border-color var(--transition-base), transform var(--transition-base);
+ white-space: nowrap;
+ text-decoration: none;
+}
+
+.button:focus-visible {
+ outline: 2px solid var(--color-focus-ring);
+ outline-offset: 2px;
+}
+
+.button:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+}
+
+/* Variants */
+.primary {
+ --btn-bg: var(--color-accent-default);
+ --btn-text: var(--color-text-on-accent);
+ background-color: var(--btn-bg);
+ color: var(--btn-text);
+ border: 1px solid transparent;
+}
+
+.primary:hover:not(:disabled) {
+ background-color: var(--color-accent-hover);
+}
+
+.secondary {
+ background-color: transparent;
+ color: var(--color-text-primary);
+ border: 1px solid var(--color-border-strong);
+}
+
+.secondary:hover:not(:disabled) {
+ background-color: var(--color-bg-muted);
+}
+
+.ghost {
+ background-color: transparent;
+ color: var(--color-text-secondary);
+ border: 1px solid transparent;
+}
+
+.ghost:hover:not(:disabled) {
+ color: var(--color-text-primary);
+ background-color: var(--color-bg-muted);
+}
+
+/* Sizes */
+.sm {
+ font-size: var(--text-xs);
+ padding: var(--space-2) var(--space-3);
+}
+
+.md {
+ font-size: var(--text-sm);
+ padding: var(--space-3) var(--space-6);
+}
+
+.lg {
+ font-size: var(--text-base);
+ padding: var(--space-4) var(--space-8);
+}
diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx
new file mode 100644
index 0000000..9f1dfa8
--- /dev/null
+++ b/src/components/ui/Button.tsx
@@ -0,0 +1,46 @@
+import Link from "next/link";
+import styles from "./Button.module.css";
+
+interface ButtonProps {
+ variant?: "primary" | "secondary" | "ghost";
+ size?: "sm" | "md" | "lg";
+ href?: string;
+ onClick?: () => void;
+ disabled?: boolean;
+ children: React.ReactNode;
+ className?: string;
+ external?: boolean;
+}
+
+export default function Button({
+ variant = "primary",
+ size = "md",
+ href,
+ onClick,
+ disabled,
+ children,
+ className,
+ external,
+}: ButtonProps) {
+ const cls = [styles.button, styles[variant], styles[size], className]
+ .filter(Boolean)
+ .join(" ");
+
+ if (href) {
+ return (
+
+ {children}
+
+ );
+ }
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/ui/Divider.module.css b/src/components/ui/Divider.module.css
new file mode 100644
index 0000000..6481990
--- /dev/null
+++ b/src/components/ui/Divider.module.css
@@ -0,0 +1,12 @@
+.divider {
+ border: none;
+ border-top: 1px solid var(--color-border-default);
+}
+
+.sm { margin-block: var(--space-6); }
+.md { margin-block: var(--space-12); }
+.lg { margin-block: var(--space-20); }
+
+.subtle {
+ border-top-color: var(--color-border-subtle);
+}
diff --git a/src/components/ui/Divider.tsx b/src/components/ui/Divider.tsx
new file mode 100644
index 0000000..15d1cf3
--- /dev/null
+++ b/src/components/ui/Divider.tsx
@@ -0,0 +1,14 @@
+import styles from "./Divider.module.css";
+
+interface DividerProps {
+ spacing?: "sm" | "md" | "lg";
+ subtle?: boolean;
+}
+
+export default function Divider({ spacing = "md", subtle = false }: DividerProps) {
+ return (
+
+ );
+}
diff --git a/src/components/ui/ProseWrapper.module.css b/src/components/ui/ProseWrapper.module.css
new file mode 100644
index 0000000..2a886ed
--- /dev/null
+++ b/src/components/ui/ProseWrapper.module.css
@@ -0,0 +1,103 @@
+.prose {
+ margin: 0 auto;
+ padding: 0 var(--space-gutter);
+
+ @media (min-width: 768px) {
+ padding: 0 var(--space-gutter-lg);
+ }
+}
+
+.content { max-width: var(--max-width-content); }
+.wide { max-width: var(--max-width-wide); }
+
+/* Prose typography */
+.prose h2 {
+ font-size: var(--text-2xl);
+ font-weight: var(--weight-semibold);
+ letter-spacing: var(--tracking-tight);
+ line-height: var(--leading-snug);
+ color: var(--color-text-primary);
+ margin-top: var(--space-16);
+ margin-bottom: var(--space-6);
+}
+
+.prose h3 {
+ font-size: var(--text-xl);
+ font-weight: var(--weight-semibold);
+ letter-spacing: var(--tracking-snug);
+ line-height: var(--leading-snug);
+ color: var(--color-text-primary);
+ margin-top: var(--space-10);
+ margin-bottom: var(--space-4);
+}
+
+.prose p {
+ font-size: var(--text-base);
+ line-height: var(--leading-relaxed);
+ color: var(--color-text-secondary);
+ margin-bottom: var(--space-6);
+}
+
+.prose ul,
+.prose ol {
+ list-style: revert;
+ padding-left: var(--space-6);
+ margin-bottom: var(--space-6);
+}
+
+.prose li {
+ font-size: var(--text-base);
+ line-height: var(--leading-relaxed);
+ color: var(--color-text-secondary);
+ margin-bottom: var(--space-2);
+}
+
+.prose strong {
+ font-weight: var(--weight-semibold);
+ color: var(--color-text-primary);
+}
+
+.prose code {
+ font-family: var(--font-mono);
+ font-size: 0.875em;
+ background-color: var(--color-bg-muted);
+ color: var(--color-text-primary);
+ padding: 0.15em 0.4em;
+ border-radius: var(--radius-sm);
+}
+
+.prose pre {
+ font-family: var(--font-mono);
+ font-size: var(--text-sm);
+ background-color: var(--color-bg-subtle);
+ border: 1px solid var(--color-border-default);
+ border-radius: var(--radius-md);
+ padding: var(--space-6);
+ overflow-x: auto;
+ margin-bottom: var(--space-8);
+}
+
+.prose pre code {
+ background: none;
+ padding: 0;
+ border-radius: 0;
+}
+
+.prose blockquote {
+ border-left: 3px solid var(--color-accent-default);
+ padding-left: var(--space-6);
+ margin-bottom: var(--space-6);
+ color: var(--color-text-secondary);
+ font-style: italic;
+}
+
+.prose a {
+ color: var(--color-accent-default);
+ text-decoration: underline;
+ text-underline-offset: 3px;
+ transition: color var(--transition-base);
+}
+
+.prose a:hover {
+ color: var(--color-accent-hover);
+}
diff --git a/src/components/ui/ProseWrapper.tsx b/src/components/ui/ProseWrapper.tsx
new file mode 100644
index 0000000..6ba94ed
--- /dev/null
+++ b/src/components/ui/ProseWrapper.tsx
@@ -0,0 +1,14 @@
+import styles from "./ProseWrapper.module.css";
+
+interface ProseWrapperProps {
+ children: React.ReactNode;
+ width?: "content" | "wide";
+}
+
+export default function ProseWrapper({ children, width = "content" }: ProseWrapperProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/ui/Tag.module.css b/src/components/ui/Tag.module.css
new file mode 100644
index 0000000..218407c
--- /dev/null
+++ b/src/components/ui/Tag.module.css
@@ -0,0 +1,55 @@
+.tag {
+ display: inline-flex;
+ align-items: center;
+ border-radius: var(--radius-pill);
+ font-weight: var(--weight-medium);
+ letter-spacing: var(--tracking-wide);
+ white-space: nowrap;
+}
+
+/* Sizes */
+.sm {
+ font-size: var(--text-xs);
+ padding: var(--space-1) var(--space-3);
+}
+
+.md {
+ font-size: var(--text-sm);
+ padding: var(--space-2) var(--space-4);
+}
+
+/* Colors */
+.default {
+ background-color: var(--color-bg-muted);
+ color: var(--color-text-secondary);
+}
+
+.blue {
+ background-color: var(--color-blue-100);
+ color: var(--color-blue-600);
+}
+
+.green {
+ background-color: var(--color-green-100);
+ color: var(--color-green-600);
+}
+
+.amber {
+ background-color: var(--color-amber-100);
+ color: var(--color-amber-600);
+}
+
+[data-theme="dark"] .blue {
+ background-color: var(--color-blue-900);
+ color: var(--color-blue-300);
+}
+
+[data-theme="dark"] .green {
+ background-color: var(--color-green-900);
+ color: var(--color-green-500);
+}
+
+[data-theme="dark"] .amber {
+ background-color: var(--color-amber-900);
+ color: var(--color-amber-500);
+}
diff --git a/src/components/ui/Tag.tsx b/src/components/ui/Tag.tsx
new file mode 100644
index 0000000..fa6b2fa
--- /dev/null
+++ b/src/components/ui/Tag.tsx
@@ -0,0 +1,15 @@
+import styles from "./Tag.module.css";
+
+interface TagProps {
+ label: string;
+ color?: "default" | "blue" | "green" | "amber";
+ size?: "sm" | "md";
+}
+
+export default function Tag({ label, color = "default", size = "sm" }: TagProps) {
+ return (
+
+ {label}
+
+ );
+}
diff --git a/src/components/ui/ThemeToggle.module.css b/src/components/ui/ThemeToggle.module.css
new file mode 100644
index 0000000..9e36206
--- /dev/null
+++ b/src/components/ui/ThemeToggle.module.css
@@ -0,0 +1,20 @@
+.toggle {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ border-radius: var(--radius-md);
+ color: var(--color-text-secondary);
+ transition: color var(--transition-base), background-color var(--transition-base);
+}
+
+.toggle:hover {
+ color: var(--color-text-primary);
+ background-color: var(--color-bg-muted);
+}
+
+.toggle:focus-visible {
+ outline: 2px solid var(--color-focus-ring);
+ outline-offset: 2px;
+}
diff --git a/src/components/ui/ThemeToggle.tsx b/src/components/ui/ThemeToggle.tsx
new file mode 100644
index 0000000..7ed5a38
--- /dev/null
+++ b/src/components/ui/ThemeToggle.tsx
@@ -0,0 +1,43 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import styles from "./ThemeToggle.module.css";
+
+export default function ThemeToggle() {
+ const [theme, setTheme] = useState<"light" | "dark">("light");
+
+ useEffect(() => {
+ const stored = localStorage.getItem("theme");
+ const preferred = window.matchMedia("(prefers-color-scheme: dark)").matches
+ ? "dark"
+ : "light";
+ setTheme((stored as "light" | "dark") || preferred);
+ }, []);
+
+ function toggle() {
+ const next = theme === "light" ? "dark" : "light";
+ setTheme(next);
+ document.documentElement.setAttribute("data-theme", next);
+ localStorage.setItem("theme", next);
+ }
+
+ return (
+
+ {theme === "light" ? (
+
+
+
+
+ ) : (
+
+
+
+ )}
+
+ );
+}
diff --git a/src/components/work/Annotation.module.css b/src/components/work/Annotation.module.css
new file mode 100644
index 0000000..a694532
--- /dev/null
+++ b/src/components/work/Annotation.module.css
@@ -0,0 +1,68 @@
+.annotation {
+ padding: var(--space-5) var(--space-6);
+ border-radius: var(--radius-md);
+ margin-block: var(--space-8);
+ border: 1px solid transparent;
+}
+
+.typeLabel {
+ display: inline-block;
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-bold);
+ letter-spacing: var(--tracking-widest);
+ text-transform: uppercase;
+ margin-bottom: var(--space-2);
+}
+
+.content {
+ font-size: var(--text-sm);
+ line-height: var(--leading-relaxed);
+ color: var(--color-text-secondary);
+}
+
+.content p {
+ margin-bottom: 0;
+ color: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+
+/* Type variants */
+.insight {
+ background-color: var(--color-accent-subtle);
+ border-color: var(--color-blue-300);
+}
+.insight .typeLabel { color: var(--color-accent-blue); }
+
+.decision {
+ background-color: var(--color-green-100);
+ border-color: var(--color-green-500);
+}
+.decision .typeLabel { color: var(--color-green-600); }
+
+.learning {
+ background-color: var(--color-amber-100);
+ border-color: var(--color-amber-500);
+}
+.learning .typeLabel { color: var(--color-amber-600); }
+
+.warning {
+ background-color: var(--color-amber-100);
+ border-color: var(--color-amber-600);
+}
+.warning .typeLabel { color: var(--color-amber-600); }
+
+[data-theme="dark"] .decision {
+ background-color: var(--color-green-900);
+ border-color: var(--color-green-500);
+}
+[data-theme="dark"] .decision .typeLabel { color: var(--color-green-500); }
+
+[data-theme="dark"] .learning,
+[data-theme="dark"] .warning {
+ background-color: var(--color-amber-900);
+ border-color: var(--color-amber-500);
+}
+[data-theme="dark"] .learning .typeLabel,
+[data-theme="dark"] .warning .typeLabel { color: var(--color-amber-500); }
diff --git a/src/components/work/Annotation.tsx b/src/components/work/Annotation.tsx
new file mode 100644
index 0000000..7cab4e0
--- /dev/null
+++ b/src/components/work/Annotation.tsx
@@ -0,0 +1,24 @@
+import styles from "./Annotation.module.css";
+
+type AnnotationType = "insight" | "decision" | "learning" | "warning";
+
+interface AnnotationProps {
+ type: AnnotationType;
+ children: React.ReactNode;
+}
+
+const labels: Record = {
+ insight: "Insight",
+ decision: "Decision",
+ learning: "Learning",
+ warning: "Watch out",
+};
+
+export default function Annotation({ type, children }: AnnotationProps) {
+ return (
+
+ {labels[type]}
+ {children}
+
+ );
+}
diff --git a/src/components/work/CaseStudyHeader.module.css b/src/components/work/CaseStudyHeader.module.css
new file mode 100644
index 0000000..2931281
--- /dev/null
+++ b/src/components/work/CaseStudyHeader.module.css
@@ -0,0 +1,75 @@
+.header {
+ padding-bottom: var(--space-section-sm);
+}
+
+.accent {
+ height: 6px;
+ width: 100%;
+}
+
+.inner {
+ max-width: var(--max-width-content);
+ margin: 0 auto;
+ padding: var(--space-12) var(--space-gutter) 0;
+
+ @media (min-width: 768px) {
+ padding: var(--space-16) var(--space-gutter-lg) 0;
+ }
+}
+
+.title {
+ font-size: var(--text-4xl);
+ font-weight: var(--weight-bold);
+ letter-spacing: var(--tracking-tight);
+ line-height: var(--leading-tight);
+ color: var(--color-text-primary);
+ margin-bottom: var(--space-4);
+
+ @media (max-width: 767px) {
+ font-size: var(--text-3xl);
+ }
+}
+
+.tagline {
+ font-size: var(--text-lg);
+ line-height: var(--leading-relaxed);
+ color: var(--color-text-secondary);
+ margin-bottom: var(--space-10);
+}
+
+.meta {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: var(--space-6);
+ margin-bottom: var(--space-8);
+ padding: var(--space-6);
+ background-color: var(--color-bg-subtle);
+ border: 1px solid var(--color-border-subtle);
+ border-radius: var(--radius-md);
+
+ @media (min-width: 480px) {
+ grid-template-columns: repeat(4, 1fr);
+ }
+}
+
+.metaItem dt {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-medium);
+ letter-spacing: var(--tracking-widest);
+ text-transform: uppercase;
+ color: var(--color-text-tertiary);
+ margin-bottom: var(--space-1);
+}
+
+.metaItem dd {
+ font-size: var(--text-sm);
+ font-weight: var(--weight-medium);
+ color: var(--color-text-primary);
+}
+
+.tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-2);
+}
diff --git a/src/components/work/CaseStudyHeader.tsx b/src/components/work/CaseStudyHeader.tsx
new file mode 100644
index 0000000..846eb01
--- /dev/null
+++ b/src/components/work/CaseStudyHeader.tsx
@@ -0,0 +1,47 @@
+import Tag from "@/components/ui/Tag";
+import type { CaseStudyFrontmatter } from "@/types/case-study";
+import styles from "./CaseStudyHeader.module.css";
+
+interface CaseStudyHeaderProps {
+ frontmatter: CaseStudyFrontmatter;
+}
+
+export default function CaseStudyHeader({ frontmatter }: CaseStudyHeaderProps) {
+ const { title, tagline, company, role, year, duration, tags, coverAccent } = frontmatter;
+
+ return (
+
+ );
+}
diff --git a/src/components/work/MetricCallout.module.css b/src/components/work/MetricCallout.module.css
new file mode 100644
index 0000000..81a660c
--- /dev/null
+++ b/src/components/work/MetricCallout.module.css
@@ -0,0 +1,39 @@
+.callout {
+ display: flex;
+ align-items: center;
+ gap: var(--space-6);
+ padding: var(--space-6) var(--space-8);
+ background-color: var(--color-bg-subtle);
+ border-left: 3px solid var(--color-accent-default);
+ border-radius: 0 var(--radius-md) var(--radius-md) 0;
+ margin-block: var(--space-8);
+}
+
+.metric {
+ font-size: var(--text-4xl);
+ font-weight: var(--weight-bold);
+ letter-spacing: var(--tracking-tight);
+ color: var(--color-accent-default);
+ line-height: 1;
+ white-space: nowrap;
+}
+
+.text {
+ flex: 1;
+}
+
+.label {
+ font-size: var(--text-sm);
+ font-weight: var(--weight-semibold);
+ color: var(--color-text-primary);
+ margin-bottom: 0;
+ line-height: var(--leading-snug);
+}
+
+.context {
+ font-size: var(--text-xs);
+ font-family: var(--font-mono);
+ color: var(--color-text-tertiary);
+ margin-top: var(--space-1);
+ margin-bottom: 0;
+}
diff --git a/src/components/work/MetricCallout.tsx b/src/components/work/MetricCallout.tsx
new file mode 100644
index 0000000..d97468f
--- /dev/null
+++ b/src/components/work/MetricCallout.tsx
@@ -0,0 +1,19 @@
+import styles from "./MetricCallout.module.css";
+
+interface MetricCalloutProps {
+ metric: string;
+ label: string;
+ context?: string;
+}
+
+export default function MetricCallout({ metric, label, context }: MetricCalloutProps) {
+ return (
+
+
{metric}
+
+
{label}
+ {context &&
{context}
}
+
+
+ );
+}
diff --git a/src/components/work/TokenTable.module.css b/src/components/work/TokenTable.module.css
new file mode 100644
index 0000000..abbca98
--- /dev/null
+++ b/src/components/work/TokenTable.module.css
@@ -0,0 +1,69 @@
+.wrapper {
+ overflow-x: auto;
+ margin-block: var(--space-8);
+ border: 1px solid var(--color-border-default);
+ border-radius: var(--radius-md);
+}
+
+.table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: var(--text-sm);
+}
+
+.table thead tr {
+ border-bottom: 1px solid var(--color-border-default);
+ background-color: var(--color-bg-subtle);
+}
+
+.table th {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-semibold);
+ letter-spacing: var(--tracking-wide);
+ text-transform: uppercase;
+ color: var(--color-text-tertiary);
+ text-align: left;
+ padding: var(--space-3) var(--space-4);
+}
+
+.table td {
+ padding: var(--space-3) var(--space-4);
+ border-bottom: 1px solid var(--color-border-subtle);
+ vertical-align: middle;
+ color: var(--color-text-secondary);
+}
+
+.table tr:last-child td {
+ border-bottom: none;
+}
+
+.swatch {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ border-radius: var(--radius-sm);
+ border: 1px solid var(--color-border-default);
+ vertical-align: middle;
+}
+
+.tokenName {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-accent-default);
+ background: none;
+ padding: 0;
+}
+
+.value {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--color-text-secondary);
+ background: none;
+ padding: 0;
+}
+
+.usage {
+ font-size: var(--text-xs);
+ color: var(--color-text-tertiary);
+}
diff --git a/src/components/work/TokenTable.tsx b/src/components/work/TokenTable.tsx
new file mode 100644
index 0000000..cf6c029
--- /dev/null
+++ b/src/components/work/TokenTable.tsx
@@ -0,0 +1,49 @@
+import styles from "./TokenTable.module.css";
+
+interface TokenRow {
+ name: string;
+ value: string;
+ resolved: string;
+ usage: string;
+}
+
+interface TokenTableProps {
+ tokens: TokenRow[];
+}
+
+export default function TokenTable({ tokens }: TokenTableProps) {
+ return (
+
+
+
+
+ Swatch
+ Token
+ Value
+ Usage
+
+
+
+ {tokens.map((token) => (
+
+
+
+
+
+ {token.name}
+
+
+ {token.value}
+
+ {token.usage}
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/content/case-studies/building-meridian.mdx b/src/content/case-studies/building-meridian.mdx
new file mode 100644
index 0000000..185e1d6
--- /dev/null
+++ b/src/content/case-studies/building-meridian.mdx
@@ -0,0 +1,87 @@
+---
+title: "Building Meridian"
+tagline: "A design system built from zero at a Series B fintech"
+company: "Meridian Financial"
+role: "Design Systems Lead"
+year: "2023"
+duration: "18 months"
+tags: ["Foundations", "Tokens", "Component Library"]
+coverAccent: "var(--color-accent-blue)"
+status: "published"
+---
+
+## Overview
+
+Meridian Financial was growing fast — four product teams, twelve designers, twenty-eight engineers, and zero shared design language. I joined as their first dedicated design systems hire with a mandate to build the foundation from scratch.
+
+Eighteen months later, Meridian had a versioned component library, a structured token system synced between Figma and code, and a documentation site used by every team in the company.
+
+## The Problem
+
+The product looked different depending on which team had built it. Buttons had six different corner radii across the product. The same action — saving a record — used three different colors in three different parts of the app. Error states were inconsistent enough that a user study flagged them as confusing.
+
+Designers were duplicating work constantly. Engineers were making ad-hoc styling decisions because there was no canonical source of truth. Every sprint included undifferentiated rework.
+
+## Audit Findings
+
+Before building anything, I spent three weeks auditing the existing product and codebase.
+
+
+
+The raw numbers: **340 unique color values** in Figma across all files. **12 button variants** with different radii, weights, and colors. **No spacing system** — every component used hardcoded pixel values. No token file in the codebase.
+
+
+The audit wasn't just a design exercise. It was how I built credibility with engineering. When I showed an engineer that the codebase had 247 unique hardcoded hex values, the conversation about "why we need this" ended.
+
+
+## System Decisions
+
+**Three-tier token architecture.** Primitive tokens hold raw values. Semantic tokens map meaning to those values. Component tokens scope decisions to individual components. No component CSS ever references a primitive token directly.
+
+**Semantic tokens before components.** The instinct is to start building buttons. The right move is to define what "primary text" and "interactive background" mean *before* you build a single component. We spent the first two months on tokens alone.
+
+
+We chose semantic token naming over descriptive naming. `--color-text-primary` rather than `--color-dark-gray` means the token stays correct when the theme changes. This added two weeks of debate but saved months of migration later.
+
+
+**Figma Variables synced to CSS custom properties.** The Figma token library and the CSS token file are generated from the same source of truth using Style Dictionary. A designer updating a color in Figma kicks off a PR in the design tokens repo.
+
+## Implementation
+
+We started with form inputs, not buttons. Buttons are visible; inputs are used constantly and had the most inconsistency. Fixing them first showed the team immediate, tangible improvement.
+
+Component architecture followed a clear spec: every component defines its own component-level tokens, which reference semantic tokens. No component imports another component's tokens directly.
+
+
+
+Storybook served as the documentation layer — but the real documentation was the usage guidelines written alongside each component. Not just "here are the props," but "use this when X, not when Y."
+
+## Governance Model
+
+V1 shipped with a simple governance model: one weekly sync, a `#design-system` Slack channel, and a shared Notion backlog visible to all teams.
+
+Contribution happened through a lightweight RFC process. Teams proposing a new component fill out a one-page template: what problem does this solve, which teams need it, what's the usage pattern? RFCs with broad applicability got prioritized; one-off needs were documented as patterns without becoming components.
+
+## Impact
+
+
+
+
+
+The accessibility improvement was unexpected: because every component was built to WCAG 2.1 AA standards from the start, the product's accessibility score improved from 61 to 89 in six months without a dedicated accessibility sprint.
+
+## Learnings
+
+Starting with tokens instead of components was the right call, but it required selling the delay to stakeholders. A visual preview of the token system — color palette, type ramp, spacing scale — helped make the "invisible" work legible.
+
+
+The system is not done when it ships. The teams that got the most value from Meridian were the ones that sent someone to the weekly sync. Adoption is a relationship, not a rollout.
+
+
+I would start the governance model earlier — ideally during the audit phase, not after V1. Getting engineers and product managers involved in RFC reviews from the beginning would have built more ownership sooner.
diff --git a/src/content/case-studies/scaling-forma.mdx b/src/content/case-studies/scaling-forma.mdx
new file mode 100644
index 0000000..023a175
--- /dev/null
+++ b/src/content/case-studies/scaling-forma.mdx
@@ -0,0 +1,85 @@
+---
+title: "Scaling Forma"
+tagline: "Governance & contribution models at scale"
+company: "Forma Labs"
+role: "Design Systems Lead"
+year: "2024"
+duration: "9 months"
+tags: ["Governance", "Process", "Multi-team"]
+coverAccent: "var(--color-accent-green)"
+status: "published"
+---
+
+## Overview
+
+Forma already had a design system. It had worked beautifully when the company was fifty people and two product teams. At four hundred people and eight product teams, it was collapsing under its own weight.
+
+The problem was not the design. The problem was governance — or the absence of it. I was brought in to fix the process, not the pixels.
+
+## The Problem
+
+Pull requests sat for six weeks without review. Teams were forking components and maintaining their own copies. The system had three nominal "owners" with no clear decision-making authority, which meant any contested PR became a negotiation rather than a decision.
+
+The backlog had 140 open issues. No prioritization framework. Engineers had stopped filing issues because nothing seemed to happen.
+
+
+When I joined, I spent the first two weeks just reading — every open issue, every PR comment, every Slack thread in the `#design-system` channel. The pattern was clear: teams didn't distrust the system. They distrusted the *process*. They'd been burned by slow responses too many times.
+
+
+## Audit Findings
+
+I ran a contribution audit before proposing any changes. Questions: Who had contributed in the last six months? What was the median time from PR open to merge? What percentage of issues were closed without action?
+
+The answers: 23 component forks in active use across product codebases. Median PR review time: 41 days. 64% of issues closed as "won't fix" with no explanation.
+
+## System Decisions
+
+**Federated ownership model.** Each product area (Growth, Core Product, Data, Platform, etc.) has a designated "DS Champion" — an engineer or senior designer with commit rights to their domain. The core team reviews and approves, but champions can merge within their area without waiting for central sign-off.
+
+
+Federated ownership felt risky. What if champions make inconsistent decisions? But the alternative — a single bottleneck team — had already failed. We accepted some inconsistency risk in exchange for meaningful speed improvement.
+
+
+**Tiered contribution model.** Not all contributions are equal. We defined three tiers:
+
+- **T1 (Style overrides):** Color, spacing, typography tweaks within token bounds. No RFC required. PR directly to your area.
+- **T2 (Variant additions):** New variants of existing components. Lightweight RFC: one paragraph, usage example, affected teams.
+- **T3 (Net-new components):** Full RFC required. Usage data from at least two teams. Two-week open comment period. Champion sign-off.
+
+**Async by default.** The weekly all-hands sync was replaced with a monthly review. Day-to-day happened in Notion comments and a streamlined `#ds-rfc` Slack channel. Open office hours twice a week — 30 minutes, no agenda, come with questions.
+
+## Implementation
+
+I wrote the RFC template in week three and ran it by six engineers and four designers before socializing it. The template asks: What problem are you solving? Who else needs this? What does "done" look like? How will you measure it?
+
+The backlog was triaged over four weeks. Every open issue got one of three labels: `accepted`, `declined-with-reason`, or `needs-RFC`. Every declined issue got a written explanation.
+
+
+Writing decline reasons took three times longer than just closing issues. It was worth it. Teams saw that decisions had reasoning behind them, not arbitrary gatekeeping. One team converted their "declined" issue into a successful T2 RFC within a month.
+
+
+The fork cleanup was handled gradually, not in a big-bang migration. Each product area DS champion took ownership of their fork and either contributed it back as a T3 RFC or deprecated it with an official alternative.
+
+## Governance Model
+
+The RFC process is now the load-bearing structure of the system. Monthly reviews go through accepted RFCs in priority order. Champions have a voice in prioritization for their area. The core team focuses on T3 decisions and system-wide concerns.
+
+Versioning follows strict semantic versioning: patch for bug fixes, minor for new variants, major for breaking changes. Every major release has a migration guide written before the release ships.
+
+## Impact
+
+
+
+
+
+Engineer confidence in the system, measured via quarterly survey, went from 3.1/5 to 4.4/5 in two cycles.
+
+## Learnings
+
+Governance is a product. It needs the same care as a component — a clear purpose, a defined API (the RFC process), and documentation that explains not just the "what" but the "why."
+
+
+The hardest part of this project wasn't designing the process. It was earning trust from teams who'd been burned. Process documentation doesn't build trust — consistent, fast, explained decisions do. The first few months were mostly about showing up and closing things quickly.
+
+
+I'd start the champion network earlier. Finding the right DS Champions before you have a governance model — people who already care about system quality — makes adoption much faster than training champions after the model is designed.
diff --git a/src/content/case-studies/token-migration-dusk.mdx b/src/content/case-studies/token-migration-dusk.mdx
new file mode 100644
index 0000000..1c57af0
--- /dev/null
+++ b/src/content/case-studies/token-migration-dusk.mdx
@@ -0,0 +1,94 @@
+---
+title: "Token Migration at Dusk"
+tagline: "Semantic tokens and multi-brand theming from a legacy codebase"
+company: "Dusk Technologies"
+role: "Senior Design Systems Designer"
+year: "2024"
+duration: "7 months"
+tags: ["Tokens", "Migration", "Theming"]
+coverAccent: "var(--color-accent-amber)"
+status: "published"
+---
+
+## Overview
+
+Dusk had been acquired by a larger enterprise platform and needed to white-label their product for three enterprise clients — each with distinct brand requirements, strict accessibility standards, and zero tolerance for visual inconsistency.
+
+The challenge: 1,247 hardcoded hex values in the codebase. No token file. No Figma variables. Seven years of accumulated styling decisions with no shared language.
+
+## The Problem
+
+Every brand request was effectively a fork. The engineering team estimated a full white-label for a new client would take six weeks. Client contracts required delivery in two. The business was blocked.
+
+The surface-level problem was the codebase. The deeper problem was that there had never been a separation between "this is a raw value" and "this is what this value means." Color wasn't semantic — it was just color.
+
+## Audit Findings
+
+The first thing I did was run a regex scan on the codebase.
+
+
+`grep -r "#[0-9A-Fa-f]" --include="*.css" --include="*.tsx" .` returned 1,247 matches across 89 unique hex values. Showing this output to the CTO ended the "how bad is it really" conversation immediately.
+
+
+
+
+The 89 unique values collapsed to 34 semantic tokens. Many hex values were near-duplicates that had drifted over time — `#1a73e8` and `#1b74e9` appearing in different parts of the same flow.
+
+## System Decisions
+
+**Token layer before component layer.** The strategic decision that made everything else possible: we would not touch a single component until the token system was stable. Touching components first means touching them twice.
+
+**Primitive → Semantic → Brand.** Three layers:
+1. Primitive tokens: raw values, brand-neutral (`--color-blue-600: #2563eb`)
+2. Semantic tokens: meaning-based (`--color-action-primary: var(--color-blue-600)`)
+3. Brand overrides: `[data-theme="brand-a"]` semantic overrides only
+
+No brand ever touches a primitive directly. Brand tokens only override semantic tokens.
+
+
+This constraint — brands only override semantics — was initially controversial. Brand A's team wanted to use a custom blue that didn't exist in the primitive scale. We added the primitive, then mapped the semantic token to it. The extra step pays for itself in maintainability.
+
+
+**Automated codemods for the migration.** Manually replacing 1,247 instances is error-prone and expensive. We wrote jscodeshift transforms that matched hardcoded hex values and replaced them with the appropriate semantic token. The transforms were conservative — they flagged ambiguous cases (a hex used in multiple semantic roles) for manual review rather than making a guess.
+
+## Implementation
+
+Phase 1: Build the primitive token set and the semantic layer for Brand A (the original Dusk brand). Ship this in production. Verify no visual regressions.
+
+Phase 2: Run the codemods. Each engineer reviewed their domain's flagged cases. 94% of replacements were automated; 6% required manual decision.
+
+Phase 3: Build Brand B and Brand C token files. These are 40–60 line CSS files of `[data-theme="brand-x"]` overrides. That's it. The entire multi-brand capability lives in those overrides.
+
+
+
+**The Figma side:** Migrated local styles to Figma Variables in parallel. Three variable collections — Brand A, B, C — with identical token names. Designers switch between brands with a single variable mode toggle.
+
+## Handling Edge Cases
+
+Brand C had no concept of "danger" states — their product domain was messaging, not financial transactions. The semantic token `--color-feedback-danger` had no meaningful equivalent in their brand language.
+
+
+Semantic tokens assume a universal semantic vocabulary. When a brand genuinely doesn't have a semantic concept, don't force a mapping. We used a fallback strategy: Brand C's danger token defaulted to a high-contrast neutral rather than the usual red. The UI pattern was preserved; the brand language wasn't violated.
+
+
+## Impact
+
+
+
+
+
+## Learnings
+
+The migration tooling was as important as the token system design. A well-designed token architecture that requires manual migration will stall. Automating the tedious parts let the team focus on the decisions that actually required judgment.
+
+
+Write the migration guide before you start the migration. The act of writing "here's how you move from X to Y" reveals ambiguities in your token design before you've committed to it in 1,000 places. We rewrote the naming convention twice based on feedback from the draft guide.
+
+
+The business outcome here was unusually concrete — a new enterprise client can now be onboarded in a week instead of six. That number has been cited in sales calls. It's a rare case where design systems work directly contributed to revenue.
diff --git a/src/lib/case-studies.ts b/src/lib/case-studies.ts
new file mode 100644
index 0000000..107561b
--- /dev/null
+++ b/src/lib/case-studies.ts
@@ -0,0 +1,35 @@
+import type { CaseStudy } from "@/types/case-study";
+
+export const CASE_STUDIES: CaseStudy[] = [
+ {
+ slug: "building-meridian",
+ title: "Building Meridian",
+ tagline: "A design system built from zero at a Series B fintech",
+ tags: ["Foundations", "Tokens", "Component Library"],
+ year: "2023",
+ role: "Design Systems Lead",
+ coverAccent: "var(--color-accent-blue)",
+ },
+ {
+ slug: "scaling-forma",
+ title: "Scaling Forma",
+ tagline: "Governance & contribution models at scale",
+ tags: ["Governance", "Process", "Multi-team"],
+ year: "2024",
+ role: "Design Systems Lead",
+ coverAccent: "var(--color-accent-green)",
+ },
+ {
+ slug: "token-migration-dusk",
+ title: "Token Migration at Dusk",
+ tagline: "Semantic tokens and multi-brand theming from a legacy codebase",
+ tags: ["Tokens", "Migration", "Theming"],
+ year: "2024",
+ role: "Senior Design Systems Designer",
+ coverAccent: "var(--color-accent-amber)",
+ },
+];
+
+export function getCaseStudyList(): CaseStudy[] {
+ return CASE_STUDIES;
+}
diff --git a/src/lib/fonts.ts b/src/lib/fonts.ts
new file mode 100644
index 0000000..7ed7be6
--- /dev/null
+++ b/src/lib/fonts.ts
@@ -0,0 +1,13 @@
+import { Inter, JetBrains_Mono } from "next/font/google";
+
+export const inter = Inter({
+ subsets: ["latin"],
+ variable: "--font-inter",
+ display: "swap",
+});
+
+export const jetbrainsMono = JetBrains_Mono({
+ subsets: ["latin"],
+ variable: "--font-jetbrains-mono",
+ display: "swap",
+});
diff --git a/src/lib/mdx.ts b/src/lib/mdx.ts
new file mode 100644
index 0000000..9f088ac
--- /dev/null
+++ b/src/lib/mdx.ts
@@ -0,0 +1,47 @@
+import fs from "fs";
+import path from "path";
+import matter from "gray-matter";
+import type { ComponentType } from "react";
+import type { CaseStudyFrontmatter } from "@/types/case-study";
+
+const CONTENT_DIR = path.join(process.cwd(), "src/content/case-studies");
+
+export function getAllSlugs(): string[] {
+ const files = fs.readdirSync(CONTENT_DIR);
+ return files
+ .filter((f) => f.endsWith(".mdx"))
+ .map((f) => f.replace(/\.mdx$/, ""));
+}
+
+export function getFrontmatter(slug: string): CaseStudyFrontmatter {
+ const filePath = path.join(CONTENT_DIR, `${slug}.mdx`);
+ const raw = fs.readFileSync(filePath, "utf-8");
+ const { data } = matter(raw);
+ return data as CaseStudyFrontmatter;
+}
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+type MDXModule = { default: ComponentType };
+
+const mdxModuleLoaders: Record Promise> = {
+ "building-meridian": () =>
+ import("@/content/case-studies/building-meridian.mdx"),
+ "scaling-forma": () =>
+ import("@/content/case-studies/scaling-forma.mdx"),
+ "token-migration-dusk": () =>
+ import("@/content/case-studies/token-migration-dusk.mdx"),
+};
+
+export async function getMdxContent(slug: string): Promise<{
+ frontmatter: CaseStudyFrontmatter;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ Content: ComponentType;
+}> {
+ const loader = mdxModuleLoaders[slug];
+ if (!loader) throw new Error(`ENOENT: no MDX file for slug "${slug}"`);
+
+ const [{ default: Content }] = await Promise.all([loader()]);
+ const frontmatter = getFrontmatter(slug);
+
+ return { frontmatter, Content };
+}
diff --git a/src/types/case-study.ts b/src/types/case-study.ts
new file mode 100644
index 0000000..07e9f36
--- /dev/null
+++ b/src/types/case-study.ts
@@ -0,0 +1,21 @@
+export interface CaseStudyFrontmatter {
+ title: string;
+ tagline: string;
+ company: string;
+ role: string;
+ year: string;
+ duration: string;
+ tags: string[];
+ coverAccent: string;
+ status: "published" | "draft";
+}
+
+export interface CaseStudy {
+ slug: string;
+ title: string;
+ tagline: string;
+ tags: string[];
+ year: string;
+ role: string;
+ coverAccent: string;
+}
diff --git a/src/types/mdx.d.ts b/src/types/mdx.d.ts
new file mode 100644
index 0000000..cb0ff5c
--- /dev/null
+++ b/src/types/mdx.d.ts
@@ -0,0 +1 @@
+export {};
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..c133409
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}