diff --git a/.gitignore b/.gitignore
index b8a7d65..07ddd6d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,6 @@ pnpm-debug.log*
# typescript
*.tsbuildinfo
+
+# local Chrome for the devtools MCP (mise run chrome:install)
+.browser/
diff --git a/.mcp.json b/.mcp.json
new file mode 100644
index 0000000..e5d4d50
--- /dev/null
+++ b/.mcp.json
@@ -0,0 +1,13 @@
+{
+ "mcpServers": {
+ "chrome-devtools": {
+ "command": "chrome-devtools-mcp",
+ "args": [
+ "--user-data-dir=.browser/chrome/user-data",
+ "--executable-path=.browser/chrome/chrome",
+ "--usage-statistics=false"
+ ],
+ "type": "stdio"
+ }
+ }
+}
diff --git a/.prettierignore b/.prettierignore
index 61fd6f4..77c609c 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -11,3 +11,4 @@ tools/lint/anti-slop/**
plan/**
docs/adr/**
DESIGN.md
+.browser/**
diff --git a/brand/Brand Guidelines.pdf b/brand/Brand Guidelines.pdf
new file mode 100644
index 0000000..c1840de
Binary files /dev/null and b/brand/Brand Guidelines.pdf differ
diff --git a/brand/blueprint-shirt/back.png b/brand/blueprint-shirt/back.png
new file mode 100644
index 0000000..53b7be9
Binary files /dev/null and b/brand/blueprint-shirt/back.png differ
diff --git a/brand/blueprint-shirt/front.png b/brand/blueprint-shirt/front.png
new file mode 100644
index 0000000..f84e49d
Binary files /dev/null and b/brand/blueprint-shirt/front.png differ
diff --git a/brand/logo/black-full-width.svg b/brand/logo/black-full-width.svg
new file mode 100644
index 0000000..f6364de
--- /dev/null
+++ b/brand/logo/black-full-width.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/brand/logo/black.svg b/brand/logo/black.svg
new file mode 100644
index 0000000..6068b6c
--- /dev/null
+++ b/brand/logo/black.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/brand/logo/color-full-width.svg b/brand/logo/color-full-width.svg
new file mode 100644
index 0000000..64915a2
--- /dev/null
+++ b/brand/logo/color-full-width.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/brand/logo/color.svg b/brand/logo/color.svg
new file mode 100644
index 0000000..821252e
--- /dev/null
+++ b/brand/logo/color.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/functions/api/form/submit.ts b/functions/api/form/submit.ts
index 7621a3e..ece7d4d 100644
--- a/functions/api/form/submit.ts
+++ b/functions/api/form/submit.ts
@@ -7,10 +7,8 @@ export const onRequestPost: PagesFunction<{
}> = async ({ env, request }) => {
const data = await request.json();
- let key = env.TS_SECRET_KEY;
- if (!key) {
- key = "1x0000000000000000000000000000000AA";
- }
+ // Falls back to Turnstile's documented always-passes test key for local dev without a secret.
+ const key = env.TS_SECRET_KEY || "1x0000000000000000000000000000000AA";
try {
const ts = await validateTurnstile(
diff --git a/functions/util.ts b/functions/util.ts
index c793763..2c444a9 100644
--- a/functions/util.ts
+++ b/functions/util.ts
@@ -1,12 +1,5 @@
import type { APIResponse, TurnstileResponse, TurnstileVerificationResponse } from "@/types";
-/**
- * Helper function to generate response message to return to the client. Helps standardize
- * communication and error logging between API and web frontend.
- * @param apiResponse A standardized response object, shared between the API and web frontend
- * @param status HTTP status code
- * @returns A standard HTTP Response object
- */
export const res = (apiResponse: APIResponse, status: number): Response => {
if (!apiResponse.success && apiResponse.error) {
console.error(apiResponse.error);
@@ -20,13 +13,6 @@ export const res = (apiResponse: APIResponse, status: number): Response => {
});
};
-/**
- * Helper function to verify CF Turnstile challenges
- * @param secretKey Turnstile secret key (generated from the Cloudflare Dashboard)
- * @param response Response provided by the Turnstile client
- * @param ip IP Provided by the Turnstile client
- * @returns A boolean indicating whether or not the turnstile verification passed
- */
export const validateTurnstile = async (
secretKey: string,
response: string,
diff --git a/mise.lock b/mise.lock
index 3782ebf..0d2ee5d 100644
--- a/mise.lock
+++ b/mise.lock
@@ -32,6 +32,14 @@ url = "https://nodejs.org/dist/v26.7.0/node-v26.7.0-darwin-x64.tar.gz"
checksum = "sha256:d3bd72755141ed32bbcd841228ee81897c8a98d50dfa7dae2179399a0a7c90f8"
url = "https://nodejs.org/dist/v26.7.0/node-v26.7.0-win-x64.zip"
+[[tools."npm:@puppeteer/browsers"]]
+version = "3.2.1"
+backend = "npm:@puppeteer/browsers"
+
+[[tools."npm:chrome-devtools-mcp"]]
+version = "1.7.0"
+backend = "npm:chrome-devtools-mcp"
+
[[tools.pnpm]]
version = "11.22.0"
backend = "aqua:pnpm/pnpm"
diff --git a/mise.toml b/mise.toml
index 6d23adf..34b9246 100644
--- a/mise.toml
+++ b/mise.toml
@@ -7,10 +7,27 @@ npm.package_manager = "pnpm"
node = "26.7.0"
pnpm = "11.22.0"
+"npm:@puppeteer/browsers" = "3.2.1"
+"npm:chrome-devtools-mcp" = "1.7.0"
+
[env]
_.path = ["{{config_root}}/node_modules/.bin"]
ASTRO_TELEMETRY_DISABLED = "1"
+[tasks."install:chrome"]
+description = "Install Chrome for Testing"
+depends = ["clean:chrome"]
+run = """
+bin=$(browsers install chrome@stable --path="$MISE_PROJECT_ROOT/.browser" | tail -n1 | awk '{print $2}')
+ln -sfn "$bin" "$MISE_PROJECT_ROOT/.browser/chrome/chrome"
+mkdir -p "$MISE_PROJECT_ROOT/.browser/chrome/user-data"
+echo "Chrome linked: .browser/chrome/chrome -> $bin"
+"""
+
+[tasks."clean:chrome"]
+description = "Remove the installed Chrome for Testing"
+run = "rm -rf .browser/chrome"
+
[tasks.install]
run = "pnpm install"
description = "Install dependencies"
diff --git a/plan/00-overview.md b/plan/00-overview.md
index 692ac59..1386991 100644
--- a/plan/00-overview.md
+++ b/plan/00-overview.md
@@ -131,6 +131,44 @@ This directory is the complete implementation plan for rewriting scstem.org from
- [ ] Anything user-visible respects `DESIGN.md` (from Phase 02 onward).
- [ ] Phase file's acceptance checkboxes updated in the phase PR.
+## Standing rules from the midpoint review (binding for Phases 06+)
+
+A simplify + code-review + browser-verification pass ran between Phases 05 and 06 and landed as
+the **`overhaul/midpoint-checkin`** layer (stacked on `overhaul/05-app-shell`; **Phase 06 stacks
+on it**, not on 05 directly). These are the mistake patterns it found and fixed — reintroducing
+one is a review blocker:
+
+1. **Data drives chrome.** Routes, labels, and nav structure come from `src/data/site.ts`
+ (`nav.primary` with `surfaces`/`panel`, `nav.calendar`, `nav.cta`, `site.icons`,
+ `site.ogImage`). Never re-type an href or label a component can reference; never address a
+ list entry by index. The 404 page throws at build when a route it derives from `nav.primary`
+ disappears — that failure is the mechanism working, not a bug to suppress.
+2. **Tokens have one home.** Any value declared in `src/styles/global.css` `@theme` is read
+ through `@/lib/tokens` (`color`, `radius`, `duration`, `breakpoint`, `programColor`,
+ `programThemes`) — never restated as a literal in TS, frontmatter, or a meta tag. Component
+ `
-
diff --git a/src/components/ui/primitives/ChalkOval.astro b/src/components/ui/primitives/ChalkOval.astro
index 9326162..7de0186 100644
--- a/src/components/ui/primitives/ChalkOval.astro
+++ b/src/components/ui/primitives/ChalkOval.astro
@@ -1,6 +1,6 @@
---
import { cn } from "@/lib/cn";
-import { type HandTone, handToneClass } from "@/lib/hand";
+import { handMarkClass, type HandTone, handToneClass, type HandVariant } from "@/lib/hand";
/**
* A key word circled by hand (DESIGN.md §2.12) — the "Real ⬭Skills⬭. Real ⬭Robots⬭." treatment.
@@ -13,7 +13,7 @@ import { type HandTone, handToneClass } from "@/lib/hand";
* Pill-shaped UI is banned (DESIGN.md §10) — this is the sanctioned way to ring a word.
*/
interface Props {
- variant?: 1 | 2 | 3;
+ variant?: HandVariant;
/** `chalk` is white for photo contexts; `pencil` is the accent, for the page ground. */
tone?: HandTone;
class?: string;
@@ -40,7 +40,8 @@ const rotations = { 1: "-1.2deg", 2: "0.9deg", 3: "-0.6deg" } as const;