diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 0000000..209e3ef
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+20
diff --git a/AMPLIFY_DEPLOY.md b/AMPLIFY_DEPLOY.md
new file mode 100644
index 0000000..11f6df5
--- /dev/null
+++ b/AMPLIFY_DEPLOY.md
@@ -0,0 +1,171 @@
+# Deploying to AWS Amplify Hosting
+
+First-time walkthrough for shipping this app on AWS Amplify Hosting. Amplify
+auto-detects Next.js, runs the build, and routes SSR pages + API routes
+through managed Lambda behind a CloudFront CDN. The `amplify.yml` and
+`.nvmrc` in this repo are the only pieces Amplify needs from the codebase.
+
+## Before you start
+
+- An AWS account with billing enabled. Amplify Hosting has a free tier
+ (1,000 build minutes + 15 GB served + 100 GB-hours of SSR Lambda per
+ month for the first year) — typical traffic for this app stays comfortably
+ inside it.
+- GitHub access to `joelbrock/WebForm-mw`. Amplify connects via OAuth or a
+ GitHub App; either works.
+- The three required CiviCRM env vars in hand: `CIVI_BASE_URL`,
+ `CIVI_API_KEY`, `CIVI_SITE_KEY`. Plus the optional `CIVI_HTTP_AUTH_*`
+ pair if CiviCRM sits behind webserver-level Basic Auth.
+
+## Step 1 — Create the Amplify app
+
+1. Open the Amplify console:
+ - Pick a region near you / near CiviCRM. `us-east-1` (N. Virginia) is
+ the default and has the lowest cold-start latency for most setups.
+2. Click **Create new app** → **Host web app**.
+3. Choose **GitHub** as the source. Authorize Amplify if prompted.
+ The first time, AWS will install a GitHub App on your account; grant
+ it access just to `joelbrock/WebForm-mw` (not "all repos").
+4. Pick the repo `joelbrock/WebForm-mw` and the `main` branch.
+5. **App name:** something like `coop-checkin` (this becomes the
+ `*.amplifyapp.com` subdomain).
+6. Amplify reads `amplify.yml` automatically; you do not need to edit
+ the build spec on this screen. Click **Next**.
+
+## Step 2 — Environment variables
+
+On the environment-variables screen, add the following. Required ones first:
+
+| Variable | Value |
+|---|---|
+| `CIVI_BASE_URL` | e.g. `https://crm.fci.coop` |
+| `CIVI_API_KEY` | mark as **Secret** (eye icon on the value field) |
+| `CIVI_SITE_KEY` | mark as **Secret** |
+| `NODE_ENV` | `production` |
+
+Optional, only if relevant:
+
+| Variable | When |
+|---|---|
+| `CIVI_HTTP_AUTH_USER` | CiviCRM has webserver-level Basic Auth in front of it |
+| `CIVI_HTTP_AUTH_PASS` | same — mark **Secret** |
+| `HEALTH_TOKEN` | gates `/api/health` in production — mark **Secret** |
+| `PREVIEW_ADMIN_TOKEN` | gates `/api/preview-link` — mark **Secret** |
+
+"Secret" values are encrypted at rest in AWS Systems Manager Parameter
+Store and only injected into the build/runtime environment. They are not
+visible in logs or to anyone without `amplify:*` IAM permissions.
+
+Click **Next**, review, **Save and deploy**.
+
+## Step 3 — First build
+
+The first build takes about 4–6 minutes (later builds are 2–3 with cache).
+You can watch progress in the **Hosting environments** view. When it
+completes, three things are confirmed:
+
+- The build succeeded → `next build` produced `.next/`.
+- SSR functions deployed → API routes and dynamic pages have a Lambda
+ function behind them.
+- The app is reachable at `https://main..amplifyapp.com`.
+
+Smoke-test the deploy:
+
+```
+https://main..amplifyapp.com/healthz
+```
+
+Should return `200 OK` (lightweight platform check, doesn't hit CiviCRM).
+
+If you set `HEALTH_TOKEN`, the richer diagnostic at
+`/api/health?token=` will probe the CiviCRM connection itself —
+useful to confirm env vars are wired correctly.
+
+## Step 4 — Custom domain (optional)
+
+Amplify can attach a custom domain (e.g. `check-in.fci.coop`) with
+auto-issued TLS in a few minutes:
+
+1. In the app, **Hosting** → **Custom domains** → **Add domain**.
+2. Enter the apex (`fci.coop`) or a subdomain.
+3. Amplify suggests DNS records (CNAME / ALIAS). Add them at your DNS
+ provider (Cloudflare, Route53, Namecheap, etc.).
+4. Wait 5–15 minutes for validation and certificate issuance.
+
+The `*.amplifyapp.com` URL keeps working alongside the custom domain.
+
+## Step 5 — Per-PR previews (optional, recommended)
+
+Amplify can build a preview environment for every pull request:
+
+- **Hosting** → **Previews** → enable for the `joelbrock/WebForm-mw` repo.
+- PRs get their own `https://pr-..amplifyapp.com` URL,
+ posted as a comment on the PR.
+- Previews inherit the main branch's environment variables unless you
+ override on the preview branch.
+
+Useful for reviewing form changes against live CiviCRM DEV before merging.
+
+## What it'll cost
+
+Realistic estimate for this app's expected traffic (a few hundred
+form/report loads per month, very modest API throughput):
+
+- **Hosting (CDN + static)**: pennies. Free tier covers it.
+- **SSR Lambda**: ~$0.05–$0.50/month depending on traffic. Each form load
+ triggers a couple of API calls into CiviCRM via Lambda; each call is a
+ short-lived invocation.
+- **Build minutes**: free tier covers up to ~300 small builds/month.
+
+Expect **<$5/month** total at launch traffic. If usage grows
+significantly, the costs scale roughly linearly with Lambda invocations.
+
+## Things to know going in
+
+- **Cold starts.** First request after a quiet period adds 500–1500ms of
+ Lambda init. Subsequent requests reuse the warm container. For an
+ internal-use form this is fine; users see the loading state during
+ init.
+- **Outbound IPs are not static.** Amplify SSR Lambda functions egress
+ through AWS-managed IPs that change. If CiviCRM has an IP allowlist
+ on its API, this won't work out of the box — you'd need a VPC + NAT
+ Gateway + Elastic IP setup (significantly more complex). Most
+ CiviCRM auth uses API keys, not IP allowlisting, so this usually
+ isn't an issue.
+- **No Render-style background workers.** Amplify is request/response
+ only. Anything cron-shaped needs EventBridge + a separate Lambda.
+ The current app doesn't have background jobs, so this doesn't apply.
+- **Logs live in CloudWatch.** Each SSR function has its own log group;
+ click into the function from the Amplify app view to jump to logs.
+ Retention defaults to "never expire" — switch to 30 days unless you
+ need more.
+- **Secrets rotate manually.** No auto-rotation. To rotate
+ `CIVI_API_KEY`, update it in Amplify env vars → trigger a redeploy.
+- **`render.yaml` is now informational.** It's still in the repo for the
+ Render path; Amplify ignores it. If you commit fully to Amplify and
+ abandon Render, the `render.yaml` and the Render section of
+ `DEPLOYMENT.md` can be removed.
+
+## When something breaks
+
+- **Build fails on `npm ci`** → usually a Node version mismatch. The
+ `.nvmrc` pins Node 20; if Amplify's build image doesn't have it, the
+ `amplify.yml` `nvm install` falls back. Open the build log, search
+ for "Node version".
+- **Build fails on `next build`** → typecheck or lint error. Reproduce
+ locally with `npm run build`.
+- **Site builds but routes 503** → SSR Lambda misconfig. Check the
+ function's CloudWatch logs for the actual error. Most common cause:
+ missing required env var (the app refuses to boot in production stub
+ mode and the `lib/env.ts` validator throws).
+- **CiviCRM calls fail with 401** → API key or Basic Auth credentials
+ wrong. Re-check `CIVI_API_KEY` and `CIVI_HTTP_AUTH_*` in the Amplify
+ env-vars panel; redeploy after changes.
+
+## After the first successful deploy
+
+- Bookmark the Amplify app URL and the CloudWatch log group.
+- Run the email/link smoke test from `EMAIL_DELIVERY.md` against the
+ Amplify URL to confirm `/api/preview-link` produces working tokenized
+ URLs end-to-end.
+- Update any docs / staff runbooks pointing at the old Render URL.
diff --git a/README.md b/README.md
index 5ec3c24..a7fab2a 100644
--- a/README.md
+++ b/README.md
@@ -105,11 +105,11 @@ works while in stub mode. The report is at `/report?cid=1&cs=anything`.
## Deploy
-The repo ships with `render.yaml` for [Render](https://render.com) and a
-detailed `DEPLOYMENT.md` covering Render specifically. The app is a
+The repo ships with `amplify.yml` for [AWS Amplify Hosting](https://aws.amazon.com/amplify/)
+(see `AMPLIFY_DEPLOY.md` for a first-time walkthrough) and `render.yaml`
+for [Render](https://render.com) (see `DEPLOYMENT.md`). The app is a
standard Next.js 16 App Router project and runs anywhere Node 20+ can
-run `next start` — Vercel, AWS Amplify Hosting, App Runner, Fly,
-self-hosted, etc.
+run `next start` — Vercel, App Runner, Fly, self-hosted, etc.
Build + start:
diff --git a/amplify.yml b/amplify.yml
new file mode 100644
index 0000000..1615c3d
--- /dev/null
+++ b/amplify.yml
@@ -0,0 +1,20 @@
+version: 1
+applications:
+ - frontend:
+ phases:
+ preBuild:
+ commands:
+ - nvm use $(cat .nvmrc) || nvm install $(cat .nvmrc)
+ - npm ci --cache .npm --prefer-offline
+ build:
+ commands:
+ - npm run build
+ artifacts:
+ baseDirectory: .next
+ files:
+ - '**/*'
+ cache:
+ paths:
+ - node_modules/**/*
+ - .next/cache/**/*
+ - .npm/**/*