From 7f9dbdd068d88873a10b248d6e8970f3ad51efc2 Mon Sep 17 00:00:00 2001 From: Viktor Vaczi Date: Sun, 7 Jun 2026 14:59:13 +0200 Subject: [PATCH] =?UTF-8?q?feat(site):=20=F0=9F=9B=AC=20PCBJam=20landing?= =?UTF-8?q?=20page=20=E2=80=94=20waitlist,=20sections,=20Tailwind?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the placeholder homepage with the PCBJam waitlist conversion page (13 sections from the copy spec), shared chrome, and a real email-capture backend, keeping the site zero-runtime-JS / zero-web-font. - Tailwind v4 via @tailwindcss/postcss (the Vite plugin breaks on Astro 6 rolldown-vite); Preflight skipped; brand tokens in :root + @theme inline. - Waitlist: /api/waitlist serverless route (prerender=false) + Resend, with dual JSON/303 responses; WaitlistForm progressive enhancement bound on astro:page-load (survives ClientRouter swaps); honeypot + validation. - New components (Icon, Placeholder, DemoVideo, SectionBand, Chip, EECredit, StatBar, Pillar, Step, CheckItem, WaitlistForm) + 13 section components. - Redesigned Header (sticky, mobile menu, CTA) + 4-col Footer (KiCad attribution); BaseLayout gains full-bleed mode + OG/canonical meta. - Assets scaffolded with marked placeholders; EE/generic/client SVGs copied. Co-Authored-By: Claude Opus 4.8 (1M context) --- site/.env.example | 14 + site/package-lock.json | 689 +++++++++++++++++++++- site/package.json | 7 +- site/postcss.config.mjs | 9 + site/public/clients/axdraft.svg | 28 + site/public/clients/chapterly.svg | 1 + site/public/clients/filtered.svg | 13 + site/public/clients/lex.svg | 28 + site/public/clients/memrise.svg | 17 + site/public/clients/skiff.svg | 11 + site/public/clients/swaralink.svg | 9 + site/public/ee/ee-logo.svg | 36 ++ site/public/ee/ee-mark.svg | 9 + site/public/ee/stripe-partner.svg | 1 + site/public/icons/ai.svg | 9 + site/public/icons/arrow-outward.svg | 8 + site/public/icons/cloud.svg | 50 ++ site/public/icons/collab.svg | 9 + site/public/icons/divider-dot.svg | 10 + site/public/icons/github.svg | 10 + site/public/icons/idea.svg | 9 + site/public/icons/mail.svg | 3 + site/public/icons/message.svg | 11 + site/public/icons/play.svg | 1 + site/public/icons/product.svg | 9 + site/public/icons/prototype.svg | 9 + site/public/icons/responsive.svg | 126 ++++ site/public/icons/right-arrow.svg | 3 + site/public/icons/yjs.svg | 92 +++ site/public/icons/you-are-here-mobile.svg | 5 + site/public/icons/you-are-here.svg | 5 + site/src/components/CheckItem.astro | 26 + site/src/components/Chip.astro | 15 + site/src/components/DemoVideo.astro | 133 +++++ site/src/components/EECredit.astro | 66 +++ site/src/components/Footer.astro | 135 ++++- site/src/components/Header.astro | 164 ++++- site/src/components/Icon.astro | 57 ++ site/src/components/Pillar.astro | 35 ++ site/src/components/Placeholder.astro | 63 ++ site/src/components/SectionBand.astro | 55 ++ site/src/components/StatBar.astro | 48 ++ site/src/components/Step.astro | 52 ++ site/src/components/WaitlistForm.astro | 203 +++++++ site/src/layouts/BaseLayout.astro | 57 +- site/src/pages/api/waitlist.ts | 99 ++++ site/src/pages/index.astro | 88 ++- site/src/sections/AiNote.astro | 37 ++ site/src/sections/BuiltByEmergence.astro | 88 +++ site/src/sections/CapabilityProof.astro | 88 +++ site/src/sections/FinalCta.astro | 52 ++ site/src/sections/Hero.astro | 100 ++++ site/src/sections/HowItWorks.astro | 44 ++ site/src/sections/MakeMoney.astro | 52 ++ site/src/sections/MigrationStrip.astro | 68 +++ site/src/sections/Multiplayer.astro | 84 +++ site/src/sections/OpenYours.astro | 75 +++ site/src/sections/Pillars.astro | 24 + site/src/sections/PositioningMap.astro | 21 + site/src/sections/TrustBar.astro | 23 + site/src/styles/global.css | 284 +++++++-- 61 files changed, 3460 insertions(+), 117 deletions(-) create mode 100644 site/.env.example create mode 100644 site/postcss.config.mjs create mode 100644 site/public/clients/axdraft.svg create mode 100644 site/public/clients/chapterly.svg create mode 100644 site/public/clients/filtered.svg create mode 100644 site/public/clients/lex.svg create mode 100644 site/public/clients/memrise.svg create mode 100644 site/public/clients/skiff.svg create mode 100644 site/public/clients/swaralink.svg create mode 100644 site/public/ee/ee-logo.svg create mode 100644 site/public/ee/ee-mark.svg create mode 100644 site/public/ee/stripe-partner.svg create mode 100644 site/public/icons/ai.svg create mode 100644 site/public/icons/arrow-outward.svg create mode 100644 site/public/icons/cloud.svg create mode 100644 site/public/icons/collab.svg create mode 100644 site/public/icons/divider-dot.svg create mode 100644 site/public/icons/github.svg create mode 100644 site/public/icons/idea.svg create mode 100644 site/public/icons/mail.svg create mode 100644 site/public/icons/message.svg create mode 100644 site/public/icons/play.svg create mode 100644 site/public/icons/product.svg create mode 100644 site/public/icons/prototype.svg create mode 100644 site/public/icons/responsive.svg create mode 100644 site/public/icons/right-arrow.svg create mode 100644 site/public/icons/yjs.svg create mode 100644 site/public/icons/you-are-here-mobile.svg create mode 100644 site/public/icons/you-are-here.svg create mode 100644 site/src/components/CheckItem.astro create mode 100644 site/src/components/Chip.astro create mode 100644 site/src/components/DemoVideo.astro create mode 100644 site/src/components/EECredit.astro create mode 100644 site/src/components/Icon.astro create mode 100644 site/src/components/Pillar.astro create mode 100644 site/src/components/Placeholder.astro create mode 100644 site/src/components/SectionBand.astro create mode 100644 site/src/components/StatBar.astro create mode 100644 site/src/components/Step.astro create mode 100644 site/src/components/WaitlistForm.astro create mode 100644 site/src/pages/api/waitlist.ts create mode 100644 site/src/sections/AiNote.astro create mode 100644 site/src/sections/BuiltByEmergence.astro create mode 100644 site/src/sections/CapabilityProof.astro create mode 100644 site/src/sections/FinalCta.astro create mode 100644 site/src/sections/Hero.astro create mode 100644 site/src/sections/HowItWorks.astro create mode 100644 site/src/sections/MakeMoney.astro create mode 100644 site/src/sections/MigrationStrip.astro create mode 100644 site/src/sections/Multiplayer.astro create mode 100644 site/src/sections/OpenYours.astro create mode 100644 site/src/sections/Pillars.astro create mode 100644 site/src/sections/PositioningMap.astro create mode 100644 site/src/sections/TrustBar.astro diff --git a/site/.env.example b/site/.env.example new file mode 100644 index 0000000..1520106 --- /dev/null +++ b/site/.env.example @@ -0,0 +1,14 @@ +# PCBJam marketing site — server-side secrets for the waitlist endpoint. +# Copy to `.env` for local dev, and set these in the Vercel project settings. +# Without RESEND_API_KEY the endpoint still accepts submits (logs + no email), +# so the form UX is testable locally without keys. + +# Resend API key (https://resend.com/api-keys). Server-only — never PUBLIC_. +RESEND_API_KEY= + +# Resend Audience to add waitlist contacts to (https://resend.com/audiences). +RESEND_AUDIENCE_ID= + +# Optional: confirmation sender. Domain must be verified in Resend. +# Defaults to "PCBJam ". +WAITLIST_FROM_EMAIL= diff --git a/site/package-lock.json b/site/package-lock.json index dded199..7656880 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -9,12 +9,30 @@ "version": "0.0.1", "dependencies": { "@astrojs/vercel": "^10.0.8", - "astro": "^6.4.4" + "astro": "^6.4.4", + "resend": "^6.12.4" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.3.0", + "tailwindcss": "^4.3.0" }, "engines": { "node": ">=22.12.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@astrojs/compiler": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-4.0.0.tgz", @@ -1098,12 +1116,55 @@ "node": ">=18.0.0" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", @@ -1578,6 +1639,283 @@ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "license": "MIT" }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.0.tgz", + "integrity": "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "postcss": "^8.5.10", + "tailwindcss": "4.3.0" + } + }, "node_modules/@types/debug": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", @@ -2429,6 +2767,20 @@ "node": ">=4" } }, + "node_modules/enhanced-resolve": { + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz", + "integrity": "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -2555,6 +2907,12 @@ "license": "MIT", "optional": true }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "license": "Unlicense" + }, "node_modules/fast-string-truncated-width": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", @@ -3042,6 +3400,16 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-yaml": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", @@ -3077,6 +3445,267 @@ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "license": "MIT" }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "devOptional": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -4329,6 +4958,12 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/postal-mime": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/postal-mime/-/postal-mime-2.7.4.tgz", + "integrity": "sha512-0WdnFQYUrPGGTFu1uOqD2s7omwua8xaeYGdO6rb88oD5yJ/4pPHDA4sdWqfD8wQVfCny563n/HQS7zTFft+f/g==", + "license": "MIT-0" + }, "node_modules/postcss": { "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", @@ -4571,6 +5206,27 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/resend": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/resend/-/resend-6.12.4.tgz", + "integrity": "sha512-lRpJ2Hxd+ht+JPDm97juRcUp9HOMuZyxaRFRFmc9Tx8iNWiei94Dx9v6SWufgKk2667C/uCeKKspMotOHSpCSg==", + "license": "MIT", + "dependencies": { + "postal-mime": "2.7.4", + "standardwebhooks": "1.0.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@react-email/render": "*" + }, + "peerDependenciesMeta": { + "@react-email/render": { + "optional": true + } + } + }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -4843,6 +5499,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/standardwebhooks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", @@ -4891,6 +5557,27 @@ "url": "https://opencollective.com/svgo" } }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/tar": { "version": "7.5.16", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.16.tgz", diff --git a/site/package.json b/site/package.json index 5c7862c..8dd4cc7 100644 --- a/site/package.json +++ b/site/package.json @@ -13,7 +13,12 @@ "astro": "astro" }, "dependencies": { + "@astrojs/vercel": "^10.0.8", "astro": "^6.4.4", - "@astrojs/vercel": "^10.0.8" + "resend": "^6.12.4" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.3.0", + "tailwindcss": "^4.3.0" } } diff --git a/site/postcss.config.mjs b/site/postcss.config.mjs new file mode 100644 index 0000000..63a5935 --- /dev/null +++ b/site/postcss.config.mjs @@ -0,0 +1,9 @@ +// Tailwind v4 via PostCSS. +// NOTE: we use the PostCSS plugin (not @tailwindcss/vite) on purpose — the Vite +// plugin currently fails to build under Astro 6's rolldown-vite (withastro/astro#16542). +// Astro auto-detects this file; no astro.config.mjs change is needed. +export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +}; diff --git a/site/public/clients/axdraft.svg b/site/public/clients/axdraft.svg new file mode 100644 index 0000000..abea681 --- /dev/null +++ b/site/public/clients/axdraft.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/site/public/clients/chapterly.svg b/site/public/clients/chapterly.svg new file mode 100644 index 0000000..36ad815 --- /dev/null +++ b/site/public/clients/chapterly.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/public/clients/filtered.svg b/site/public/clients/filtered.svg new file mode 100644 index 0000000..32d31a8 --- /dev/null +++ b/site/public/clients/filtered.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/site/public/clients/lex.svg b/site/public/clients/lex.svg new file mode 100644 index 0000000..42b65f4 --- /dev/null +++ b/site/public/clients/lex.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/site/public/clients/memrise.svg b/site/public/clients/memrise.svg new file mode 100644 index 0000000..73db065 --- /dev/null +++ b/site/public/clients/memrise.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/site/public/clients/skiff.svg b/site/public/clients/skiff.svg new file mode 100644 index 0000000..40346d2 --- /dev/null +++ b/site/public/clients/skiff.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/site/public/clients/swaralink.svg b/site/public/clients/swaralink.svg new file mode 100644 index 0000000..80a2078 --- /dev/null +++ b/site/public/clients/swaralink.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/site/public/ee/ee-logo.svg b/site/public/ee/ee-logo.svg new file mode 100644 index 0000000..3a6b40a --- /dev/null +++ b/site/public/ee/ee-logo.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/site/public/ee/ee-mark.svg b/site/public/ee/ee-mark.svg new file mode 100644 index 0000000..9766cd3 --- /dev/null +++ b/site/public/ee/ee-mark.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/site/public/ee/stripe-partner.svg b/site/public/ee/stripe-partner.svg new file mode 100644 index 0000000..398024b --- /dev/null +++ b/site/public/ee/stripe-partner.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/public/icons/ai.svg b/site/public/icons/ai.svg new file mode 100644 index 0000000..1a0bbb2 --- /dev/null +++ b/site/public/icons/ai.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/site/public/icons/arrow-outward.svg b/site/public/icons/arrow-outward.svg new file mode 100644 index 0000000..4333182 --- /dev/null +++ b/site/public/icons/arrow-outward.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/site/public/icons/cloud.svg b/site/public/icons/cloud.svg new file mode 100644 index 0000000..77ea073 --- /dev/null +++ b/site/public/icons/cloud.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/site/public/icons/collab.svg b/site/public/icons/collab.svg new file mode 100644 index 0000000..a9d2c31 --- /dev/null +++ b/site/public/icons/collab.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/site/public/icons/divider-dot.svg b/site/public/icons/divider-dot.svg new file mode 100644 index 0000000..a74c088 --- /dev/null +++ b/site/public/icons/divider-dot.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/site/public/icons/github.svg b/site/public/icons/github.svg new file mode 100644 index 0000000..3dde813 --- /dev/null +++ b/site/public/icons/github.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/site/public/icons/idea.svg b/site/public/icons/idea.svg new file mode 100644 index 0000000..bbdbc22 --- /dev/null +++ b/site/public/icons/idea.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/site/public/icons/mail.svg b/site/public/icons/mail.svg new file mode 100644 index 0000000..3c780bf --- /dev/null +++ b/site/public/icons/mail.svg @@ -0,0 +1,3 @@ + + + diff --git a/site/public/icons/message.svg b/site/public/icons/message.svg new file mode 100644 index 0000000..ff8f20d --- /dev/null +++ b/site/public/icons/message.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/site/public/icons/play.svg b/site/public/icons/play.svg new file mode 100644 index 0000000..c39461a --- /dev/null +++ b/site/public/icons/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/public/icons/product.svg b/site/public/icons/product.svg new file mode 100644 index 0000000..a9040a0 --- /dev/null +++ b/site/public/icons/product.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/site/public/icons/prototype.svg b/site/public/icons/prototype.svg new file mode 100644 index 0000000..5e166eb --- /dev/null +++ b/site/public/icons/prototype.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/site/public/icons/responsive.svg b/site/public/icons/responsive.svg new file mode 100644 index 0000000..2e03199 --- /dev/null +++ b/site/public/icons/responsive.svg @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/site/public/icons/right-arrow.svg b/site/public/icons/right-arrow.svg new file mode 100644 index 0000000..08950a9 --- /dev/null +++ b/site/public/icons/right-arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/site/public/icons/yjs.svg b/site/public/icons/yjs.svg new file mode 100644 index 0000000..171fac6 --- /dev/null +++ b/site/public/icons/yjs.svg @@ -0,0 +1,92 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/site/public/icons/you-are-here-mobile.svg b/site/public/icons/you-are-here-mobile.svg new file mode 100644 index 0000000..8b89565 --- /dev/null +++ b/site/public/icons/you-are-here-mobile.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/site/public/icons/you-are-here.svg b/site/public/icons/you-are-here.svg new file mode 100644 index 0000000..37722b4 --- /dev/null +++ b/site/public/icons/you-are-here.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/site/src/components/CheckItem.astro b/site/src/components/CheckItem.astro new file mode 100644 index 0000000..00dab73 --- /dev/null +++ b/site/src/components/CheckItem.astro @@ -0,0 +1,26 @@ +--- +/** CheckItem — a ✓ capability row (§12). Render inside a
    . */ +interface Props { + label: string; +} +const { label } = Astro.props; +--- + +
  • + + {label} +
  • + + diff --git a/site/src/components/Chip.astro b/site/src/components/Chip.astro new file mode 100644 index 0000000..13cbec8 --- /dev/null +++ b/site/src/components/Chip.astro @@ -0,0 +1,15 @@ +--- +/** Chip — a mono file/feature chip (e.g. `.kicad_pcb`, "Self-hostable"). */ +interface Props { + label?: string; + icon?: string; // optional /public icon path for a leading mark + class?: string; +} +const { label, icon, class: cls = '' } = Astro.props; +import Icon from './Icon.astro'; +--- + + + {icon && } + {label} + diff --git a/site/src/components/DemoVideo.astro b/site/src/components/DemoVideo.astro new file mode 100644 index 0000000..dc8c327 --- /dev/null +++ b/site/src/components/DemoVideo.astro @@ -0,0 +1,133 @@ +--- +/** + * DemoVideo — the multiplayer demo centerpiece. + * + * Poster-first and lazy (`preload="none"`); never autoplays in markup. A tiny + * IntersectionObserver (bound on astro:page-load, see diff --git a/site/src/components/EECredit.astro b/site/src/components/EECredit.astro new file mode 100644 index 0000000..f09f2eb --- /dev/null +++ b/site/src/components/EECredit.astro @@ -0,0 +1,66 @@ +--- +/** + * EECredit — the "Built by Emergence Engineering" lockup. The EE wordmark keeps + * its own (guest-brand) colors via Icon mode="img". + * - variant="footer": wordmark + studio blurb + contact (footer column 3). + * - variant="inline": compact wordmark + "Built by" label. + */ +import Icon from './Icon.astro'; + +interface Props { + variant?: 'footer' | 'inline'; + class?: string; +} +const { variant = 'footer', class: cls = '' } = Astro.props; +--- + +{ + variant === 'inline' ? ( + + Built by + + + ) : ( + + ) +} + + diff --git a/site/src/components/Footer.astro b/site/src/components/Footer.astro index a6c5283..5b69d78 100644 --- a/site/src/components/Footer.astro +++ b/site/src/components/Footer.astro @@ -1,14 +1,135 @@ --- +import Icon from './Icon.astro'; +import EECredit from './EECredit.astro'; + const year = new Date().getFullYear(); +// TODO(links): add Roadmap (/roadmap) and dedicated How-it-works/Open pages when they ship. +const GITHUB_URL = 'https://github.com/emergence-engineering'; --- + + diff --git a/site/src/components/Header.astro b/site/src/components/Header.astro index e0dc793..144326a 100644 --- a/site/src/components/Header.astro +++ b/site/src/components/Header.astro @@ -1,18 +1,170 @@ --- +import Icon from './Icon.astro'; + +// TODO(assets): replace favicon "K" + text wordmark with the real PCBJam logo (§4b.1). +// TODO(links): add "Roadmap" → /roadmap and repoint How-it-works/Open to their own +// pages once the supporting pages ship; for now they anchor to landing sections. +// TODO(config): point at the public PCBJam repo when it exists. +const GITHUB_URL = 'https://github.com/emergence-engineering'; + +const links = [ + { href: '/#how-it-works', label: 'How it works' }, + { href: '/#open', label: 'Open & yours' }, + { href: '/blog', label: 'Blog' }, + { href: '/#built-by', label: 'Built by Emergence Engineering' }, +]; + const path = Astro.url.pathname; const isActive = (href: string) => - href === '/' ? path === '/' : path.startsWith(href); + href.startsWith('/#') ? false : href === '/' ? path === '/' : path.startsWith(href); --- + + + + diff --git a/site/src/components/Icon.astro b/site/src/components/Icon.astro new file mode 100644 index 0000000..a39fbbc --- /dev/null +++ b/site/src/components/Icon.astro @@ -0,0 +1,57 @@ +--- +/** + * Icon — renders an SVG from /public. + * + * mode="mask" (default): paints the SVG silhouette with `currentColor` via a CSS + * mask, so monochrome glyphs (github, arrows, play, mail…) tint to the brand + * regardless of the file's own hardcoded fills. + * mode="img": a plain , preserving the file's original colors — use for + * multicolor illustrations, brand wordmarks, and client/partner logos. + * + * `name` is a path under /public, e.g. "icons/github.svg" or "clients/skiff.svg". + */ +interface Props { + name: string; + alt?: string; + size?: string; // any CSS length; defaults to 1.25rem + mode?: 'mask' | 'img'; + class?: string; +} + +const { name, alt = '', size = '1.25rem', mode = 'mask', class: cls = '' } = Astro.props; +const src = `/${name.replace(/^\/+/, '')}`; +const decorative = alt === ''; +--- + +{ + mode === 'mask' ? ( + + ) : ( + {alt} + ) +} + + diff --git a/site/src/components/Pillar.astro b/site/src/components/Pillar.astro new file mode 100644 index 0000000..88ad713 --- /dev/null +++ b/site/src/components/Pillar.astro @@ -0,0 +1,35 @@ +--- +/** Pillar — one value-pillar card (§10). Body via default slot. */ +import Icon from './Icon.astro'; +interface Props { + title: string; + icon?: string; + iconMode?: 'mask' | 'img'; +} +const { title, icon, iconMode = 'mask' } = Astro.props; +--- + +
    + {icon && } +

    {title}

    +

    +
    + + diff --git a/site/src/components/Placeholder.astro b/site/src/components/Placeholder.astro new file mode 100644 index 0000000..e492a42 --- /dev/null +++ b/site/src/components/Placeholder.astro @@ -0,0 +1,63 @@ +--- +/** + * Placeholder — a clearly-marked, dashed-box slot for an asset that doesn't + * exist yet (PCBJam logo, demo video, product screenshots…). Real assets drop + * into the same spot later by swapping this component for an /. + * + * The `label` should name what to produce + the spec ref, e.g. + * "PRODUCE: multiplayer demo loop (§4b.2)" + */ +interface Props { + label: string; + ratio?: string; // e.g. "16 / 9", "1 / 1"; default 16/9 + kind?: 'image' | 'video' | 'logo'; + class?: string; +} + +const { label, ratio = '16 / 9', kind = 'image', class: cls = '' } = Astro.props; +const glyph = kind === 'video' ? '▶' : kind === 'logo' ? '◆' : '▦'; +--- + + + + diff --git a/site/src/components/SectionBand.astro b/site/src/components/SectionBand.astro new file mode 100644 index 0000000..dde9655 --- /dev/null +++ b/site/src/components/SectionBand.astro @@ -0,0 +1,55 @@ +--- +/** + * SectionBand — a full-width section wrapper. Centers its content in the shared + * .container with default vertical rhythm; optionally tints the band + * (`tone="soft"`) and/or paints the faint blueprint dot-grid motif (`grid`). + * Pass `flush` to drop the default block padding (e.g. the thin stat bar). + * Pass an `id` for in-page anchor links. + */ +interface Props { + id?: string; + tone?: 'default' | 'soft'; + grid?: boolean; + flush?: boolean; + class?: string; + labelledby?: string; +} + +const { + id, + tone = 'default', + grid = false, + flush = false, + class: cls = '', + labelledby, +} = Astro.props; +const bandClass = ['band', tone === 'soft' ? 'band-soft' : '', grid ? 'bp-grid' : '', cls] + .filter(Boolean) + .join(' '); +const innerClass = ['container', flush ? '' : 'band-inner'].filter(Boolean).join(' '); +--- + +
    +
    + +
    +
    + + diff --git a/site/src/components/StatBar.astro b/site/src/components/StatBar.astro new file mode 100644 index 0000000..e4d90d6 --- /dev/null +++ b/site/src/components/StatBar.astro @@ -0,0 +1,48 @@ +--- +/** StatBar — thin band of mono numerals (§8). */ +interface Props { + stats: { value: string; label: string }[]; +} +const { stats } = Astro.props; +--- + +
    + { + stats.map((s) => ( +
    +
    {s.value}
    +
    {s.label}
    +
    + )) + } +
    + + diff --git a/site/src/components/Step.astro b/site/src/components/Step.astro new file mode 100644 index 0000000..8e3ac99 --- /dev/null +++ b/site/src/components/Step.astro @@ -0,0 +1,52 @@ +--- +/** Step — one numbered "how a jam works" step (§11). Body via default slot. */ +import Icon from './Icon.astro'; +interface Props { + n: number; + title: string; + icon?: string; +} +const { n, title, icon } = Astro.props; +--- + +
    +
    + {n} + {icon && } +
    +

    {title}

    +

    +
    + + diff --git a/site/src/components/WaitlistForm.astro b/site/src/components/WaitlistForm.astro new file mode 100644 index 0000000..4071e47 --- /dev/null +++ b/site/src/components/WaitlistForm.astro @@ -0,0 +1,203 @@ +--- +/** + * WaitlistForm — email capture with progressive enhancement. + * - Works with NO JS: native POST to /api/waitlist → 303 back to /?waitlist=ok. + * - With JS: fetch + inline success state, no navigation. + * Reused in the hero, final CTA, and footer via the `source` prop (attribution). + * The script is bound on astro:page-load so it survives ClientRouter SPA swaps. + */ +interface Props { + source: string; // 'hero' | 'final' | 'footer' … + id?: string; // wrapper id; pass "waitlist" on the hero for the #waitlist anchor + layout?: 'row' | 'stack'; + microcopy?: string; + class?: string; +} + +const { + source, + id, + layout = 'row', + microcopy = "Early access rolls out in waves. We'll email you when your seat opens — no spam.", + class: cls = '', +} = Astro.props; + +const emailId = `wl-email-${source}`; +const hpId = `wl-hp-${source}`; +--- + +
    +
    + + + + {/* Honeypot — humans never see/fill this. */} + + + + +
    + + + + + + {microcopy &&

    {microcopy}

    } +
    + + + + diff --git a/site/src/layouts/BaseLayout.astro b/site/src/layouts/BaseLayout.astro index 17c78f5..8a6d9e1 100644 --- a/site/src/layouts/BaseLayout.astro +++ b/site/src/layouts/BaseLayout.astro @@ -7,14 +7,24 @@ import '../styles/global.css'; interface Props { title: string; description?: string; + /** When true, render the page edge-to-edge (no .container) so full-width + * bands work; each section manages its own container. Used by the landing + * page. Blog/legal keep the default centered container. */ + wide?: boolean; + /** Absolute or root-relative OG/Twitter share image. */ + ogImage?: string; } -const { title, description = 'KiCad in the browser — WebAssembly port.' } = - Astro.props; -const fullTitle = - title === 'KiCad in the browser' - ? title - : `${title} — KiCad in the browser`; +const { + title, + description = 'PCBJam — open-source PCB design in your browser, with real-time multiplayer. Built on KiCad.', + wide = false, + ogImage = '/og/og-home.png', +} = Astro.props; + +// Pages may pass a complete title (containing "PCBJam"); otherwise append the suffix. +const fullTitle = title.includes('PCBJam') ? title : `${title} — PCBJam`; +const canonical = Astro.url.href; --- @@ -23,18 +33,43 @@ const fullTitle = + {fullTitle} + + {/* Open Graph / Twitter */} + + + + + + + + + + + + {/* Per-page extras (e.g. JSON-LD) */} + + {/* SPA-style navigation: swaps the page without a full reload. */}
    -
    -
    - -
    -
    + { + wide ? ( +
    + +
    + ) : ( +
    +
    + +
    +
    + ) + }