diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 84d9ded..48a360d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,4 +22,5 @@ jobs:
- run: npm run check
- run: npm run build
- run: npm run validate
+ - run: npm test
- run: npx wrangler deploy --dry-run
diff --git a/README.md b/README.md
index c4ce3b4..73e765a 100644
--- a/README.md
+++ b/README.md
@@ -101,11 +101,14 @@ npm ci
npm run check
npm run build
npm run validate
+npm test
npm run preview
```
Cloudflare deployment uses Workers Static Assets. The site is fully static and does not require the Astro Cloudflare server adapter.
+The site uses locally hosted Archivo and IBM Plex Mono fonts, an original SVG hardware illustration and a small progressive-enhancement script. Navigation and all four research tracks remain available without JavaScript. Interaction tests run against the built HTML and cover tab selection, keyboard commands, menu state, support returns and document structure; they do not replace browser layout or assistive-technology testing.
+
## Licenses
- Software source: [MIT](LICENSES/MIT.txt)
diff --git a/package-lock.json b/package-lock.json
index 884ce80..4e5aaca 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,9 @@
"astro": "^7.2.8",
"typescript": "^5.9.3",
"wrangler": "^4.127.0"
+ },
+ "devDependencies": {
+ "linkedom": "^0.18.13"
}
},
"node_modules/@astrojs/check": {
@@ -593,9 +596,9 @@
}
},
"node_modules/@cloudflare/workerd-darwin-64": {
- "version": "1.20260826.1",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260826.1.tgz",
- "integrity": "sha512-8UsGGY8ZUiYHOWdsxBlNsGmaHBGArVwJ3CM4nWpfBhthjjYe4M/OqrTpqKF7NNWb63qQiv1d8Z+Z6/hmUqNKIQ==",
+ "version": "1.20260911.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260911.1.tgz",
+ "integrity": "sha512-785eaY1bkR1cm4Z/PCUeteZYmTMe6lre2zz63/GdGGimsoMsKxgl4brFPRukim8iv28EyD1XoCB/VPYF20BERA==",
"cpu": [
"x64"
],
@@ -609,9 +612,9 @@
}
},
"node_modules/@cloudflare/workerd-darwin-arm64": {
- "version": "1.20260826.1",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260826.1.tgz",
- "integrity": "sha512-0bLqVQYsQ3v3FdYGmzh23vi9fJeYTBx19o4LUySIsRcgBggGSlR39ml162vTXvZzUISOjVW2qfL7Y+SMrrfXmQ==",
+ "version": "1.20260911.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260911.1.tgz",
+ "integrity": "sha512-WU4bFqEN0H7ndGWxoedegv95DmNVBtv0ncXcHG9nYFTUI78sxEb0qoT3U6Ga4hyBkzsJFBX/zvVBIGX3qKldGA==",
"cpu": [
"arm64"
],
@@ -625,9 +628,9 @@
}
},
"node_modules/@cloudflare/workerd-linux-64": {
- "version": "1.20260826.1",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260826.1.tgz",
- "integrity": "sha512-DTC0yWzybX4gUH5Q1pJo3UwEQjp0Gmz0Q71I+39xT9SXBesX5QndOIQv45yHQ86Z84EzK2WwaENdlpmDSvJKmw==",
+ "version": "1.20260911.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260911.1.tgz",
+ "integrity": "sha512-0Y2gy62oxQxWa38qinSPE6zNL5+JmumJtDY9AWW1HB8KHuATxN71o5MGzmVFfB8PwZsiHfUd2Sv7O22krCOrhw==",
"cpu": [
"x64"
],
@@ -641,9 +644,9 @@
}
},
"node_modules/@cloudflare/workerd-linux-arm64": {
- "version": "1.20260826.1",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260826.1.tgz",
- "integrity": "sha512-PFerWi+DP2Ckc6eATAS4dhotBt8IeXJjTzIgy18H+uqsswlXc7A8HsnAunHL9v/7/BjCgq46iMo2g4iUAsEpDw==",
+ "version": "1.20260911.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260911.1.tgz",
+ "integrity": "sha512-kttNPnx1r2lCqFUoMH62z7CqGV+j4QBbw5fdtaz4pzOrzBv0AWkNATt7onFUe+SwP8zhcepMtbm2F4kKzTf6VA==",
"cpu": [
"arm64"
],
@@ -657,9 +660,9 @@
}
},
"node_modules/@cloudflare/workerd-windows-64": {
- "version": "1.20260826.1",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260826.1.tgz",
- "integrity": "sha512-X26hulrG2MSSfpRmjZbCq98LNaZnrRqbmGcgo7g1U/U0nJ5npYruPm3fAyZ2y6VDr5CmQo9BLCahxP8VB/QR6A==",
+ "version": "1.20260911.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260911.1.tgz",
+ "integrity": "sha512-5iO/YfoBDOgO3CrHdkiiVP8SL3O2jC+c6Ux3d378TSPKLhU5+CgHjtE/ZSodWQrzr4FzFRqdW8S7n5nbyD1MHQ==",
"cpu": [
"x64"
],
@@ -2783,6 +2786,13 @@
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
"license": "CC0-1.0"
},
+ "node_modules/cssom": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
+ "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/defu": {
"version": "6.1.7",
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
@@ -3243,6 +3253,39 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/htmlparser2": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
+ "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.2.2",
+ "entities": "^7.0.1"
+ }
+ },
+ "node_modules/htmlparser2/node_modules/entities": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/http-cache-semantics": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
@@ -3577,6 +3620,181 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/linkedom": {
+ "version": "0.18.13",
+ "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.13.tgz",
+ "integrity": "sha512-ES/o9qotMpzpN2MHs+Iq/JcVoOj8Fa5wiQYrTdFpvAnwXL0g66XHHUc9WUMk6nAlBtGsFQ24ne+SYnvnaQ2FSw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "css-select": "^7.0.0",
+ "cssom": "^0.5.0",
+ "html-escaper": "^3.0.3",
+ "htmlparser2": "^10.1.0",
+ "uhyphen": "^0.2.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "canvas": ">= 2"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/linkedom/node_modules/boolbase": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-2.0.0.tgz",
+ "integrity": "sha512-DkVaaQHymRhpYEYo9x1oo7Q7B0Y6KJUsjm3c9eTyFDby4MHLBTwZ6ZDWBel5zrYxj1WsZgC5oLpiz+93MluXeA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/linkedom/node_modules/css-select": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-7.0.0.tgz",
+ "integrity": "sha512-snmjEVXy+1LnwXdxhYvTMj1d9tOh4HxkA1YmoayVBeeyR2C14Pum7fcxJIm4SswYspVy866eYNwlH6xC3/VH5g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^2.0.0",
+ "css-what": "^8.0.0",
+ "domhandler": "^6.0.1",
+ "domutils": "^4.0.2",
+ "nth-check": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/linkedom/node_modules/css-what": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-8.0.0.tgz",
+ "integrity": "sha512-DH0Bqq3DNp5tdOReuNyAA+Ev4Y2GS5FMbZpeTLP6C4CDi0h5nL0BmUPChXw3o/qbHLDWHl49sbNqQVY7bMSDdw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/linkedom/node_modules/dom-serializer": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-3.1.1.tgz",
+ "integrity": "sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^3.0.0",
+ "domhandler": "^6.0.0",
+ "entities": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/linkedom/node_modules/domelementtype": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz",
+ "integrity": "sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20.19.0"
+ }
+ },
+ "node_modules/linkedom/node_modules/domhandler": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz",
+ "integrity": "sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/linkedom/node_modules/domutils": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-4.0.2.tgz",
+ "integrity": "sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^3.0.0",
+ "domelementtype": "^3.0.0",
+ "domhandler": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/linkedom/node_modules/entities": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz",
+ "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/linkedom/node_modules/nth-check": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-3.0.1.tgz",
+ "integrity": "sha512-GX0gsdbGVCgnRgbeGaubfjpBXyYRWOOCVeYh08bSQvDZqxz5ndXs1OTfAt/h36G1xvI94YIspsI0sVFqAV9+RQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
"node_modules/lru-cache": {
"version": "11.5.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz",
@@ -3723,15 +3941,15 @@
"license": "MIT"
},
"node_modules/miniflare": {
- "version": "5.20260826.0-alpha",
- "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20260826.0-alpha.tgz",
- "integrity": "sha512-ZXR3Bieg+B5MK0T/zYIWaZiCGCb9Z3en4+/TleYKhIGPLx/e0cRcG/ZvvTviUapVBBK2zODB+aiypdIojU3x6Q==",
+ "version": "5.20260911.0-alpha",
+ "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20260911.0-alpha.tgz",
+ "integrity": "sha512-CRieJmvHx+7rNqnA5SKdsYsER6rfkUIE/jruIUw+fLhsQ4sORfuMtr3+FQzsQ9/y8lhk061V4Fl1DFdHiyBB6g==",
"license": "MIT",
"dependencies": {
"@cspotcode/source-map-support": "0.8.1",
- "sharp": "0.35.2",
+ "sharp": "0.35.4",
"undici": "7.29.0",
- "workerd": "1.20260826.1",
+ "workerd": "1.20260911.1",
"ws": "8.21.0",
"youch": "4.1.0-beta.10"
},
@@ -3739,610 +3957,75 @@
"node": ">=22.0.0"
}
},
- "node_modules/miniflare/node_modules/@img/sharp-darwin-arm64": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.2.tgz",
- "integrity": "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
+ "node_modules/miniflare/node_modules/undici": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
+ "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
+ "license": "MIT",
"engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-darwin-arm64": "1.3.1"
+ "node": ">=20.18.1"
}
},
- "node_modules/miniflare/node_modules/@img/sharp-darwin-x64": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.2.tgz",
- "integrity": "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
+ "node_modules/mrmime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+ "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
+ "license": "MIT",
"engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-darwin-x64": "1.3.1"
+ "node": ">=10"
}
},
- "node_modules/miniflare/node_modules/@img/sharp-freebsd-wasm32": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.2.tgz",
- "integrity": "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==",
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "freebsd"
+ "node_modules/muggle-string": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
+ "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.18",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
+ "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
],
- "dependencies": {
- "@img/sharp-wasm32": "0.35.2"
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
},
"engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
- "node_modules/miniflare/node_modules/@img/sharp-libvips-darwin-arm64": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.1.tgz",
- "integrity": "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==",
- "cpu": [
- "arm64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "darwin"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
+ "node_modules/neotraverse": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-1.0.1.tgz",
+ "integrity": "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
}
},
- "node_modules/miniflare/node_modules/@img/sharp-libvips-darwin-x64": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.1.tgz",
- "integrity": "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==",
- "cpu": [
- "x64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "darwin"
- ],
+ "node_modules/nlcst-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz",
+ "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/nlcst": "^2.0.0"
+ },
"funding": {
- "url": "https://opencollective.com/libvips"
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "node_modules/miniflare/node_modules/@img/sharp-libvips-linux-arm": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.1.tgz",
- "integrity": "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==",
- "cpu": [
- "arm"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-libvips-linux-arm64": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.1.tgz",
- "integrity": "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==",
- "cpu": [
- "arm64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-libvips-linux-ppc64": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.1.tgz",
- "integrity": "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==",
- "cpu": [
- "ppc64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-libvips-linux-riscv64": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.1.tgz",
- "integrity": "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==",
- "cpu": [
- "riscv64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-libvips-linux-s390x": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.1.tgz",
- "integrity": "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==",
- "cpu": [
- "s390x"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-libvips-linux-x64": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.1.tgz",
- "integrity": "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==",
- "cpu": [
- "x64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-libvips-linuxmusl-arm64": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.1.tgz",
- "integrity": "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==",
- "cpu": [
- "arm64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-libvips-linuxmusl-x64": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.1.tgz",
- "integrity": "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==",
- "cpu": [
- "x64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-linux-arm": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.2.tgz",
- "integrity": "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==",
- "cpu": [
- "arm"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-arm": "1.3.1"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-linux-arm64": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.2.tgz",
- "integrity": "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-arm64": "1.3.1"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-linux-ppc64": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.2.tgz",
- "integrity": "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==",
- "cpu": [
- "ppc64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-ppc64": "1.3.1"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-linux-riscv64": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.2.tgz",
- "integrity": "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==",
- "cpu": [
- "riscv64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-riscv64": "1.3.1"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-linux-s390x": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.2.tgz",
- "integrity": "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==",
- "cpu": [
- "s390x"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-s390x": "1.3.1"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-linux-x64": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.2.tgz",
- "integrity": "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-x64": "1.3.1"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-linuxmusl-arm64": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.2.tgz",
- "integrity": "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-arm64": "1.3.1"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-linuxmusl-x64": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.2.tgz",
- "integrity": "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-x64": "1.3.1"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-wasm32": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.2.tgz",
- "integrity": "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==",
- "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
- "optional": true,
- "dependencies": {
- "@emnapi/runtime": "^1.11.1"
- },
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-webcontainers-wasm32": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.2.tgz",
- "integrity": "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==",
- "cpu": [
- "wasm32"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "@img/sharp-wasm32": "0.35.2"
- },
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-win32-arm64": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.2.tgz",
- "integrity": "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-win32-ia32": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.2.tgz",
- "integrity": "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==",
- "cpu": [
- "ia32"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": "^20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/@img/sharp-win32-x64": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.2.tgz",
- "integrity": "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/miniflare/node_modules/sharp": {
- "version": "0.35.2",
- "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.2.tgz",
- "integrity": "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==",
- "license": "Apache-2.0",
- "dependencies": {
- "@img/colour": "^1.1.0",
- "detect-libc": "^2.1.2",
- "semver": "^7.8.4"
- },
- "engines": {
- "node": ">=20.9.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-darwin-arm64": "0.35.2",
- "@img/sharp-darwin-x64": "0.35.2",
- "@img/sharp-freebsd-wasm32": "0.35.2",
- "@img/sharp-libvips-darwin-arm64": "1.3.1",
- "@img/sharp-libvips-darwin-x64": "1.3.1",
- "@img/sharp-libvips-linux-arm": "1.3.1",
- "@img/sharp-libvips-linux-arm64": "1.3.1",
- "@img/sharp-libvips-linux-ppc64": "1.3.1",
- "@img/sharp-libvips-linux-riscv64": "1.3.1",
- "@img/sharp-libvips-linux-s390x": "1.3.1",
- "@img/sharp-libvips-linux-x64": "1.3.1",
- "@img/sharp-libvips-linuxmusl-arm64": "1.3.1",
- "@img/sharp-libvips-linuxmusl-x64": "1.3.1",
- "@img/sharp-linux-arm": "0.35.2",
- "@img/sharp-linux-arm64": "0.35.2",
- "@img/sharp-linux-ppc64": "0.35.2",
- "@img/sharp-linux-riscv64": "0.35.2",
- "@img/sharp-linux-s390x": "0.35.2",
- "@img/sharp-linux-x64": "0.35.2",
- "@img/sharp-linuxmusl-arm64": "0.35.2",
- "@img/sharp-linuxmusl-x64": "0.35.2",
- "@img/sharp-webcontainers-wasm32": "0.35.2",
- "@img/sharp-win32-arm64": "0.35.2",
- "@img/sharp-win32-ia32": "0.35.2",
- "@img/sharp-win32-x64": "0.35.2"
- }
- },
- "node_modules/miniflare/node_modules/undici": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
- "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
- "license": "MIT",
- "engines": {
- "node": ">=20.18.1"
- }
- },
- "node_modules/mrmime": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
- "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/muggle-string": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
- "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
- "license": "MIT"
- },
- "node_modules/nanoid": {
- "version": "3.3.18",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
- "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
- "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/neotraverse": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-1.0.1.tgz",
- "integrity": "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==",
- "license": "MIT",
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/nlcst-to-string": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz",
- "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==",
- "license": "MIT",
- "dependencies": {
- "@types/nlcst": "^2.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/node-fetch-native": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
- "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
- "license": "MIT"
+ "node_modules/node-fetch-native": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
+ "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
+ "license": "MIT"
},
"node_modules/node-mock-http": {
"version": "1.0.5",
@@ -4744,7 +4427,6 @@
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz",
"integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==",
"license": "Apache-2.0",
- "optional": true,
"dependencies": {
"@img/colour": "^1.1.0",
"detect-libc": "^2.1.2",
@@ -5053,6 +4735,13 @@
"integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==",
"license": "MIT"
},
+ "node_modules/uhyphen": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz",
+ "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/ultrahtml": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz",
@@ -5698,9 +5387,9 @@
"license": "MIT"
},
"node_modules/workerd": {
- "version": "1.20260826.1",
- "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260826.1.tgz",
- "integrity": "sha512-oTG9ot5zxO9OjjKCskt86+nVrBL0kiUqExHnYaTg4OCkHf/K4zr+9hYvgwmG8DdCWG0TQGErHzD+1h50TM5b+w==",
+ "version": "1.20260911.1",
+ "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260911.1.tgz",
+ "integrity": "sha512-vRr8QdBxueQOZJO1hRCI73EZlix87IAyBAcSyI3rA1VB+6oxjw3oaqzYnIV8C4IOPtUgihbdMAgzkb5GM4V7DQ==",
"hasInstallScript": true,
"license": "Apache-2.0",
"bin": {
@@ -5710,27 +5399,27 @@
"node": ">=16"
},
"optionalDependencies": {
- "@cloudflare/workerd-darwin-64": "1.20260826.1",
- "@cloudflare/workerd-darwin-arm64": "1.20260826.1",
- "@cloudflare/workerd-linux-64": "1.20260826.1",
- "@cloudflare/workerd-linux-arm64": "1.20260826.1",
- "@cloudflare/workerd-windows-64": "1.20260826.1"
+ "@cloudflare/workerd-darwin-64": "1.20260911.1",
+ "@cloudflare/workerd-darwin-arm64": "1.20260911.1",
+ "@cloudflare/workerd-linux-64": "1.20260911.1",
+ "@cloudflare/workerd-linux-arm64": "1.20260911.1",
+ "@cloudflare/workerd-windows-64": "1.20260911.1"
}
},
"node_modules/wrangler": {
- "version": "4.127.0",
- "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.127.0.tgz",
- "integrity": "sha512-4dPqcBEMJfGeZeNnjHT7ThNJs+EiNYxUTg4ywqIdQubcXHBhFeVMQyHV4A9AOhZRFr83cckqdds034KGcr/dtw==",
+ "version": "4.131.1",
+ "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.131.1.tgz",
+ "integrity": "sha512-1u5FMdJAn6UOcL02cVsIITcnHrk6mC7N+RF10EkVhPL18R/o9g5BZb4PCjByL+3AsRP5wQpppCIPHhYPRmIJwg==",
"license": "MIT OR Apache-2.0",
"dependencies": {
"@cloudflare/kv-asset-handler": "0.5.0",
"@cloudflare/unenv-preset": "2.16.1",
"blake3-wasm": "2.1.5",
"esbuild": "0.28.1",
- "miniflare": "5.20260826.0-alpha",
+ "miniflare": "5.20260911.0-alpha",
"path-to-regexp": "6.3.0",
"unenv": "2.0.0-rc.24",
- "workerd": "1.20260826.1"
+ "workerd": "1.20260911.1"
},
"bin": {
"cf-wrangler": "bin/cf-wrangler.js",
@@ -5744,7 +5433,7 @@
"fsevents": "2.3.3"
},
"peerDependencies": {
- "@cloudflare/workers-types": "^5.20260826.1"
+ "@cloudflare/workers-types": "^5.20260911.1"
},
"peerDependenciesMeta": {
"@cloudflare/workers-types": {
diff --git a/package.json b/package.json
index a1aa995..1cc7414 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
"check": "astro check",
"build": "astro build",
"validate": "node scripts/validate.mjs",
+ "test": "node --test scripts/test-site.mjs",
"preview": "wrangler dev",
"deploy": "wrangler deploy"
},
@@ -16,5 +17,8 @@
"astro": "^7.2.8",
"typescript": "^5.9.3",
"wrangler": "^4.127.0"
+ },
+ "devDependencies": {
+ "linkedom": "^0.18.13"
}
}
diff --git a/public/favicon.svg b/public/favicon.svg
index 56859b2..502b0cc 100644
--- a/public/favicon.svg
+++ b/public/favicon.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/public/fonts/Archivo-LICENSE.txt b/public/fonts/Archivo-LICENSE.txt
new file mode 100644
index 0000000..78b7453
--- /dev/null
+++ b/public/fonts/Archivo-LICENSE.txt
@@ -0,0 +1,93 @@
+Copyright 2020 The Archivo Project Authors (https://github.com/Omnibus-Type/Archivo) Archivo-Italic[wdth,wght].ttf: Copyright 2020 The Archivo Project Authors (https://github.com/Omnibus-Type/Archivo)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/public/fonts/IBM-Plex-Mono-LICENSE.txt b/public/fonts/IBM-Plex-Mono-LICENSE.txt
new file mode 100644
index 0000000..83eac82
--- /dev/null
+++ b/public/fonts/IBM-Plex-Mono-LICENSE.txt
@@ -0,0 +1,93 @@
+Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-ThinItalic.ttf: Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-ExtraLight.ttf: Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-ExtraLightItalic.ttf: Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-Light.ttf: Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-LightItalic.ttf: Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-Regular.ttf: Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-Italic.ttf: Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-Medium.ttf: Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-MediumItalic.ttf: Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-SemiBold.ttf: Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-SemiBoldItalic.ttf: Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-Bold.ttf: Copyright 2017 IBM Corp. All rights reserved. IBMPlexMono-BoldItalic.ttf: Copyright 2017 IBM Corp. All rights reserved.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/public/fonts/README.md b/public/fonts/README.md
new file mode 100644
index 0000000..fbf40bf
--- /dev/null
+++ b/public/fonts/README.md
@@ -0,0 +1,8 @@
+# Website fonts
+
+Locally hosted Latin subsets from Fontsource, used under the SIL Open Font License.
+
+- Archivo variable, weights 100–900: `@fontsource-variable/archivo` 5.3.0. See `Archivo-LICENSE.txt`.
+- IBM Plex Mono, regular 400: `@fontsource/ibm-plex-mono` 5.3.0. See `IBM-Plex-Mono-LICENSE.txt`.
+
+No third-party font requests are made. Archivo is preloaded; both families use `font-display: swap`.
diff --git a/public/fonts/archivo-latin-wght-normal.woff2 b/public/fonts/archivo-latin-wght-normal.woff2
new file mode 100644
index 0000000..716d4ab
Binary files /dev/null and b/public/fonts/archivo-latin-wght-normal.woff2 differ
diff --git a/public/fonts/ibm-plex-mono-latin-400-normal.woff2 b/public/fonts/ibm-plex-mono-latin-400-normal.woff2
new file mode 100644
index 0000000..0804aaf
Binary files /dev/null and b/public/fonts/ibm-plex-mono-latin-400-normal.woff2 differ
diff --git a/public/lab.js b/public/lab.js
index 64dfc96..0198d49 100644
--- a/public/lab.js
+++ b/public/lab.js
@@ -4,3 +4,79 @@ const supportThanks = document.querySelector('#support-thanks');
if (supportThanks && params.get('support') === 'thanks') {
supportThanks.hidden = false;
}
+
+const header = document.querySelector('.site-header');
+const menuToggle = header?.querySelector('.menu-toggle');
+const navigation = header?.querySelector('#primary-navigation');
+
+if (header && menuToggle && navigation) {
+ header.setAttribute('data-enhanced', '');
+ menuToggle.hidden = false;
+ const closeMenu = () => {
+ menuToggle.setAttribute('aria-expanded', 'false');
+ navigation.removeAttribute('data-open');
+ };
+ menuToggle.addEventListener('click', () => {
+ const open = menuToggle.getAttribute('aria-expanded') !== 'true';
+ menuToggle.setAttribute('aria-expanded', String(open));
+ navigation.toggleAttribute('data-open', open);
+ });
+ navigation.addEventListener('click', (event) => {
+ if (event.target.closest('a')) closeMenu();
+ });
+ header.addEventListener('keydown', (event) => {
+ if (
+ event.key === 'Escape' &&
+ menuToggle.getAttribute('aria-expanded') === 'true'
+ ) {
+ closeMenu();
+ menuToggle.focus();
+ }
+ });
+ window.matchMedia('(max-width: 800px)').addEventListener('change', closeMenu);
+}
+
+const explorer = document.querySelector('[data-research-explorer]');
+if (explorer) {
+ const selector = explorer.querySelector('.track-selector');
+ const tabs = [...explorer.querySelectorAll('[role="tab"]')];
+ const panels = [...explorer.querySelectorAll('[data-track-panel]')];
+ const select = (index) => {
+ tabs.forEach((tab, i) => {
+ tab.setAttribute('aria-selected', String(i === index));
+ tab.tabIndex = i === index ? 0 : -1;
+ panels[i].hidden = i !== index;
+ });
+ };
+ // Enhance the fully readable static sections only after all controls exist.
+ if (selector && tabs.length === panels.length && tabs.length) {
+ panels.forEach((panel, index) => {
+ panel.setAttribute('role', 'tabpanel');
+ panel.setAttribute('aria-labelledby', tabs[index].id);
+ panel.tabIndex = 0;
+ });
+ tabs.forEach((tab, index) => {
+ tab.addEventListener('click', () => select(index));
+ tab.addEventListener('keydown', (event) => {
+ let next;
+ if (event.key === 'ArrowDown' || event.key === 'ArrowRight')
+ next = (index + 1) % tabs.length;
+ if (event.key === 'ArrowUp' || event.key === 'ArrowLeft')
+ next = (index - 1 + tabs.length) % tabs.length;
+ if (event.key === 'Home') next = 0;
+ if (event.key === 'End') next = tabs.length - 1;
+ if (next !== undefined) {
+ event.preventDefault();
+ select(next);
+ tabs[next].focus();
+ }
+ });
+ });
+ const initial = panels.findIndex(
+ (panel) => `#${panel.id}` === window.location.hash,
+ );
+ select(initial < 0 ? 0 : initial);
+ explorer.setAttribute('data-enhanced', '');
+ selector.hidden = false;
+ }
+}
diff --git a/public/social-card.png b/public/social-card.png
new file mode 100644
index 0000000..fbb1f61
Binary files /dev/null and b/public/social-card.png differ
diff --git a/public/social-card.svg b/public/social-card.svg
new file mode 100644
index 0000000..3a666e3
--- /dev/null
+++ b/public/social-card.svg
@@ -0,0 +1 @@
+arca Open Hardware Lab INDEPENDENT RESEARCH. REAL MACHINES. Hardware, in the open. What works. What breaks. What changes. SANTIAGO, CHILE / NODE 001 ohl.arca.computer Exploded view of a computer assembly A custom technical illustration separating the heatsink, circuit board and chassis to reveal what is inside. This is a concept drawing, not a tested lab unit. 01 / THERMALS 02 / COMPUTE 03 / BUILD
\ No newline at end of file
diff --git a/scripts/test-site.mjs b/scripts/test-site.mjs
new file mode 100644
index 0000000..3d4484e
--- /dev/null
+++ b/scripts/test-site.mjs
@@ -0,0 +1,163 @@
+import assert from 'node:assert/strict';
+import { readFile, stat } from 'node:fs/promises';
+import { resolve } from 'node:path';
+import { test } from 'node:test';
+import vm from 'node:vm';
+import { parseHTML } from 'linkedom';
+
+const root = resolve(import.meta.dirname, '..');
+const html = await readFile(resolve(root, 'dist/index.html'), 'utf8');
+const script = await readFile(resolve(root, 'dist/lab.js'), 'utf8');
+
+function page(url = 'https://ohl.arca.computer/', source = html) {
+ const { window, document } = parseHTML(source);
+ window.location = new URL(url);
+ window.matchMedia = () => ({ addEventListener() {} });
+ vm.runInNewContext(script, { window, document, URLSearchParams });
+ return { window, document };
+}
+
+function key(window, target, value) {
+ const event = new window.Event('keydown', {
+ bubbles: true,
+ cancelable: true,
+ });
+ Object.defineProperty(event, 'key', { value });
+ target.dispatchEvent(event);
+ return event;
+}
+
+function assertSelected(document, expected) {
+ const tabs = [...document.querySelectorAll('[role="tab"]')];
+ const panels = [...document.querySelectorAll('[role="tabpanel"]')];
+ assert.equal(
+ tabs.filter((tab) => tab.getAttribute('aria-selected') === 'true').length,
+ 1,
+ );
+ assert.equal(
+ tabs.filter((tab) => tab.getAttribute('tabindex') === '0').length,
+ 1,
+ );
+ assert.equal(tabs[expected].getAttribute('aria-selected'), 'true');
+ assert.equal(panels.filter((panel) => !panel.hidden).length, 1);
+ assert.equal(panels[expected].hidden, false);
+ assert.equal(
+ panels[expected].getAttribute('aria-labelledby'),
+ tabs[expected].id,
+ );
+}
+
+test('all research remains readable and navigation available without JavaScript', () => {
+ const { document } = parseHTML(html);
+ const panels = [...document.querySelectorAll('[data-track-panel]')];
+ assert.equal(panels.length, 4);
+ assert.ok(
+ panels.every((panel) => !panel.hidden && panel.querySelector('h3')),
+ );
+ assert.ok(document.querySelector('.track-selector').hidden);
+ assert.ok(document.querySelector('.menu-toggle').hidden);
+ assert.equal(
+ document.querySelector('.site-header').hasAttribute('data-enhanced'),
+ false,
+ );
+ assert.equal(document.querySelector('#primary-navigation').hidden, false);
+});
+
+test('track selection keeps exactly one selected tab, tab stop and visible panel', () => {
+ const { document } = page();
+ const tabs = [...document.querySelectorAll('[role="tab"]')];
+ assertSelected(document, 0);
+ for (const index of [2, 1, 3, 0]) {
+ tabs[index].click();
+ assertSelected(document, index);
+ }
+});
+
+test('track keyboard controls wrap and support Home and End', () => {
+ const { document, window } = page();
+ const tabs = [...document.querySelectorAll('[role="tab"]')];
+ assert.equal(key(window, tabs[0], 'ArrowUp').defaultPrevented, true);
+ assertSelected(document, 3);
+ key(window, tabs[3], 'ArrowDown');
+ assertSelected(document, 0);
+ key(window, tabs[0], 'End');
+ assertSelected(document, 3);
+ key(window, tabs[3], 'Home');
+ assertSelected(document, 0);
+ key(window, tabs[0], 'ArrowRight');
+ assertSelected(document, 1);
+ key(window, tabs[1], 'ArrowLeft');
+ assertSelected(document, 0);
+ assert.equal(key(window, tabs[0], 'Tab').defaultPrevented, false);
+});
+
+test('an incoming research fragment opens the requested panel', () => {
+ const { document } = page('https://ohl.arca.computer/#track-agents');
+ assertSelected(document, 2);
+});
+
+test('menu expands, closes on navigation and dismisses with Escape', () => {
+ const { document, window } = page();
+ const button = document.querySelector('.menu-toggle');
+ const nav = document.querySelector('#primary-navigation');
+ assert.equal(button.hidden, false);
+ assert.equal(button.getAttribute('aria-expanded'), 'false');
+ button.click();
+ assert.equal(button.getAttribute('aria-expanded'), 'true');
+ assert.equal(nav.hasAttribute('data-open'), true);
+ nav.querySelector('a').click();
+ assert.equal(button.getAttribute('aria-expanded'), 'false');
+ assert.equal(nav.hasAttribute('data-open'), false);
+ button.click();
+ key(window, nav, 'Escape');
+ assert.equal(button.getAttribute('aria-expanded'), 'false');
+ assert.equal(nav.hasAttribute('data-open'), false);
+});
+
+test('support return message is conditional and does not assert payment verification', () => {
+ assert.equal(page().document.querySelector('#support-thanks').hidden, true);
+ assert.equal(
+ page('https://ohl.arca.computer/?support=no').document.querySelector(
+ '#support-thanks',
+ ).hidden,
+ true,
+ );
+ const thanks = page(
+ 'https://ohl.arca.computer/?support=thanks',
+ ).document.querySelector('#support-thanks');
+ assert.equal(thanks.hidden, false);
+ assert.match(thanks.textContent, /If your payment completed/);
+ assert.doesNotMatch(thanks.textContent, /Support received/);
+});
+
+for (const route of ['index.html', 'support-terms/index.html', '404.html']) {
+ test(`${route}: document landmarks, unique IDs and local assets are intact`, async () => {
+ const source = await readFile(resolve(root, 'dist', route), 'utf8');
+ const { document } = page('https://ohl.arca.computer/', source);
+ assert.equal(document.querySelectorAll('main').length, 1);
+ assert.equal(document.querySelectorAll('h1').length, 1);
+ assert.equal(document.documentElement.lang, 'en');
+ assert.ok(document.querySelector('#main-content'));
+ assert.equal(
+ document.querySelector('.skip-link').getAttribute('href'),
+ '#main-content',
+ );
+ const ids = [...document.querySelectorAll('[id]')].map(
+ (element) => element.id,
+ );
+ assert.equal(new Set(ids).size, ids.length, 'IDs must be unique');
+ for (const anchor of document.querySelectorAll('a[href^="#"]')) {
+ assert.ok(
+ document.getElementById(anchor.getAttribute('href').slice(1)),
+ `Missing anchor: ${anchor.getAttribute('href')}`,
+ );
+ }
+ for (const asset of document.querySelectorAll(
+ 'script[src], link[rel="stylesheet"], link[rel="preload"], link[rel="icon"]',
+ )) {
+ const path = asset.getAttribute('src') || asset.getAttribute('href');
+ assert.ok(path.startsWith('/'), `Asset must be hosted locally: ${path}`);
+ assert.ok((await stat(resolve(root, 'dist', path.slice(1)))).size > 0);
+ }
+ });
+}
diff --git a/src/components/Arrow.astro b/src/components/Arrow.astro
new file mode 100644
index 0000000..d3da58a
--- /dev/null
+++ b/src/components/Arrow.astro
@@ -0,0 +1,22 @@
+---
+interface Props {
+ diagonal?: boolean;
+ class?: string;
+}
+const { diagonal = false, class: className = '' } = Astro.props;
+---
+
+
+ {diagonal ? (
+
+ ) : (
+
+ )}
+
diff --git a/src/components/Footer.astro b/src/components/Footer.astro
new file mode 100644
index 0000000..817309b
--- /dev/null
+++ b/src/components/Footer.astro
@@ -0,0 +1,34 @@
+---
+import Arrow from './Arrow.astro';
+---
+
+
diff --git a/src/components/HardwareDiagram.astro b/src/components/HardwareDiagram.astro
new file mode 100644
index 0000000..8572c8d
--- /dev/null
+++ b/src/components/HardwareDiagram.astro
@@ -0,0 +1,297 @@
+---
+const pins = Array.from({ length: 12 }, (_, i) => i);
+const fins = Array.from({ length: 15 }, (_, i) => i);
+---
+
+
diff --git a/src/components/Header.astro b/src/components/Header.astro
new file mode 100644
index 0000000..f9daa75
--- /dev/null
+++ b/src/components/Header.astro
@@ -0,0 +1,46 @@
+---
+import Arrow from './Arrow.astro';
+const home = Astro.url.pathname === '/' ? '' : '/';
+---
+
+
diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro
index 5ff3b7b..e125d56 100644
--- a/src/layouts/Layout.astro
+++ b/src/layouts/Layout.astro
@@ -1,26 +1,52 @@
---
import '../styles/global.css';
-interface Props { title: string; description: string; }
+interface Props {
+ title: string;
+ description: string;
+}
const { title, description } = Astro.props;
const canonical = new URL(Astro.url.pathname, Astro.site);
+const socialImage = new URL('/social-card.png', Astro.site);
---
+
-
+
+
-
+
+
+
+
+
+
{title}
-
+
+
+ Skip to content
+
+
+
+
diff --git a/src/pages/404.astro b/src/pages/404.astro
index d8920d9..291bbb5 100644
--- a/src/pages/404.astro
+++ b/src/pages/404.astro
@@ -1,11 +1,21 @@
---
import Layout from '../layouts/Layout.astro';
+import Header from '../components/Header.astro';
+import Footer from '../components/Footer.astro';
---
-
-
- 404 · Wrong branch
+
+
+
+
+ 404 · Wrong branch
Nothing compiled here.
The page you requested is not part of this build.
- Return to the lab
+
+ Return to the lab
+
+
diff --git a/src/pages/index.astro b/src/pages/index.astro
index a638fa2..2a7b1f7 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -1,218 +1,673 @@
---
import Layout from '../layouts/Layout.astro';
-const title = 'Arca Open Hardware Lab | Open compatibility research';
-const description = 'An independent hardware compatibility lab based in Santiago. We register every unit, run reproducible tests, publish evidence and send failures upstream.';
+import Header from '../components/Header.astro';
+import Footer from '../components/Footer.astro';
+import Arrow from '../components/Arrow.astro';
+import HardwareDiagram from '../components/HardwareDiagram.astro';
+const title = 'Arca Open Hardware Lab | Hardware, in the open.';
+const description =
+ 'Independent hardware compatibility research in Santiago, Chile. Real machines, reproducible methods, public evidence. Hardware intake is open.';
const repo = 'https://github.com/arcacomputer/open-hardware-lab';
const methodology = `${repo}/tree/main/methodology`;
const inventory = `${repo}/tree/main/hardware`;
const ledger = `${repo}/tree/main/ledger`;
-const contact = 'ohl@arca.computer';
-const intakeEmail = `mailto:${contact}?subject=Open%20Hardware%20Lab%20intake`;
+const intakeEmail =
+ 'mailto:ohl@arca.computer?subject=Open%20Hardware%20Lab%20intake';
const supportUrl = 'https://buy.stripe.com/aFacN41Lw5oH1BH7G57ss03';
+const tracks = [
+ {
+ id: 'omarchy',
+ number: '01',
+ title: 'Omarchy compatibility',
+ short: 'Omarchy',
+ question: 'A clean install is only the beginning.',
+ description:
+ 'What happens when a real machine becomes a daily driver? Follow installation, suspend, graphics, media and updates through repeatable runs.',
+ categories: [
+ 'Installation & boot',
+ 'Suspend & resume',
+ 'Graphics & displays',
+ 'Media & connectivity',
+ ],
+ symbol: 'terminal',
+ note: 'From first boot to everyday use.',
+ },
+ {
+ id: 'linux',
+ number: '02',
+ title: 'Retail Linux hardware',
+ short: 'Retail Linux',
+ question: 'Same model. Different machine?',
+ description:
+ 'A model name rarely tells the whole story. Examine exact retail configurations, regional SKUs and the components people can actually buy.',
+ categories: [
+ 'Exact configuration',
+ 'Regional components',
+ 'Input & connectivity',
+ 'Firmware & kernel',
+ ],
+ symbol: 'board',
+ note: 'The configuration is the evidence.',
+ },
+ {
+ id: 'agents',
+ number: '03',
+ title: 'Agent workstations',
+ short: 'Agent workloads',
+ question: 'Does it hold up when the work gets real?',
+ description:
+ 'Investigate containers, hosted-agent workflows, local inference where practical, and sustained developer use with bounded, documented workloads.',
+ categories: [
+ 'Containers & virtualization',
+ 'Sustained thermals',
+ 'Power behavior',
+ 'Developer workloads',
+ ],
+ symbol: 'compute',
+ note: 'Useful workloads. Documented limits.',
+ },
+ {
+ id: 'regressions',
+ number: '04',
+ title: 'Regression watch',
+ short: 'Regression watch',
+ question: 'It worked yesterday. What changed?',
+ description:
+ 'Keep the hardware in the loop. Repeat runs after firmware, kernel and operating-system updates to make improvements and regressions visible.',
+ categories: [
+ 'Firmware changes',
+ 'Kernel updates',
+ 'Repeated test runs',
+ 'Upstream issue history',
+ ],
+ symbol: 'cycle',
+ note: 'A longer life for every test result.',
+ },
+];
+const stages = [
+ {
+ number: '01',
+ label: 'UNIT',
+ title: 'Know the machine.',
+ text: 'Register an OHL ID, exact configuration, regional SKU, source, ownership and provider relationship.',
+ },
+ {
+ number: '02',
+ label: 'RUN',
+ title: 'Ask a useful question.',
+ text: 'Choose an operating system and a bounded test. Record the method, versions and conditions.',
+ },
+ {
+ number: '03',
+ label: 'REPORT',
+ title: 'Show the evidence.',
+ text: 'Publish privacy-scrubbed observations, failures, workarounds, expenses and upstream links.',
+ },
+ {
+ number: '04',
+ label: 'RETEST',
+ title: 'Keep asking.',
+ text: 'Run it again after firmware, kernel and operating-system changes. A result is a record, not a lifetime verdict.',
+ },
+];
---
-
-
-
-
-
-
Hardware intake open · Santiago, Chile
-
Test locally.Publish globally.
-
An independent compatibility lab that works with the hardware it can obtain, asks useful questions and publishes the unit, relationship, method, expense, failure and upstream outcome.
+
+
+
+
+
+
+
+ +
+ {' '}
+ Independent research. Real machines.
+
+
+ Hardware,
+
+ in the open.
+
+
+
+ What works. What breaks. What changes.
+
+ We test hardware in Santiago and publish the evidence for everyone.
+
-
-
-
-
- $ ohl status --node santiago-001
- intake open
- public_units 0
- active_runs 0
- reports 0
- → queue: omarchy · linux · agents
- → evidence before verdict
-
+
+ SANTIAGO, CHILE{' '}
+ / NODE 001
-
RESULTS STAY PUBLIC
+
-
-
OPEN PROVENANCE REPRODUCIBLE RUNS OPEN EXPENSES UPSTREAM FIRST INDEPENDENT RESULTS OPEN PROVENANCE REPRODUCIBLE RUNS OPEN EXPENSES UPSTREAM FIRST INDEPENDENT RESULTS
-
-
-
-
What “open” means here
-
OHL does not claim that every computer it tests is open hardware. The methodology, provenance, disclosures, evidence, expense records and upstream work are open. Physical tests happen in Santiago today; the results are designed to be reproduced anywhere.
+
+
+
+
+
+
Hardware intake open
+
Building the public inventory.
+
+
+
+
+
+
+
+
Public units
+ 00
+
+
+
Active runs
+ 00
+
+
+
Published reports
+ 00
+
+
+
+ View the register
+
-
-
-
-
Live operating state
The lab starts empty, not imaginary.
-
OHL grows from hardware accepted, money actually available and questions worth answering. Empty states remain visible until evidence replaces them.
-
-
-
OPEN Hardware intake Proposals are reviewed before anything ships.
-
EMPTY Public inventory No physical unit has been accepted into the public register yet.
-
READY Baseline method Core test and evidence categories are published.
methodology
-
QUEUE Research questions Omarchy, retail Linux compatibility and agent workloads are open tracks.
+
+
+ 01 / THE PRINCIPLE
+
+ +
+
-
-
-
-
-
-
Inventory-driven research
Units, runs, reports, retests.
-
Campaigns are optional procurement tools, not the lab’s skeleton. One machine can answer several questions across multiple operating systems and years.
-
-
-
Register the unit Assign an OHL ID and publish the exact configuration, regional SKU, source, ownership and provider relationship.
-
Choose the run Select an operating system and bounded research question based on public value, available funds and lab capacity.
-
Publish the report Release privacy-scrubbed evidence, observations, failures, workarounds, expenses and upstream links.
-
Retest over time Track what changes after firmware, kernel and operating-system updates instead of issuing one disposable verdict.
+
+
+ A machine is a
+ starting point.
+
+ Evidence is the work.
+
+
+
What “open” means here
+
+ The process is open: methodology, provenance, disclosures, evidence,
+ expenses and upstream work. We don’t claim that every computer we
+ test is open hardware.
+
+
+ One machine can answer many questions, across operating systems and
+ years. Test locally. Publish globally.
+
+
+ Read our independence policy
+
-
-
+
-
-
Inputs, not promises
Work with what is real.
-
The next run comes from the intersection of available hardware, available lab funds, useful research questions and enough capacity to document the work properly.
+
+ 02 / RESEARCH DIRECTIONS
+ FOUR OPEN TRACKS
-
-
- Hardware pool
- What the lab can obtain.
- Owned machines, local retail purchases, permanent placements and approved loaners all enter the same public inventory system.
- Exact unit and regional SKU Ownership and custody Provider relationship Return deadline when loaned
-
-
- Lab fund
- What the lab can afford.
- General support is pooled until a transparent allocation is made. A specific funding target is opened only when a particular purchase or study requires it.
- Gross support and processing fees Public allocations Retail invoices and logistics No hidden preorder obligations
-
+
+
+ Better questions.
+
+ Useful answers.
+
+
+ Compatibility goes beyond a spec sheet.
+
+ These are the questions shaping our queue.
+
+
+
+
+ {tracks.map((track, i) => (
+
+ {track.number}
+ {track.short}
+
+
+ ))}
+
+
+ {tracks.map((track) => (
+
+
+
+ {track.number} / {track.title}
+
+ IN THE QUEUE
+
+
+ {track.symbol === 'terminal' && (
+
+
+
+
+
+ )}
+ {track.symbol === 'board' && (
+
+
+
+
+
+
+ )}
+ {track.symbol === 'compute' && (
+
+
+
+
+
+ )}
+ {track.symbol === 'cycle' && (
+
+
+
+
+
+ )}
+
{track.note}
+
+ {track.question}
+ {track.description}
+
+ {track.categories.map((category) => (
+ {category}
+ ))}
+
+
+ ))}
+
+
+ TRACKS ARE RESEARCH DIRECTIONS. THEY DO NOT GUARANTEE A TEST, SCHEDULE
+ OR PUBLICATION.
+
-
-
-
-
Open research queue
Questions can share hardware.
-
Tracks organize ongoing questions. They do not lock a machine into one campaign or guarantee that every proposed run will happen.
+
+
-
-
01 Omarchy compatibility Installation, daily use, power, suspend, media, graphics and update behavior on real machines.
-
02 Retail Linux hardware Exact configurations people can actually buy, including regional component and warranty differences.
-
03 Agent workstations Containers, local inference where practical, hosted-agent workflows, thermals and sustained developer use.
-
04 Regression watch Repeatable retests after firmware, kernel and operating-system changes.
+
+
+ Every result
+
+ has a paper trail.
+
+
+ Units, runs, reports, retests.
+ A record you can follow. A method you can repeat.
+
-
-
-
-
-
-
Hardware intake
Give a machine a public job.
-
Do not ship first and negotiate later. Every unit needs written approval, declared ownership, workable logistics and permission to publish independent results.
-
-
-
01
Temporary loan A time-bounded research loan with arrival condition, disclosed ownership, return date and prepaid return logistics.
-
02
Permanent placement Ownership transfers to Arca so the machine can remain in the inventory for long-term regression testing.
-
03
Retail delivery A partner funds or supplies an exact unit through an approved retailer or distributor with clean warranty and customs handling.
-
-
-
Acceptance gate
Useful, documentable, unconditional.
-
Exact configuration known Research value is clear Shipping and return are solved No favorable-result condition
-
-
-
Want to offer hardware? Email the model, exact configuration, location, ownership proposal and shipping plan to {contact} . A suggestion does not guarantee an operating system, schedule, report or positive result.
-
Start intake
-
+
+ {stages.map((stage) => (
+
+
+ {stage.label}
+ {stage.title}
+ {stage.text}
+
+ ))}
+
+
-
-
-
-
-
Public record
Follow the unit to the outcome.
-
Every accepted unit connects provenance, custody, test runs, reports, expenses and upstream work. Nothing is promoted from empty to complete by vibes.
+
+
+
+
04 / THE PUBLIC RECORD
+
+ Nothing hidden.
+
+ Nothing invented.
+
+
+ The lab starts empty, not imaginary. No physical unit has been
+ accepted into the public register yet.
+
+
+ Hardware intake is open. The method is taking shape. This is where
+ the evidence will go.
+
+
+ Follow the work on GitHub
+
-
-
- Current Open Hardware Lab operating ledger
- State Record Evidence
+
+
+ OHL / REGISTER INDEX
+ PUBLIC
+
+
+
+ Current Open Hardware Lab records and status
+
+
+
+ Record
+ State
+
+ Source
+
+
+
- OPEN Hardware proposals accepted through written intake contact
- READY Baseline methodology and evidence rules source
- EMPTY No public hardware unit accepted yet inventory
- QUEUE Omarchy, retail Linux and agent workload tracks repository
- LIVE Unrestricted voluntary support is available ledger policy
+
+
+ Hardware inventoryProvenance & custody
+
+
+ Awaiting units
+
+
+
+
+
+
+
+
+
+ Baseline methodology
+ Test categories & evidence rules
+
+
+ Published
+
+
+
+
+
+
+
+
+
+ Reports & retests
+ Results, failures & upstream work
+
+
+ Awaiting runs
+
+
+
+
+
+
+
+
+
+ Lab ledgerSupport, expenses & allocations
+
+
+ Policy published
+
+
+
+
+
+
+
+
+ CURRENT PUBLISHED STATE · MAINTAINED IN THE REPOSITORY
+
-
-
-
-
Support received. Thank you.
-
Stripe has sent the receipt to the email used at checkout.
+
+
+ 05 / MAKE THE WORK POSSIBLE
+
+ +
+
-
-
Stripe-backed support
Fund the lab, not a verdict.
-
Choose a one-time amount in USD. Support joins the unallocated OHL pool until the lab publishes a specific hardware, logistics or research allocation.
+
+
+ Put hardware
+
+ to public use.
+
+
+ Have a machine worth investigating?
+
+ Help turn it into a reproducible record.
+
-
-
- You choose the amount
- $5+
- one-time support in USD
- General support is not a preorder, reservation, purchased test or guarantee that a particular machine, operating system or report will be selected.
-
-
-
Voluntary support
-
Back open evidence.
-
Your payment supports independent testing, public evidence, equipment and upstream work. It does not buy goods, services, attribution, editorial control or a favorable result.
-
- One-time, customer-chosen amount
- Processed and receipted by Stripe
- Recorded as support revenue by Arca Computer, Inc.
- Not a charitable contribution or tax-deductible
-
-
-
Integrity clause: support funds lab capacity, not a positive result, private user data, editorial control or a promised upstream merge.
+
+
+
Hardware pool
+
+ There’s more than
+
+ one way in.
+
+
+ Owned machines, approved loans, permanent placements and retail
+ purchases enter the same public inventory.
+
+
+ We select work from available hardware, funds, research value and
+ lab capacity. Campaigns are optional procurement tools.
+
+
+ Offer hardware
+
+
+
+
+ 01
+
+
Temporary loan
+
+ A bounded research loan with arrival condition, disclosed
+ ownership, return date and prepaid return logistics.
+
+
+
+
+
+ 02
+
+
Permanent placement
+
+ Ownership transfers to Arca Computer, Inc. The machine stays in
+ the inventory for long-term regression testing.
+
+
+
+
+
+ 03
+
+
Retail delivery
+
+ Fund or supply an exact unit through an approved retailer or
+ distributor, with warranty and customs responsibilities
+ resolved.
+
+
+
+
+
+
+
+ Before anything
+ ships
+
+
+ +
+
+
+
+
+ Get written intake approval first. Send the model, exact
+ configuration, location, ownership proposal and shipping plan to{' '}
+ ohl@arca.computer . International intake
+ must resolve shipping, customs, warranty and return
+ responsibilities; local distribution or DDP is preferred.
+
+
+ The unit must have clear research value, workable logistics and a
+ publicly disclosed relationship. No favorable-result or
+ editorial-control conditions are accepted. A suggestion does not
+ guarantee an operating system, schedule, report or positive result.
+
+
+
-
-
-
-
What stays open. Unit provenance and custody Methodology and test definitions Privacy-scrubbed evidence Expenses, failures and upstream work
-
What nobody can buy. A positive result A guaranteed test or deadline Editorial approval Removal of legitimate failures
+
+
+
+ Thank you for supporting open research.
+
+ If your payment completed, Stripe will send a receipt to the email
+ used at checkout.
+
+
+
+
+
Lab fund / voluntary support
+
+ Fund the work.
+
+ Free the findings.
+
+
+ Help cover equipment, testing and public evidence.
+
+ General support stays in the unallocated OHL pool until a specific
+ use is published.
+
+
+ Follow the money
+
+
+
+
ONE-TIME SUPPORT · USD
+
+ Your call↗
+
+
You choose the amount at checkout.
+
+ Support with Stripe
+
+
+ Read the support terms
+
+
+ Voluntary support to Arca Computer, Inc. Not a charitable
+ contribution or tax-deductible. No goods, services, reserved tests
+ or promised results.
+
+
+
+
+
+ ↗
+
+
+ Our independence isn’t for sale. No paid verdicts.
+ No editorial approval. No removal of legitimate failures. Support
+ buys lab capacity, never a conclusion.
+
+
+
+
+
-
-
-
-
+
diff --git a/src/pages/support-terms.astro b/src/pages/support-terms.astro
index 468f62e..1125806 100644
--- a/src/pages/support-terms.astro
+++ b/src/pages/support-terms.astro
@@ -1,65 +1,117 @@
---
import Layout from '../layouts/Layout.astro';
+import Header from '../components/Header.astro';
+import Footer from '../components/Footer.astro';
const title = 'Support terms | Arca Open Hardware Lab';
-const description = 'Terms for voluntary payments supporting the Arca Open Hardware Lab.';
+const description =
+ 'Terms for voluntary payments supporting the Arca Open Hardware Lab.';
const contact = 'ohl@arca.computer';
const supportUrl = 'https://buy.stripe.com/aFacN41Lw5oH1BH7G57ss03';
---
+
-
+
-
- Effective August 28, 2026
+
+ Effective August 28, 2026
Support terms.
- These terms apply to voluntary payments made through the Arca Open Hardware Lab Stripe Payment Link.
+
+ These terms apply to voluntary payments made through the Arca Open
+ Hardware Lab Stripe Payment Link.
+
Who receives the payment
- Payments are made to Arca Computer, Inc., a Delaware for-profit corporation operating the Arca Open Hardware Lab. The lab is not a charity or tax-exempt organization.
+
+ Payments are made to Arca Computer, Inc., a Delaware for-profit
+ corporation operating the Arca Open Hardware Lab. The lab is not a
+ charity or tax-exempt organization.
+
What support means
- Your payment supports independent hardware testing, public research evidence, lab equipment and upstream open-source work. It is voluntary and does not purchase hardware, services, access, attribution, advertising, editorial control, a favorable result or a promised upstream contribution.
+
+ Your payment supports independent hardware testing, public research
+ evidence, lab equipment and upstream open-source work. It is voluntary
+ and does not purchase hardware, services, access, attribution,
+ advertising, editorial control, a favorable result or a promised
+ upstream contribution.
+
No charitable deduction
- Payments are not charitable contributions and are not tax-deductible as charitable donations. Stripe's receipt confirms payment processing; it is not a charitable-donation receipt.
+
+ Payments are not charitable contributions and are not tax-deductible
+ as charitable donations. Stripe's receipt confirms payment processing;
+ it is not a charitable-donation receipt.
+
Amount and processing
- You choose a one-time amount in USD, subject to the limits displayed by Stripe. Stripe processes the payment and may offer payment methods based on your location and account. Stripe's own terms and privacy policy also apply to its checkout.
+
+ You choose a one-time amount in USD, subject to the limits displayed
+ by Stripe. Stripe processes the payment and may offer payment methods
+ based on your location and account. Stripe's own terms and privacy
+ policy also apply to its checkout.
+
No specific work guarantee
- Support joins the general OHL pool and does not reserve a product, operating system, test slot, publication date or report. The lab publishes allocations when money is assigned to particular hardware, logistics or research work.
+
+ Support joins the general OHL pool and does not reserve a product,
+ operating system, test slot, publication date or report. The lab
+ publishes allocations when money is assigned to particular hardware,
+ logistics or research work.
+
Refunds and payment errors
- Voluntary support payments are normally final. For duplicate charges, payment errors or suspected unauthorized use, email {contact} within 14 days. Nothing here limits rights that cannot legally be waived.
+
+ Voluntary support payments are normally final. For duplicate charges,
+ payment errors or suspected unauthorized use, email{' '}
+
+ {contact}
+ {' '}
+ within 14 days. Nothing here limits rights that cannot legally be
+ waived.
+
Independence
- Support never buys a positive verdict. Arca may publish failures, security-relevant findings, funding disclosures and conflicts of interest as part of the research record.
+
+ Support never buys a positive verdict. Arca may publish failures,
+ security-relevant findings, funding disclosures and conflicts of
+ interest as part of the research record.
+
Questions
- Contact {contact} before paying if you need clarification. These terms may change prospectively; the version shown when you pay applies to that payment.
+
+ Contact{' '}
+
+ {contact}
+ {' '}
+ before paying if you need clarification. These terms may change
+ prospectively; the version shown when you pay applies to that payment.
+
-
+
-
+
diff --git a/src/styles/global.css b/src/styles/global.css
index 00595f0..90b0d76 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -1,214 +1,2273 @@
+@font-face {
+ font-family: Archivo;
+ src: url('/fonts/archivo-latin-wght-normal.woff2') format('woff2');
+ font-style: normal;
+ font-weight: 100 900;
+ font-display: swap;
+}
+@font-face {
+ font-family: 'IBM Plex Mono';
+ src: url('/fonts/ibm-plex-mono-latin-400-normal.woff2') format('woff2');
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+}
:root {
- --ink: #171711;
- --paper: #f4f0e6;
- --paper-2: #ebe4d4;
- --acid: #dfff43;
- --blue: #3154c7;
- --orange: #f45b35;
- --muted: #686554;
- --line: rgba(23, 23, 17, .22);
- --shadow: 8px 8px 0 var(--ink);
- }
- * { box-sizing: border-box; }
- html { scroll-behavior: smooth; }
- body {
- margin: 0;
- color: var(--ink);
- background:
- radial-gradient(circle at 12% 10%, rgba(223,255,67,.35), transparent 26rem),
- linear-gradient(rgba(23,23,17,.045) 1px, transparent 1px),
- linear-gradient(90deg, rgba(23,23,17,.045) 1px, transparent 1px),
- var(--paper);
- background-size: auto, 24px 24px, 24px 24px, auto;
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
- line-height: 1.5;
- }
- a { color: inherit; }
- button, a { -webkit-tap-highlight-color: transparent; }
- .wrap { width: min(1120px, calc(100% - 32px)); margin: 0 auto; }
- .topbar {
- display: flex; justify-content: space-between; align-items: center;
- padding: 20px 0; border-bottom: 2px solid var(--ink);
- }
- .brand { display: flex; align-items: center; gap: 10px; font-weight: 900; letter-spacing: -.04em; text-decoration: none; }
- .mark { width: 24px; height: 24px; background: var(--acid); border: 2px solid var(--ink); box-shadow: 3px 3px 0 var(--ink); transform: rotate(-5deg); }
- .prototype { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; border: 1px solid var(--ink); padding: 5px 8px; background: var(--paper); }
- nav { display: flex; gap: 18px; font-size: 13px; }
- nav a { text-decoration: none; border-bottom: 1px solid transparent; }
- nav a:hover, nav a:focus-visible { border-color: var(--ink); }
-
- .hero { display: grid; grid-template-columns: 1.15fr .85fr; gap: 56px; padding: 78px 0 66px; align-items: center; }
- .eyebrow { display: inline-flex; align-items: center; gap: 8px; text-transform: uppercase; letter-spacing: .12em; font-weight: 800; font-size: 12px; }
- .dot { width: 9px; height: 9px; background: var(--orange); border-radius: 50%; animation: pulse 1.8s infinite; }
- @keyframes pulse { 50% { transform: scale(1.45); opacity: .55; } }
- h1 { margin: 22px 0 24px; max-width: 760px; font-family: Arial Black, Arial, sans-serif; font-size: clamp(52px, 7vw, 96px); letter-spacing: -.075em; line-height: .91; }
- h1 span { display: block; color: var(--blue); }
- .lead { max-width: 680px; font-size: clamp(17px, 2vw, 21px); line-height: 1.55; }
- .hero-actions { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 32px; }
- .button {
- appearance: none; border: 2px solid var(--ink); background: var(--acid); color: var(--ink);
- padding: 14px 18px; font: inherit; font-weight: 900; text-decoration: none; cursor: pointer;
- box-shadow: 5px 5px 0 var(--ink); transition: transform .12s, box-shadow .12s;
- }
- .button:hover { transform: translate(2px,2px); box-shadow: 3px 3px 0 var(--ink); }
- .button:focus-visible { outline: 4px solid var(--blue); outline-offset: 3px; }
- .button.secondary { background: var(--paper); }
-
- .machine { position: relative; min-height: 420px; display: grid; place-items: center; }
- .laptop-lid {
- width: min(100%, 430px); aspect-ratio: 1.33; border: 3px solid var(--ink); border-radius: 18px 18px 10px 10px;
- background: linear-gradient(145deg, #d9d8d2, #a9aaa8); box-shadow: 18px 18px 0 var(--ink); position: relative; overflow: hidden;
- }
- .laptop-lid::after { content: ""; position: absolute; inset: 13px; border: 1px solid rgba(255,255,255,.65); border-radius: 10px; pointer-events: none; }
- .terminal {
- position: absolute; inset: 12% 9%; background: #11130d; color: #dfff43; border: 2px solid var(--ink); padding: 22px;
- font-size: clamp(10px, 1.4vw, 14px); line-height: 1.65; transform: rotate(-2deg); box-shadow: 8px 8px 0 rgba(65,105,225,.9);
- }
- .terminal strong { color: white; }
- .terminal .muted-code { color: #8b9273; }
- .cursor { display: inline-block; width: 8px; height: 15px; background: var(--acid); vertical-align: -2px; animation: blink 1s steps(1) infinite; }
- @keyframes blink { 50% { opacity: 0; } }
- .stamp { position: absolute; right: -3%; bottom: 4%; z-index: 2; background: var(--orange); color: var(--ink); border: 3px solid var(--ink); padding: 10px 14px; font-weight: 900; transform: rotate(7deg); box-shadow: 5px 5px 0 var(--ink); }
-
- .strip { border-top: 2px solid var(--ink); border-bottom: 2px solid var(--ink); background: var(--acid); overflow: hidden; }
- .ticker { display: flex; width: max-content; gap: 36px; padding: 12px 0; font-weight: 900; text-transform: uppercase; animation: slide 24s linear infinite; }
- @keyframes slide { to { transform: translateX(-50%); } }
-
- section { padding: 78px 0; }
- .section-head { display: grid; grid-template-columns: .55fr 1fr; gap: 40px; margin-bottom: 38px; }
- .kicker { text-transform: uppercase; font-size: 12px; letter-spacing: .12em; font-weight: 900; color: var(--blue); }
- h2 { margin: 6px 0 0; font-family: Arial Black, Arial, sans-serif; font-size: clamp(34px, 5vw, 62px); line-height: .98; letter-spacing: -.055em; }
- .section-copy { margin: 0; max-width: 640px; font-size: 18px; }
-
- .definition { padding: 42px 0; background: var(--ink); color: var(--paper); border-bottom: 2px solid var(--ink); }
- .definition-grid { display: grid; grid-template-columns: .45fr 1fr; gap: 42px; align-items: start; }
- .definition .kicker { color: var(--acid); }
- .definition p { margin: 0; max-width: 800px; font-size: 18px; }
-
- .status-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
- .status-grid article { min-height: 220px; padding: 22px; border: 2px solid var(--ink); background: var(--paper); }
- .status-grid h3 { margin: 34px 0 8px; font-size: 20px; }
- .status-grid p { margin: 0; color: var(--muted); font-size: 14px; }
- .status-grid a { display: inline-block; margin-top: 14px; font-size: 13px; font-weight: 900; }
- .state { display: inline-block; padding: 4px 7px; border: 1px solid var(--ink); font-size: 11px; font-weight: 900; letter-spacing: .08em; }
- .state.open { background: var(--acid); }
- .state.empty { background: var(--paper-2); }
- .state.ready { background: var(--blue); color: white; }
- .state.queue { background: var(--orange); color: var(--ink); }
-
- .flow { display: grid; grid-template-columns: repeat(4,1fr); gap: 16px; counter-reset: flow; }
- .flow article { counter-increment: flow; border: 2px solid var(--ink); background: var(--paper); padding: 22px; min-height: 220px; position: relative; }
- .flow article::before { content: "0" counter(flow); position: absolute; top: 14px; right: 16px; font-weight: 900; color: var(--blue); }
- .flow h3 { margin: 42px 0 10px; font-size: 20px; }
- .flow p { margin: 0; color: var(--muted); font-size: 14px; }
- .research-flow article { color: var(--ink); }
-
- .routes { background: var(--paper-2); border-top: 2px solid var(--ink); border-bottom: 2px solid var(--ink); }
- .route-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
- .route-card { border: 3px solid var(--ink); padding: clamp(26px, 4vw, 42px); min-height: 390px; box-shadow: var(--shadow); }
- .route-card.local { background: var(--acid); }
- .route-card.supplied { background: var(--blue); color: white; }
- .route-label { display: inline-block; border: 2px solid currentColor; padding: 5px 8px; text-transform: uppercase; letter-spacing: .09em; font-size: 11px; font-weight: 900; }
- .route-card h3 { margin: 28px 0 12px; font: 900 clamp(30px, 4vw, 48px)/1 Arial Black, Arial, sans-serif; letter-spacing: -.045em; }
- .route-card p { font-size: 17px; }
- .route-card ul { margin: 24px 0 0; padding-left: 20px; }
- .route-card li { margin: 8px 0; }
-
- .track-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
- .track-grid article { min-height: 260px; padding: 24px; border: 2px solid var(--ink); background: var(--paper); }
- .track-grid span { color: var(--blue); font-size: 24px; font-weight: 900; }
- .track-grid h3 { margin: 44px 0 10px; font-size: 21px; }
- .track-grid p { margin: 0; color: var(--muted); font-size: 14px; }
-
- .partner-section { background: var(--paper-2); border-top: 2px solid var(--ink); border-bottom: 2px solid var(--ink); }
- .partner-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
- .partner-grid article { position: relative; border: 2px solid var(--ink); background: var(--paper); padding: 26px; min-height: 260px; }
- .partner-grid h3 { margin: 58px 0 10px; font-size: 22px; }
- .partner-grid p { color: var(--muted); margin: 0; }
- .partner-number { position: absolute; top: 20px; right: 22px; color: var(--blue); font-size: 24px; font-weight: 900; }
- .partner-cta { display: flex; justify-content: space-between; align-items: center; gap: 28px; margin-top: 24px; border: 3px solid var(--ink); background: var(--orange); color: var(--ink); padding: 24px; box-shadow: var(--shadow); }
- .partner-cta div { display: grid; gap: 5px; }
- .partner-cta strong { font: 900 23px/1.1 Arial Black, Arial, sans-serif; }
- .partner-cta span { font-size: 13px; }
- .intake-rules { display: grid; grid-template-columns: .7fr 1.3fr; gap: 28px; align-items: center; margin-top: 24px; border: 2px solid var(--ink); background: var(--paper); padding: 24px; }
- .intake-rules h3 { margin: 6px 0 0; font: 900 24px/1.1 Arial Black, Arial, sans-serif; }
- .intake-rules ul { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 26px; margin: 0; padding-left: 20px; font-size: 13px; }
-
- .proof { background: var(--ink); color: var(--paper); }
- .proof .kicker { color: var(--acid); }
- .proof a { color: var(--acid); }
- .ledger-wrap { overflow-x: auto; border: 1px solid rgba(244,240,230,.35); }
- .ledger { width: 100%; border-collapse: collapse; min-width: 680px; }
- .ledger th, .ledger td { padding: 16px 18px; border-bottom: 1px solid rgba(244,240,230,.25); text-align: left; vertical-align: middle; }
- .ledger th { color: #aaa897; font-size: 11px; text-transform: uppercase; letter-spacing: .08em; }
- .ledger th:first-child, .ledger td:first-child { width: 90px; }
- .ledger th:last-child, .ledger td:last-child { width: 160px; text-align: right; }
- .ledger tbody tr:last-child td { border-bottom: 0; }
- .status { color: var(--acid); font-weight: 900; }
- .evidence { color: var(--acid); }
- .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
-
- .funding-grid { display: grid; grid-template-columns: .8fr 1.2fr; gap: 30px; align-items: start; }
- .meter-card { border: 3px solid var(--ink); background: var(--blue); color: white; padding: 28px; box-shadow: var(--shadow); position: sticky; top: 22px; }
- .amount { margin-top: 18px; font-family: Arial Black, Arial, sans-serif; font-size: clamp(42px, 6vw, 72px); letter-spacing: -.06em; line-height: 1; }
- .goal { margin-top: 8px; }
- .tiny { margin: 26px 0 0; font-size: 12px; }
- .support-card { border: 3px solid var(--ink); background: var(--paper); padding: clamp(24px, 4vw, 38px); box-shadow: var(--shadow); }
- .support-card h3 { margin: 12px 0; font: 900 clamp(28px, 4vw, 42px)/1 Arial Black, Arial, sans-serif; letter-spacing: -.04em; }
- .support-card p { color: var(--muted); font-size: 16px; }
- .support-card ul { margin: 24px 0; padding-left: 20px; }
- .support-card li { margin: 8px 0; }
- .support-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 22px; margin-top: 28px; }
- .support-thanks { display: flex; justify-content: space-between; gap: 18px; margin-bottom: 34px; border: 3px solid var(--ink); background: var(--acid); padding: 18px 22px; box-shadow: 5px 5px 0 var(--ink); }
- .support-thanks[hidden] { display: none; }
- .integrity { margin-top: 24px; border-left: 5px solid var(--orange); padding: 14px 18px; background: rgba(244,91,53,.08); font-size: 14px; }
-
- .not-ad { display: grid; grid-template-columns: 1fr 1fr; border: 3px solid var(--ink); }
- .not-ad > div { padding: clamp(26px, 5vw, 54px); }
- .not-ad > div:first-child { background: var(--orange); color: var(--ink); border-right: 3px solid var(--ink); }
- .not-ad h3 { font-family: Arial Black, Arial, sans-serif; font-size: clamp(30px, 4vw, 48px); line-height: 1; letter-spacing: -.04em; margin: 0 0 18px; }
- .not-ad ul { padding-left: 20px; margin-bottom: 0; }
-
- footer { border-top: 2px solid var(--ink); padding: 30px 0 46px; font-size: 12px; color: var(--muted); }
- .footer-grid { display: flex; justify-content: space-between; gap: 28px; }
-
- .terms-page { padding: 76px 0 90px; }
- .terms-page h1 { margin-bottom: 18px; }
- .terms-page > .lead { max-width: 760px; }
- .terms-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-top: 44px; }
- .terms-grid section { padding: 24px; border: 2px solid var(--ink); background: var(--paper); }
- .terms-grid h2 { margin: 0 0 12px; font: 900 23px/1.1 Arial Black, Arial, sans-serif; letter-spacing: -.025em; }
- .terms-grid p { margin: 0; color: var(--muted); }
- .terms-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 24px; margin-top: 34px; }
-
- @media (max-width: 820px) {
- nav { display: none; }
- .hero, .section-head, .funding-grid, .route-grid, .terms-grid, .definition-grid, .intake-rules { grid-template-columns: 1fr; }
- .hero { padding-top: 52px; gap: 34px; }
- .machine { min-height: 330px; }
- .flow, .status-grid, .track-grid { grid-template-columns: 1fr 1fr; }
- .partner-grid { grid-template-columns: 1fr; }
- .partner-grid article { min-height: 210px; }
- .partner-cta { align-items: stretch; flex-direction: column; }
- .partner-cta .button { text-align: center; }
- .intake-rules ul { grid-template-columns: 1fr; }
- .meter-card { position: static; }
- .not-ad { grid-template-columns: 1fr; }
- .not-ad > div:first-child { border-right: 0; border-bottom: 3px solid var(--ink); }
- }
- @media (max-width: 520px) {
- .wrap { width: min(100% - 22px, 1120px); }
- .prototype { display: none; }
- h1 { font-size: 50px; }
- .stamp { right: 4%; transform: rotate(5deg); }
- .flow, .status-grid, .track-grid { grid-template-columns: 1fr; }
- .flow article { min-height: 170px; }
- .support-thanks { flex-direction: column; }
- .footer-grid { display: grid; }
- }
- @media (prefers-reduced-motion: reduce) {
- *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; }
- }
-
-.error-page { min-height: 78vh; display: grid; align-content: center; justify-items: start; gap: 18px; }
-.error-page h1, .error-page p { margin: 0; }
+ --paper: #f4f5ee;
+ --paper-deep: #e9ede3;
+ --ink: #20352b;
+ --muted: #5f6f62;
+ --forest: #173d2f;
+ --forest-deep: #113024;
+ --lime: #d5f98b;
+ --line: #cbd2c5;
+ --font: 'Archivo', Arial, sans-serif;
+ --mono: 'IBM Plex Mono', monospace;
+}
+* {
+ box-sizing: border-box;
+}
+html {
+ scroll-behavior: smooth;
+ scroll-padding-top: 24px;
+}
+body {
+ margin: 0;
+ background: var(--paper);
+ color: var(--ink);
+ font-family: var(--font);
+ font-size: 16px;
+ line-height: 1.55;
+ -webkit-font-smoothing: antialiased;
+}
+::selection {
+ background: var(--lime);
+ color: var(--forest-deep);
+}
+a {
+ color: inherit;
+ text-underline-offset: 4px;
+}
+button {
+ font: inherit;
+}
+button,
+a,
+summary {
+ -webkit-tap-highlight-color: transparent;
+}
+a,
+button,
+summary {
+ touch-action: manipulation;
+}
+a:focus-visible,
+button:focus-visible,
+summary:focus-visible,
+[tabindex]:focus-visible {
+ outline: 3px solid #6d942d;
+ outline-offset: 5px;
+}
+button {
+ cursor: pointer;
+}
+button:disabled {
+ cursor: default;
+}
+svg {
+ display: block;
+}
+img {
+ max-width: 100%;
+ height: auto;
+}
+[hidden] {
+ display: none !important;
+}
+.wrap {
+ width: min(1320px, calc(100% - 104px));
+ margin-inline: auto;
+}
+.mono,
+.eyebrow {
+ font-family: var(--mono);
+ font-weight: 400;
+ font-size: 11px;
+ line-height: 1.6;
+ letter-spacing: 0.055em;
+}
+.mono {
+ text-transform: uppercase;
+}
+.arrow {
+ flex: none;
+ stroke: currentColor;
+ stroke-width: 1.5;
+ stroke-linecap: square;
+ stroke-linejoin: miter;
+ transition: transform 0.2s;
+}
+.text-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 15px;
+ text-decoration: none;
+ font-size: 14px;
+ font-weight: 550;
+}
+.text-link:hover .arrow {
+ transform: translate(3px, -2px);
+}
+.button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 25px;
+ min-height: 54px;
+ padding: 15px 23px;
+ background: var(--forest);
+ color: var(--paper);
+ font-size: 14px;
+ font-weight: 550;
+ text-decoration: none;
+ border: 1px solid var(--forest);
+ border-radius: 3px;
+ transition:
+ background 0.2s,
+ transform 0.2s;
+}
+.button:hover {
+ background: var(--forest-deep);
+ transform: translateY(-2px);
+}
+.button:hover .arrow {
+ transform: translateX(3px);
+}
+.button-lime {
+ background: var(--lime);
+ border-color: var(--lime);
+ color: var(--forest-deep);
+}
+.button-lime:hover {
+ background: #e3ffb0;
+}
+.skip-link {
+ position: fixed;
+ top: 12px;
+ left: 12px;
+ z-index: 20;
+ padding: 12px 20px;
+ background: var(--lime);
+ transform: translateY(-180%);
+}
+.skip-link:focus {
+ transform: translateY(0);
+}
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+h1,
+h2,
+h3,
+p {
+ margin-top: 0;
+}
+h1,
+h2,
+h3 {
+ font-weight: 550;
+}
+h2 {
+ font-size: clamp(36px, 4.1vw, 60px);
+ line-height: 1.04;
+ letter-spacing: -0.055em;
+ margin-bottom: 0;
+}
+h3 {
+ letter-spacing: -0.025em;
+}
+p {
+ margin-bottom: 18px;
+}
+p:last-child {
+ margin-bottom: 0;
+}
+.section-space {
+ padding-block: 96px;
+}
+.section-index {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 24px;
+ margin-bottom: 46px;
+}
+.small-cross {
+ font-size: 24px;
+ line-height: 1;
+}
+.section-heading {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 48px;
+ margin-bottom: 52px;
+}
+.section-heading > p {
+ max-width: 370px;
+ color: var(--muted);
+ font-size: 16px;
+ line-height: 1.65;
+ margin-bottom: 2px;
+}
+.status-dot {
+ display: inline-block;
+ flex: none;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: #5a8b41;
+ box-shadow: 0 0 0 4px #5a8b4110;
+}
+.site-header {
+ border-bottom: 1px solid var(--line);
+}
+.header-inner {
+ min-height: 100px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 30px;
+}
+.brand {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ text-decoration: none;
+}
+.brand-mark {
+ stroke: var(--ink);
+ stroke-width: 1.8;
+}
+.brand-name {
+ display: flex;
+ gap: 16px;
+ align-items: center;
+ font-size: 34px;
+ line-height: 1;
+ letter-spacing: -0.065em;
+ font-weight: 650;
+}
+.brand-name > span {
+ border-left: 1px solid var(--line);
+ padding-left: 16px;
+ font-size: 12px;
+ letter-spacing: -0.01em;
+ line-height: 1.3;
+ font-weight: 500;
+ max-width: 105px;
+}
+.site-header nav {
+ display: flex;
+ align-items: center;
+ gap: 29px;
+ font-size: 13px;
+ font-weight: 500;
+}
+.site-header nav > a {
+ text-decoration: none;
+ position: relative;
+}
+.site-header nav > a::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ bottom: -6px;
+ width: 100%;
+ height: 1px;
+ background: currentColor;
+ transform: scaleX(0);
+ transform-origin: left;
+ transition: transform 0.2s;
+}
+.site-header nav > a:hover::after {
+ transform: scaleX(1);
+}
+.site-header .nav-source {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ border-left: 1px solid var(--line);
+ padding-left: 27px;
+}
+.nav-source .arrow {
+ width: 15px;
+ height: 15px;
+}
+.menu-toggle {
+ display: none;
+}
+.hero {
+ display: grid;
+ grid-template-columns: 1.06fr 1fr;
+ gap: 15px;
+ align-items: center;
+ padding-block: 63px 57px;
+}
+.hero-copy {
+ position: relative;
+ z-index: 1;
+ padding-block: 27px;
+}
+.eyebrow {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ text-transform: uppercase;
+ font-size: 10px;
+ margin-bottom: 33px;
+}
+.crosshair {
+ font: 22px/1 var(--mono);
+ margin-top: -2px;
+}
+.hero h1 {
+ font-size: clamp(64px, 7.15vw, 108px);
+ line-height: 0.98;
+ letter-spacing: -0.075em;
+ margin: 0 0 30px;
+ font-weight: 560;
+}
+.hero h1 > span {
+ display: block;
+ white-space: nowrap;
+}
+.hero h1 em {
+ font-style: normal;
+ position: relative;
+ display: inline-block;
+}
+.hero h1 em::after {
+ content: '';
+ position: absolute;
+ z-index: -1;
+ inset: auto -4px 4px;
+ height: 28%;
+ background: var(--lime);
+ transform: rotate(-2deg);
+}
+.hero-description {
+ max-width: 412px;
+ color: var(--muted);
+ font-size: 17px;
+ line-height: 1.65;
+ margin-bottom: 30px;
+}
+.hero-actions {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 23px;
+}
+.hero-actions .text-link {
+ font-size: 13px;
+}
+.hero-location {
+ display: flex;
+ gap: 11px;
+ align-items: center;
+ margin-top: 47px;
+ font-size: 9px;
+ letter-spacing: 0.1em;
+}
+.location-divider {
+ margin-inline: 6px;
+ color: #879584;
+}
+.hero-location .status-dot {
+ width: 6px;
+ height: 6px;
+}
+.hardware-figure {
+ min-width: 0;
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+.figure-top,
+.figure-bottom {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 8px;
+ color: #667860;
+ padding: 0 18px;
+}
+.figure-top {
+ padding-top: 8px;
+}
+.figure-cross {
+ font-size: 20px;
+}
+.figure-bottom {
+ border-top: 1px solid var(--line);
+ padding-top: 14px;
+ margin-inline: 16px;
+ padding-inline: 0;
+ font-size: 7px;
+ letter-spacing: 0.065em;
+}
+.hardware-svg {
+ width: 100%;
+ height: auto;
+ overflow: visible;
+}
+.diagram-heatsink {
+ animation: heatsink-arrive 1.5s ease-out both;
+}
+.diagram-board {
+ animation: board-arrive 1.5s ease-out both;
+}
+.diagram-labels {
+ animation: labels-arrive 1.8s ease-out both;
+}
+@keyframes heatsink-arrive {
+ from {
+ translate: 0 36px;
+ opacity: 0.3;
+ }
+ to {
+ translate: 0 0;
+ opacity: 1;
+ }
+}
+@keyframes board-arrive {
+ from {
+ translate: 0 20px;
+ opacity: 0.5;
+ }
+ to {
+ translate: 0 0;
+ opacity: 1;
+ }
+}
+@keyframes labels-arrive {
+ 0%,
+ 40% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+.lab-status {
+ background: var(--paper-deep);
+ border-block: 1px solid var(--line);
+}
+.status-inner {
+ display: flex;
+ align-items: center;
+ gap: 35px;
+ min-height: 116px;
+}
+.status-intake {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ flex: 1;
+}
+.status-intake h2 {
+ font-size: 15px;
+ letter-spacing: -0.01em;
+ line-height: 1.4;
+ margin-bottom: 3px;
+}
+.status-intake p {
+ font-size: 12px;
+ color: var(--muted);
+}
+.status-intake > a {
+ display: none;
+}
+.status-numbers {
+ display: flex;
+ align-items: center;
+ gap: 35px;
+ margin: 0;
+}
+.status-numbers > div {
+ display: flex;
+ flex-direction: column-reverse;
+ min-width: 98px;
+ gap: 2px;
+ border-left: 1px solid #c5cdbf;
+ padding-left: 29px;
+}
+.status-numbers dt {
+ font-family: var(--mono);
+ font-size: 9px;
+ color: var(--muted);
+ white-space: nowrap;
+}
+.status-numbers dd {
+ font-size: 29px;
+ line-height: 1.1;
+ letter-spacing: -0.06em;
+ font-weight: 500;
+ margin: 0;
+}
+.status-register {
+ font-size: 12px;
+ margin-left: auto;
+ gap: 14px;
+ padding-left: 25px;
+}
+.intro-grid {
+ display: grid;
+ grid-template-columns: 1.2fr 1fr;
+ gap: 100px;
+}
+.intro-grid h2 {
+ font-size: clamp(36px, 3.6vw, 53px);
+ line-height: 1.1;
+}
+.muted-heading {
+ color: #798574;
+}
+.intro-copy {
+ padding-top: 3px;
+ max-width: 450px;
+}
+.intro-copy h3 {
+ font-size: 18px;
+ margin-bottom: 17px;
+ font-weight: 600;
+}
+.intro-copy p {
+ color: var(--muted);
+ font-size: 15px;
+ line-height: 1.7;
+}
+.intro-copy .text-link {
+ margin-top: 7px;
+ font-size: 13px;
+}
+.research-section {
+ background: var(--forest);
+ color: var(--paper);
+ position: relative;
+}
+.research-section .section-index {
+ color: #bdcbb8;
+}
+.research-section .section-heading > p {
+ color: #c0ceb9;
+}
+.research-explorer[data-enhanced] {
+ display: grid;
+ grid-template-columns: 0.85fr 1.15fr;
+ gap: 64px;
+}
+.track-selector {
+ padding-top: 0;
+ align-self: start;
+}
+.track-tab {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ gap: 25px;
+ min-height: 93px;
+ background: none;
+ border: 0;
+ border-top: 1px solid #52705b;
+ text-align: left;
+ color: #c5d2bc;
+ font-size: 26px;
+ letter-spacing: -0.04em;
+ padding: 17px 10px 17px 0;
+ transition:
+ color 0.2s,
+ padding 0.2s;
+}
+.track-tab:last-child {
+ border-bottom: 1px solid #52705b;
+}
+.track-tab .track-number {
+ font-size: 11px;
+ color: #a1b59a;
+ letter-spacing: 0;
+}
+.track-tab > .arrow {
+ margin-left: auto;
+ width: 24px;
+ height: 24px;
+ opacity: 0.35;
+}
+.track-tab[aria-selected='true'] {
+ color: var(--lime);
+}
+.track-tab[aria-selected='true'] .arrow {
+ opacity: 1;
+ transform: translateX(4px);
+}
+.track-tab[aria-selected='true'] .track-number {
+ color: var(--lime);
+}
+.track-tab:hover {
+ color: var(--lime);
+ padding-left: 6px;
+}
+.research-section :focus-visible {
+ outline-color: var(--lime);
+}
+.track-panel {
+ border: 1px solid #58705d;
+ background: #214634;
+ padding: 28px 32px 31px;
+ min-height: 440px;
+}
+.track-panel + .track-panel {
+ margin-top: 20px;
+}
+[data-enhanced] .track-panel {
+ margin-top: 0;
+ animation: panel-in 0.25s ease-out;
+}
+@keyframes panel-in {
+ from {
+ opacity: 0.4;
+ translate: 0 5px;
+ }
+ to {
+ opacity: 1;
+ translate: 0 0;
+ }
+}
+.track-panel-top {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 15px;
+ color: #c1d1b8;
+}
+.track-panel-top > .mono {
+ font-size: 9px;
+}
+.queue-label {
+ border: 1px solid #6e865f;
+ border-radius: 2px;
+ padding: 4px 7px;
+ white-space: nowrap;
+ color: var(--lime);
+ font-size: 8px !important;
+}
+.track-symbol {
+ display: flex;
+ align-items: center;
+ gap: 26px;
+ margin-block: 22px 21px;
+}
+.track-symbol svg {
+ width: 118px;
+ height: 78px;
+ flex: none;
+ stroke: #8cae7a;
+ stroke-width: 1.5;
+}
+.track-symbol svg .symbol-accent {
+ stroke: var(--lime);
+ stroke-width: 2.2;
+}
+.track-symbol > span {
+ font-size: 9px;
+ max-width: 170px;
+ color: #afc4a2;
+}
+.track-panel h3 {
+ font-size: 28px;
+ font-weight: 500;
+ line-height: 1.13;
+ max-width: 360px;
+ margin-bottom: 15px;
+ letter-spacing: -0.04em;
+}
+.track-panel > p {
+ font-size: 14px;
+ line-height: 1.65;
+ color: #c6d5bc;
+ max-width: 460px;
+ margin-bottom: 25px;
+}
+.track-categories {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ gap: 10px 14px;
+}
+.track-categories li {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-family: var(--mono);
+ font-size: 9px;
+ color: #d4dfca;
+}
+.track-categories li::before {
+ content: '+';
+ color: var(--lime);
+}
+.research-note {
+ font-size: 8px;
+ color: #bacdb0;
+ margin: 25px 0 0;
+ text-align: right;
+}
+.method-steps {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+.method-steps li {
+ padding: 0 27px 0 0;
+}
+.method-steps li + li {
+ padding-left: 27px;
+ border-left: 1px solid var(--line);
+}
+.method-steps li:last-child {
+ padding-right: 0;
+}
+.step-top {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 31px;
+}
+.step-number {
+ font-size: 59px;
+ font-weight: 450;
+ line-height: 1.2;
+ letter-spacing: -0.08em;
+ color: #92a18a;
+}
+.step-top .arrow {
+ width: 25px;
+}
+.method-steps li:last-child .step-top .arrow {
+ rotate: -45deg;
+}
+.step-label {
+ font-size: 9px;
+ letter-spacing: 0.1em;
+ color: var(--muted);
+}
+.method-steps h3 {
+ font-size: 20px;
+ font-weight: 550;
+ line-height: 1.15;
+ margin-block: 10px 13px;
+ letter-spacing: -0.04em;
+}
+.method-steps p {
+ color: var(--muted);
+ font-size: 13px;
+ line-height: 1.7;
+}
+.method-footnote {
+ display: flex;
+ align-items: center;
+ gap: 42px;
+ border-top: 1px solid var(--line);
+ padding-top: 22px;
+ margin-top: 40px;
+}
+.method-footnote .mono {
+ white-space: nowrap;
+ font-size: 9px;
+}
+.method-footnote p {
+ font-size: 12px;
+ color: var(--muted);
+}
+.register-section {
+ background: var(--paper-deep);
+ border-block: 1px solid var(--line);
+}
+.register-grid {
+ display: grid;
+ grid-template-columns: 0.85fr 1.15fr;
+ gap: 80px;
+ align-items: center;
+}
+.register-copy > .mono {
+ font-size: 10px;
+}
+.register-copy h2 {
+ font-size: clamp(36px, 3.6vw, 52px);
+ margin-block: 31px 22px;
+}
+.register-copy p {
+ color: var(--muted);
+ font-size: 14px;
+ line-height: 1.7;
+ max-width: 360px;
+}
+.register-copy .text-link {
+ margin-top: 12px;
+ font-size: 13px;
+}
+.register-table-wrap {
+ min-width: 0;
+}
+.register-caption {
+ display: flex;
+ justify-content: space-between;
+ font-size: 9px;
+ border-bottom: 1px solid #a9b8a0;
+ padding-bottom: 18px;
+}
+.register-caption > span:last-child {
+ color: var(--muted);
+}
+.register-table {
+ width: 100%;
+ border-collapse: collapse;
+ text-align: left;
+}
+.register-table thead th {
+ font: 9px/1.5 var(--mono);
+ text-transform: uppercase;
+ color: var(--muted);
+ padding-block: 17px 7px;
+}
+.register-table tbody tr {
+ border-bottom: 1px solid #c3cdbb;
+}
+.register-table th {
+ font-size: 15px;
+ font-weight: 550;
+ padding-block: 24px;
+}
+.register-table small {
+ display: block;
+ font-size: 11px;
+ color: var(--muted);
+ font-weight: 400;
+ line-height: 1.6;
+ margin-top: 4px;
+}
+.register-table td {
+ padding-left: 12px;
+}
+.register-table td:last-child {
+ width: 35px;
+}
+.register-table td a {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 36px;
+ height: 42px;
+ transition: background 0.2s;
+}
+.register-table td a:hover {
+ background: var(--lime);
+}
+.record-state {
+ font: 9px/1.4 var(--mono);
+ color: #5a6955;
+ white-space: nowrap;
+}
+.record-state.ready {
+ background: #d6e5c9;
+ color: #254328;
+ padding: 5px 7px;
+ border-radius: 2px;
+}
+.register-note {
+ font-size: 7px;
+ color: var(--muted);
+ margin-top: 16px;
+}
+.intake-layout {
+ display: grid;
+ grid-template-columns: 0.85fr 1.15fr;
+ gap: 80px;
+}
+.intake-intro .label {
+ border: 1px solid #b8c6ab;
+ padding: 6px 9px;
+ display: inline-block;
+ font-size: 9px;
+}
+.intake-intro h3 {
+ font-size: 32px;
+ line-height: 1.12;
+ font-weight: 500;
+ letter-spacing: -0.04em;
+ margin-block: 24px 20px;
+}
+.intake-intro p {
+ font-size: 14px;
+ color: var(--muted);
+ max-width: 340px;
+ line-height: 1.7;
+}
+.intake-intro .button {
+ margin-top: 11px;
+ min-width: 185px;
+}
+.intake-options article {
+ display: flex;
+ gap: 22px;
+ border-top: 1px solid var(--line);
+ padding: 26px 0 28px;
+}
+.intake-options article:last-child {
+ border-bottom: 1px solid var(--line);
+}
+.intake-options article > .mono {
+ font-size: 10px;
+ color: var(--muted);
+ padding-top: 5px;
+}
+.intake-options h3 {
+ font-size: 23px;
+ line-height: 1.25;
+ margin-bottom: 11px;
+ font-weight: 500;
+}
+.intake-options p {
+ font-size: 13px;
+ color: var(--muted);
+ line-height: 1.7;
+ max-width: 370px;
+}
+.intake-options .arrow {
+ margin-left: auto;
+ width: 18px;
+ opacity: 0.55;
+}
+.intake-details {
+ margin-top: 40px;
+ background: var(--paper-deep);
+ border: 1px solid var(--line);
+ border-radius: 2px;
+}
+.intake-details summary {
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 20px;
+ list-style: none;
+ padding: 21px 24px;
+ font-size: 14px;
+ font-weight: 550;
+}
+.intake-details summary::-webkit-details-marker {
+ display: none;
+}
+.intake-details summary > span:first-child {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+.details-plus {
+ font-size: 24px;
+ line-height: 1;
+ transition: rotate 0.2s;
+}
+.intake-details[open] .details-plus {
+ rotate: 45deg;
+}
+.intake-detail-content {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 40px;
+ padding: 0 24px 25px;
+}
+.intake-detail-content p {
+ font-size: 13px;
+ line-height: 1.75;
+ color: var(--muted);
+ margin: 0;
+}
+.fund-section {
+ background: var(--forest-deep);
+ color: var(--paper);
+ padding-block: 82px 0;
+}
+.fund-inner {
+ display: grid;
+ grid-template-columns: 1.3fr 0.7fr;
+ gap: 100px;
+ padding-bottom: 70px;
+}
+.fund-copy > .mono {
+ color: #b7ccaa;
+ font-size: 10px;
+}
+.fund-copy h2 {
+ margin-block: 29px 24px;
+ font-size: clamp(44px, 4.8vw, 70px);
+}
+.fund-copy h2 > span {
+ color: var(--lime);
+}
+.fund-copy p {
+ color: #c0d0b6;
+ max-width: 460px;
+ font-size: 15px;
+ line-height: 1.75;
+}
+.fund-copy .text-link {
+ margin-top: 13px;
+ color: #d4e4c9;
+ font-size: 13px;
+}
+.fund-action {
+ padding-top: 5px;
+}
+.fund-action > .mono {
+ font-size: 9px;
+ color: #bed0b2;
+}
+.support-amount {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 20px;
+ font-size: 44px;
+ letter-spacing: -0.055em;
+ line-height: 1.2;
+ margin-block: 20px 9px;
+}
+.support-amount > span {
+ color: var(--lime);
+ font-size: 46px;
+ font-weight: 300;
+}
+.fund-action > p {
+ font-size: 13px;
+ color: #c0d0b6;
+}
+.fund-action .button {
+ width: 100%;
+ margin-block: 5px 16px;
+}
+.terms-link {
+ display: block;
+ text-align: center;
+ font-size: 12px;
+}
+.fund-action .fund-disclosure {
+ font-size: 10px;
+ line-height: 1.7;
+ margin-block: 24px 0;
+ color: #b4c6a8;
+}
+.independence-line {
+ display: flex;
+ align-items: center;
+ gap: 24px;
+ border-top: 1px solid #46613e;
+ padding-block: 29px;
+ color: #c3d3b8;
+}
+.independence-symbol {
+ font-size: 30px;
+ line-height: 1;
+ color: var(--lime);
+ padding-right: 12px;
+}
+.independence-line p {
+ font-size: 12px;
+ line-height: 1.7;
+ max-width: 850px;
+}
+.independence-line strong {
+ color: var(--paper);
+ font-weight: 550;
+}
+.independence-line > a {
+ display: grid;
+ place-items: center;
+ width: 42px;
+ height: 42px;
+ margin-left: auto;
+ flex: none;
+}
+.fund-section :focus-visible {
+ outline-color: var(--lime);
+}
+.support-thanks {
+ padding: 22px 25px;
+ border: 1px solid var(--lime);
+ background: #254632;
+ margin-bottom: 40px;
+ display: grid;
+ gap: 7px;
+}
+.support-thanks strong {
+ font-size: 17px;
+}
+.support-thanks span {
+ font-size: 13px;
+ color: #c3d3b8;
+}
+.site-footer {
+ padding-block: 56px 24px;
+}
+.footer-top {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 30px;
+ padding-bottom: 46px;
+ border-bottom: 1px solid var(--line);
+}
+.footer-wordmark {
+ text-decoration: none;
+ font-size: 91px;
+ font-weight: 650;
+ letter-spacing: -0.09em;
+ line-height: 1;
+}
+.footer-wordmark > span {
+ font-size: 25px;
+ vertical-align: top;
+ letter-spacing: 0;
+ display: inline-block;
+ margin: 10px 0 0 8px;
+}
+.footer-top > p {
+ font-size: 20px;
+ line-height: 1.3;
+ letter-spacing: -0.03em;
+ margin: 0 auto 0 45px;
+}
+.footer-top .text-link {
+ font-size: 16px;
+}
+.footer-bottom {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ gap: 16px 30px;
+ padding-top: 24px;
+ font-size: 10px;
+ color: var(--muted);
+}
+.footer-bottom > div {
+ display: flex;
+ gap: 24px;
+}
+.footer-bottom > div a {
+ text-decoration: none;
+}
+.footer-bottom > span:last-child {
+ grid-column: 1/-1;
+ font-size: 9px;
+}
+.terms-page {
+ padding-block: 85px 100px;
+}
+.terms-page h1,
+.error-page h1 {
+ font-size: clamp(55px, 7vw, 96px);
+ line-height: 1;
+ letter-spacing: -0.07em;
+ margin-block: 20px 28px;
+}
+.terms-page > .lead {
+ font-size: 20px;
+ color: var(--muted);
+ max-width: 660px;
+}
+.terms-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 0 60px;
+ margin-top: 50px;
+}
+.terms-grid section {
+ border-top: 1px solid var(--line);
+ padding-block: 28px 33px;
+}
+.terms-grid h2 {
+ font-size: 24px;
+ line-height: 1.2;
+ letter-spacing: -0.04em;
+ margin-bottom: 14px;
+}
+.terms-grid p {
+ font-size: 15px;
+ color: var(--muted);
+ line-height: 1.75;
+}
+.terms-actions {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 26px;
+ margin-top: 28px;
+ font-size: 14px;
+}
+.error-page {
+ min-height: 70vh;
+ padding-block: 100px;
+}
+.error-page p {
+ font-size: 19px;
+ color: var(--muted);
+}
+.error-page .button {
+ margin-top: 20px;
+}
+@media (min-width: 1500px) {
+ .hero {
+ gap: 35px;
+ padding-block: 72px;
+ }
+ .hero h1 {
+ font-size: 108px;
+ }
+}
+@media (max-width: 1100px) {
+ .wrap {
+ width: calc(100% - 64px);
+ }
+ .header-inner {
+ min-height: 90px;
+ }
+ .site-header nav {
+ gap: 22px;
+ }
+ .brand-name {
+ font-size: 30px;
+ }
+ .brand-mark {
+ width: 36px;
+ }
+ .hero {
+ gap: 10px;
+ padding-block: 45px;
+ }
+ .hero h1 {
+ font-size: 7.3vw;
+ }
+ .hero-actions {
+ gap: 17px;
+ }
+ .hero-actions .button {
+ font-size: 12px;
+ padding-inline: 17px;
+ gap: 18px;
+ }
+ .hero-actions .text-link {
+ font-size: 12px;
+ gap: 10px;
+ }
+ .hero-description {
+ font-size: 15px;
+ max-width: 345px;
+ }
+ .hero-location {
+ margin-top: 37px;
+ }
+ .status-inner {
+ gap: 20px;
+ }
+ .status-numbers {
+ gap: 20px;
+ }
+ .status-numbers > div {
+ padding-left: 20px;
+ min-width: 88px;
+ }
+ .status-register {
+ display: none;
+ }
+ .intro-grid {
+ gap: 55px;
+ }
+ .research-explorer[data-enhanced] {
+ gap: 38px;
+ }
+ .track-tab {
+ font-size: 23px;
+ gap: 18px;
+ }
+ .track-panel {
+ padding: 23px;
+ min-height: 453px;
+ }
+ .track-categories {
+ column-gap: 8px;
+ }
+ .track-categories li {
+ font-size: 8px;
+ }
+ .track-panel-top {
+ gap: 7px;
+ }
+ .track-panel-top > .mono {
+ font-size: 8px;
+ }
+ .queue-label {
+ font-size: 7px !important;
+ }
+ .register-grid,
+ .intake-layout {
+ gap: 50px;
+ }
+ .register-table th {
+ font-size: 13px;
+ }
+ .register-table small {
+ font-size: 10px;
+ }
+ .record-state {
+ font-size: 8px;
+ }
+ .fund-inner {
+ gap: 55px;
+ }
+ .method-steps li {
+ padding-right: 18px;
+ }
+ .method-steps li + li {
+ padding-left: 18px;
+ }
+ .method-steps h3 {
+ font-size: 18px;
+ }
+ .footer-top > p {
+ margin-left: 15px;
+ }
+}
+@media (max-width: 800px) {
+ .wrap {
+ width: calc(100% - 40px);
+ }
+ .section-space {
+ padding-block: 65px;
+ }
+ .section-index {
+ margin-bottom: 30px;
+ }
+ .section-index .mono {
+ font-size: 9px;
+ }
+ .header-inner {
+ min-height: 82px;
+ flex-wrap: wrap;
+ gap: 0;
+ padding-block: 16px;
+ }
+ .brand-name {
+ font-size: 28px;
+ }
+ .brand-name > span {
+ font-size: 10px;
+ max-width: 93px;
+ }
+ .brand-mark {
+ width: 33px;
+ height: 33px;
+ }
+ .site-header nav {
+ width: 100%;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 6px;
+ padding: 24px 0 4px;
+ font-size: 12px;
+ }
+ .site-header nav > a {
+ min-height: 44px;
+ display: flex;
+ align-items: center;
+ padding-block: 10px;
+ }
+ .site-header .nav-source {
+ border: 0;
+ padding-left: 0;
+ }
+ .site-header[data-enhanced] nav {
+ display: none;
+ }
+ .site-header[data-enhanced] nav[data-open] {
+ display: grid;
+ }
+ .menu-toggle:not([hidden]) {
+ display: flex;
+ align-items: center;
+ gap: 18px;
+ background: none;
+ border: 1px solid var(--line);
+ border-radius: 2px;
+ padding: 9px 12px;
+ font-size: 11px;
+ color: var(--ink);
+ margin-left: auto;
+ min-height: 40px;
+ }
+ .menu-toggle > span {
+ font-size: 18px;
+ line-height: 1;
+ }
+ .hero {
+ grid-template-columns: 1fr 1fr;
+ gap: 0;
+ padding-block: 32px;
+ }
+ .hero-copy {
+ padding-block: 12px;
+ }
+ .eyebrow {
+ font-size: 8px;
+ gap: 6px;
+ margin-bottom: 22px;
+ }
+ .hero h1 {
+ font-size: 7.6vw;
+ margin-bottom: 23px;
+ }
+ .hero-description {
+ font-size: 13px;
+ max-width: 300px;
+ }
+ .hero-actions {
+ gap: 15px;
+ flex-direction: column;
+ align-items: flex-start;
+ }
+ .hero-actions .button {
+ min-height: 45px;
+ }
+ .hero-location {
+ font-size: 7px;
+ margin-top: 30px;
+ gap: 7px;
+ }
+ .figure-top {
+ font-size: 6px;
+ padding-inline: 10px;
+ }
+ .figure-bottom {
+ font-size: 5px;
+ margin-inline: 8px;
+ gap: 10px;
+ }
+ .status-inner {
+ min-height: 115px;
+ gap: 15px;
+ }
+ .status-intake {
+ gap: 12px;
+ }
+ .status-intake h2 {
+ font-size: 13px;
+ }
+ .status-intake p {
+ font-size: 10px;
+ }
+ .status-numbers {
+ gap: 15px;
+ }
+ .status-numbers > div {
+ padding-left: 15px;
+ min-width: 70px;
+ }
+ .status-numbers dt {
+ font-size: 7px;
+ }
+ .status-numbers dd {
+ font-size: 25px;
+ }
+ .intro-grid {
+ gap: 35px;
+ grid-template-columns: 1fr 1fr;
+ }
+ .intro-grid h2 {
+ font-size: 34px;
+ }
+ .intro-copy h3 {
+ font-size: 16px;
+ }
+ .intro-copy p {
+ font-size: 13px;
+ }
+ .intro-copy .text-link {
+ font-size: 11px;
+ gap: 9px;
+ }
+ .section-heading {
+ gap: 30px;
+ margin-bottom: 38px;
+ }
+ .section-heading h2 {
+ font-size: 39px;
+ }
+ .section-heading > p {
+ font-size: 13px;
+ max-width: 260px;
+ }
+ .research-explorer[data-enhanced] {
+ gap: 25px;
+ grid-template-columns: 0.8fr 1.2fr;
+ }
+ .track-tab {
+ font-size: 18px;
+ min-height: 87px;
+ gap: 13px;
+ }
+ .track-tab > .arrow {
+ width: 18px;
+ }
+ .track-panel {
+ padding: 20px;
+ min-height: 440px;
+ }
+ .track-panel-top {
+ flex-wrap: wrap;
+ }
+ .track-panel h3 {
+ font-size: 25px;
+ }
+ .track-panel > p {
+ font-size: 12px;
+ }
+ .track-symbol {
+ margin-block: 20px;
+ gap: 13px;
+ }
+ .track-symbol svg {
+ width: 89px;
+ height: 63px;
+ }
+ .track-symbol > span {
+ font-size: 7px;
+ }
+ .track-categories {
+ grid-template-columns: 1fr;
+ gap: 7px;
+ }
+ .research-note {
+ font-size: 7px;
+ max-width: 500px;
+ margin-left: auto;
+ }
+ .method-steps {
+ grid-template-columns: 1fr 1fr;
+ gap: 32px 0;
+ }
+ .method-steps li {
+ padding-right: 25px;
+ }
+ .method-steps li + li {
+ padding-left: 25px;
+ }
+ .method-steps li:nth-child(3) {
+ padding-left: 0;
+ border: 0;
+ }
+ .method-steps h3 {
+ font-size: 22px;
+ }
+ .method-steps p {
+ font-size: 13px;
+ }
+ .step-top {
+ margin-bottom: 20px;
+ }
+ .step-number {
+ font-size: 51px;
+ }
+ .method-footnote {
+ gap: 25px;
+ }
+ .method-footnote p {
+ font-size: 11px;
+ }
+ .register-grid {
+ grid-template-columns: 1fr;
+ gap: 40px;
+ }
+ .register-copy {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 0 35px;
+ }
+ .register-copy > .mono {
+ grid-column: 1/-1;
+ }
+ .register-copy h2 {
+ grid-column: 1;
+ grid-row: 2/5;
+ font-size: 38px;
+ margin-block: 23px 0;
+ }
+ .register-copy p {
+ grid-column: 2;
+ font-size: 13px;
+ }
+ .register-copy p:first-of-type {
+ margin-top: 23px;
+ }
+ .register-copy .text-link {
+ grid-column: 2;
+ font-size: 12px;
+ margin-top: 0;
+ }
+ .register-table th {
+ font-size: 15px;
+ }
+ .register-table small {
+ font-size: 11px;
+ }
+ .record-state {
+ font-size: 10px;
+ }
+ .register-note {
+ font-size: 8px;
+ }
+ .intake-layout {
+ gap: 35px;
+ grid-template-columns: 0.9fr 1.1fr;
+ }
+ .intake-intro h3 {
+ font-size: 29px;
+ }
+ .intake-intro p {
+ font-size: 13px;
+ }
+ .intake-options article {
+ gap: 13px;
+ padding-block: 22px;
+ }
+ .intake-options h3 {
+ font-size: 20px;
+ }
+ .intake-options p {
+ font-size: 12px;
+ }
+ .intake-options .arrow {
+ display: none;
+ }
+ .intake-detail-content {
+ gap: 25px;
+ }
+ .intake-detail-content p {
+ font-size: 12px;
+ }
+ .fund-section {
+ padding-top: 65px;
+ }
+ .fund-inner {
+ gap: 40px;
+ grid-template-columns: 1.15fr 0.85fr;
+ padding-bottom: 50px;
+ }
+ .fund-copy h2 {
+ font-size: 42px;
+ }
+ .fund-copy p {
+ font-size: 13px;
+ }
+ .support-amount {
+ font-size: 36px;
+ }
+ .fund-action .button {
+ font-size: 12px;
+ padding-inline: 15px;
+ gap: 10px;
+ }
+ .fund-action > .mono {
+ font-size: 8px;
+ }
+ .fund-action .fund-disclosure {
+ font-size: 9px;
+ }
+ .independence-line {
+ gap: 12px;
+ }
+ .independence-line p {
+ font-size: 11px;
+ }
+ .footer-top {
+ padding-bottom: 35px;
+ gap: 20px;
+ }
+ .footer-wordmark {
+ font-size: 75px;
+ }
+ .footer-top > p {
+ font-size: 17px;
+ margin-left: 0;
+ }
+ .footer-top .text-link {
+ font-size: 12px;
+ }
+ .terms-page {
+ padding-block: 60px;
+ }
+ .terms-grid {
+ gap: 0 35px;
+ }
+ .terms-grid p {
+ font-size: 14px;
+ }
+}
+@media (max-width: 560px) {
+ .wrap {
+ width: calc(100% - 36px);
+ }
+ .header-inner {
+ min-height: 76px;
+ }
+ .brand {
+ gap: 9px;
+ }
+ .brand-name {
+ gap: 12px;
+ }
+ .brand-name > span {
+ padding-left: 12px;
+ }
+ .site-header nav {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 4px;
+ }
+ .site-header .nav-source {
+ border: 0;
+ padding-left: 0;
+ }
+ .hero {
+ grid-template-columns: 1fr;
+ gap: 23px;
+ padding-block: 33px 24px;
+ }
+ .hero-copy {
+ padding: 0;
+ }
+ .eyebrow {
+ font-size: 8px;
+ margin-bottom: 25px;
+ }
+ .hero h1 {
+ font-size: clamp(53px, 12vw, 72px);
+ letter-spacing: -0.067em;
+ margin-bottom: 24px;
+ }
+ .hero-description {
+ font-size: 15px;
+ max-width: 330px;
+ margin-bottom: 25px;
+ }
+ .hero-actions {
+ flex-direction: row;
+ align-items: center;
+ gap: 22px;
+ }
+ .hero-actions .button {
+ font-size: 11px;
+ gap: 13px;
+ padding-inline: 15px;
+ min-height: 48px;
+ }
+ .hero-actions .text-link {
+ font-size: 11px;
+ gap: 8px;
+ }
+ .hero-location {
+ margin-top: 28px;
+ font-size: 8px;
+ gap: 10px;
+ }
+ .hardware-figure {
+ width: 100%;
+ max-width: 445px;
+ justify-self: center;
+ margin-top: 7px;
+ }
+ .figure-top {
+ font-size: 7px;
+ padding-inline: 10px;
+ }
+ .hardware-svg {
+ margin-top: -13px;
+ margin-bottom: -10px;
+ }
+ .figure-bottom {
+ font-size: 6px;
+ gap: 5px;
+ padding-top: 12px;
+ }
+ .status-inner {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 0;
+ padding-block: 24px;
+ }
+ .status-intake {
+ gap: 15px;
+ margin-bottom: 22px;
+ }
+ .status-intake h2 {
+ font-size: 15px;
+ }
+ .status-intake p {
+ font-size: 11px;
+ }
+ .status-intake > a {
+ display: block;
+ margin-left: auto;
+ padding: 8px;
+ }
+ .status-numbers {
+ justify-content: space-between;
+ gap: 0;
+ border-top: 1px solid #c5cdbf;
+ padding-top: 20px;
+ }
+ .status-numbers > div {
+ border-left: 1px solid #c5cdbf;
+ padding-left: 20px;
+ width: 33.333%;
+ }
+ .status-numbers > div:first-child {
+ border: 0;
+ padding-left: 0;
+ }
+ .status-numbers dd {
+ font-size: 31px;
+ }
+ .status-numbers dt {
+ font-size: 8px;
+ margin-top: 5px;
+ }
+ .section-space {
+ padding-block: 56px;
+ }
+ .section-index {
+ margin-bottom: 28px;
+ gap: 12px;
+ }
+ .section-index .mono {
+ font-size: 8px;
+ }
+ .section-index .text-link {
+ gap: 7px;
+ font-size: 7px;
+ }
+ .section-index .text-link .arrow {
+ width: 14px;
+ height: 14px;
+ }
+ .intro-grid {
+ grid-template-columns: 1fr;
+ gap: 28px;
+ }
+ .intro-grid h2 {
+ font-size: 35px;
+ line-height: 1.08;
+ }
+ .intro-copy {
+ max-width: 100%;
+ }
+ .intro-copy h3 {
+ font-size: 17px;
+ }
+ .intro-copy p {
+ font-size: 14px;
+ line-height: 1.7;
+ }
+ .intro-copy .text-link {
+ font-size: 12px;
+ }
+ .section-heading {
+ align-items: flex-start;
+ flex-direction: column;
+ gap: 21px;
+ margin-bottom: 31px;
+ }
+ .section-heading h2 {
+ font-size: 42px;
+ }
+ .section-heading > p {
+ font-size: 14px;
+ max-width: 100%;
+ margin: 0;
+ }
+ .research-explorer[data-enhanced] {
+ grid-template-columns: 1fr;
+ gap: 23px;
+ }
+ .track-selector {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 0 17px;
+ }
+ .track-tab {
+ font-size: 15px;
+ min-height: 62px;
+ gap: 9px;
+ padding: 12px 0;
+ }
+ .track-tab:nth-child(3) {
+ border-bottom: 1px solid #52705b;
+ }
+ .track-tab .track-number {
+ font-size: 8px;
+ }
+ .track-tab .arrow {
+ width: 16px;
+ }
+ .track-panel {
+ min-height: 435px;
+ padding: 23px;
+ }
+ .track-panel-top {
+ justify-content: space-between;
+ gap: 10px;
+ }
+ .track-panel-top > .mono {
+ font-size: 8px;
+ }
+ .track-panel h3 {
+ font-size: 29px;
+ max-width: 280px;
+ }
+ .track-panel > p {
+ font-size: 13px;
+ }
+ .track-symbol {
+ gap: 25px;
+ margin-block: 25px;
+ }
+ .track-symbol svg {
+ width: 104px;
+ height: 67px;
+ }
+ .track-symbol > span {
+ font-size: 8px;
+ }
+ .track-categories {
+ grid-template-columns: 1fr 1fr;
+ gap: 10px;
+ }
+ .track-categories li {
+ font-size: 8px;
+ }
+ .research-note {
+ font-size: 7px;
+ text-align: left;
+ line-height: 1.9;
+ }
+ .method-steps {
+ gap: 31px 0;
+ }
+ .method-steps li {
+ padding-right: 19px;
+ }
+ .method-steps li + li {
+ padding-left: 19px;
+ }
+ .method-steps li:nth-child(3) {
+ padding-left: 0;
+ }
+ .method-steps h3 {
+ font-size: 21px;
+ max-width: 140px;
+ }
+ .method-steps p {
+ font-size: 12px;
+ line-height: 1.65;
+ }
+ .step-number {
+ font-size: 50px;
+ }
+ .step-top .arrow {
+ width: 21px;
+ }
+ .method-footnote {
+ align-items: flex-start;
+ flex-direction: column;
+ gap: 11px;
+ margin-top: 30px;
+ }
+ .method-footnote p {
+ font-size: 12px;
+ }
+ .register-grid {
+ gap: 32px;
+ }
+ .register-copy {
+ display: block;
+ }
+ .register-copy h2 {
+ font-size: 39px;
+ margin-block: 25px 21px;
+ }
+ .register-copy p {
+ font-size: 14px;
+ max-width: 100%;
+ }
+ .register-copy .text-link {
+ font-size: 12px;
+ margin-top: 9px;
+ }
+ .register-caption {
+ font-size: 8px;
+ }
+ .register-table th {
+ font-size: 13px;
+ padding-block: 21px;
+ }
+ .register-table small {
+ font-size: 9px;
+ max-width: 175px;
+ }
+ .register-table td {
+ padding-left: 5px;
+ }
+ .record-state {
+ font-size: 8px;
+ }
+ .record-state.ready {
+ padding: 4px 5px;
+ }
+ .register-table td a {
+ width: 28px;
+ min-height: 44px;
+ }
+ .register-table td .arrow {
+ width: 16px;
+ }
+ .register-note {
+ font-size: 6px;
+ }
+ .intake-layout {
+ grid-template-columns: 1fr;
+ gap: 36px;
+ }
+ .intake-intro h3 {
+ font-size: 31px;
+ }
+ .intake-intro p {
+ font-size: 14px;
+ max-width: 100%;
+ }
+ .intake-intro .button {
+ font-size: 12px;
+ }
+ .intake-options article {
+ gap: 20px;
+ padding-block: 25px;
+ }
+ .intake-options h3 {
+ font-size: 23px;
+ }
+ .intake-options p {
+ font-size: 13px;
+ }
+ .intake-options .arrow {
+ display: block;
+ }
+ .intake-details {
+ margin-top: 30px;
+ }
+ .intake-details summary {
+ font-size: 13px;
+ padding: 18px;
+ }
+ .intake-detail-content {
+ grid-template-columns: 1fr;
+ gap: 18px;
+ padding-inline: 18px;
+ }
+ .fund-section {
+ padding-top: 55px;
+ }
+ .fund-inner {
+ grid-template-columns: 1fr;
+ gap: 40px;
+ padding-bottom: 38px;
+ }
+ .fund-copy h2 {
+ font-size: 45px;
+ margin-block: 26px 22px;
+ }
+ .fund-copy p {
+ font-size: 14px;
+ }
+ .fund-copy .text-link {
+ font-size: 12px;
+ }
+ .fund-action {
+ border-top: 1px solid #46613e;
+ padding-top: 30px;
+ }
+ .support-amount {
+ font-size: 40px;
+ max-width: 100%;
+ margin-top: 17px;
+ }
+ .fund-action > .mono {
+ font-size: 9px;
+ }
+ .fund-action .button {
+ font-size: 14px;
+ min-height: 54px;
+ }
+ .fund-action .fund-disclosure {
+ font-size: 10px;
+ max-width: 330px;
+ }
+ .independence-line {
+ align-items: flex-start;
+ gap: 13px;
+ padding-block: 24px;
+ }
+ .independence-symbol {
+ padding-right: 0;
+ margin-top: 6px;
+ font-size: 25px;
+ }
+ .independence-line p {
+ font-size: 11px;
+ }
+ .independence-line > a {
+ width: 25px;
+ height: 32px;
+ }
+ .independence-line .arrow {
+ width: 17px;
+ }
+ .site-footer {
+ padding-block: 37px 23px;
+ }
+ .footer-top {
+ flex-wrap: wrap;
+ gap: 24px;
+ padding-bottom: 29px;
+ }
+ .footer-wordmark {
+ font-size: 73px;
+ }
+ .footer-top > p {
+ font-size: 18px;
+ flex: 1;
+ margin-left: 20px;
+ }
+ .footer-top .text-link {
+ width: 100%;
+ justify-content: space-between;
+ font-size: 15px;
+ }
+ .footer-bottom {
+ display: flex;
+ flex-direction: column;
+ gap: 19px;
+ font-size: 10px;
+ }
+ .footer-bottom > span:last-child {
+ font-size: 9px;
+ line-height: 1.6;
+ max-width: 310px;
+ }
+ .terms-page h1,
+ .error-page h1 {
+ font-size: 57px;
+ }
+ .terms-page > .lead {
+ font-size: 17px;
+ }
+ .terms-grid {
+ grid-template-columns: 1fr;
+ margin-top: 35px;
+ }
+ .terms-grid section {
+ padding-block: 25px;
+ }
+ .terms-grid h2 {
+ font-size: 23px;
+ }
+ .terms-actions {
+ gap: 22px;
+ }
+ .error-page {
+ padding-block: 75px;
+ }
+ .error-page p {
+ font-size: 17px;
+ }
+}
+@media (max-width: 360px) {
+ .hero-actions {
+ gap: 16px;
+ }
+ .hero-actions .button {
+ font-size: 10px;
+ padding-inline: 12px;
+ gap: 10px;
+ }
+ .hero-actions .text-link {
+ font-size: 10px;
+ }
+ .track-panel {
+ padding: 18px;
+ }
+ .track-categories li {
+ font-size: 7px;
+ }
+ .track-tab {
+ font-size: 13px;
+ }
+ .fund-copy h2 {
+ font-size: 39px;
+ }
+ .record-state {
+ font-size: 7px;
+ }
+ .register-table th {
+ font-size: 12px;
+ }
+ .register-table small {
+ font-size: 8px;
+ }
+ .section-heading h2 {
+ font-size: 38px;
+ }
+ .intro-grid h2 {
+ font-size: 32px;
+ }
+}
+@media (prefers-reduced-motion: reduce) {
+ html {
+ scroll-behavior: auto;
+ }
+ *,
+ *::before,
+ *::after {
+ animation: none !important;
+ transition: none !important;
+ scroll-behavior: auto !important;
+ }
+}