diff --git a/obsidian/Crussell/Future Work - Gap Backlog.md b/obsidian/Crussell/Future Work - Gap Backlog.md
index fad19fa..ab2a112 100644
--- a/obsidian/Crussell/Future Work - Gap Backlog.md
+++ b/obsidian/Crussell/Future Work - Gap Backlog.md
@@ -35,6 +35,7 @@ These are things that work fine in dev (with mocks) but need real implementation
| P10 | **No automated database backups** | M (1d) | Infrastructure | PostgreSQL volume is persistent in Docker but no `pg_dump` cron, no point-in-time recovery. | Standard production DB setup task. |
| P12 | **Square sandbox smoke test (pre-go-live gate)** | S-M (1d, once credentials available) | E2E | **BLOCKED — no real Square credentials available.** Must exercise the real API path end-to-end: new-card tokenization → payment → saved card → refund → reconcile, against Square's sandbox. Also verifies the M-8 open question (is `card.customer_id` enforced as Required?). | The dev mock cannot exercise Square's real wire contract (key-length limits, `device_options`, refund statuses, error codes). This is the sole remaining item before the production flip. See `plans/p11-square-web-payments-sdk.md` Remaining Items. |
| P13 | **Reconcile deterministically-keyed saved-card charges** | S (2-3h) | Backend | **Deferred — deliberate trade-off (N-OBS-1).** The admin "Charge Saved Card" idempotency key `bookingID-sc-type-amount-cardID` dedups two *identical* repeat charges on one booking. Not UI-reachable today (PaymentModal always sends the current `totalDue`, which changes after each charge). | Revisit if the admin flow ever gains a "charge exact amount twice" path — the key would then need a client nonce or attempt counter. Tracked from the final payment review. |
+| P14 | **Square customer provisioning & consent** | S-M (1-2d) | Backend + Frontend + Docs | **PLANNED — gated on P12.** The final payment review flagged that Square's docs mark `card.customer_id` as Required on `POST /v2/cards` and on `ccof:` charges. If enforced, saved-card flows 400 in production. Plan: lazily provision Square customers only when a user saves a card, persist `square_customer_id`, charge one-off new-card payments directly with the `cnon:` nonce (no card/customer minted for non-savers or guests), update the privacy policy (Square as processor) and the card-save checkbox copy. **No standalone consent checkbox is required** — the existing card-save checkbox covers it. Includes a policy pop-over on the consent checkbox linking to `/privacy-policy` (mirrors the existing `/cancellation-policy` pop-over pattern — generalise `policyPopover.svelte`, new `/privacy-policy` route). Privacy Policy §2.2 + Terms §3.2 already drafted into the placeholders (Aug 2026). See `plans/p14-square-customer-provisioning-consent.md`. | The app currently mints a Square card-on-file for *every* new-card payment (even when not saving); if customer provisioning becomes mandatory, that would create customer profiles for all payers including guests. Data-minimisation design avoids this. Must sandbox-test first (P12): Square may not actually enforce `customer_id` (M-8/R2 open question). |
---
diff --git a/obsidian/Crussell/Privacy Policy.md b/obsidian/Crussell/Privacy Policy.md
index cd33ff8..bf11e65 100644
--- a/obsidian/Crussell/Privacy Policy.md
+++ b/obsidian/Crussell/Privacy Policy.md
@@ -1,7 +1,7 @@
# Privacy Policy
-**Last Updated:** June 2026
-**Status:** DRAFT — Local development (not yet in production)
+**Last Updated:** August 2026
+**Status:** DRAFT — Local development (not yet in production). **§2.2 (Saved Cards & Square) is drafted from the P14 plan; the placeholder sections below still need to be made 'real' before go-live.**
---
@@ -39,9 +39,23 @@ Email: help@crussell.invalid
- Gift card codes and balances
- Account balances
- Payment transaction records (processed via Square, not stored by us)
+- Saved-card references (tokenised, stored with our payment provider Square — see §2.3)
- Dormant balance records (Account ID only, no PII)
-### 2.2 Special Category Data (Health Data)
+### 2.2 Saved Cards & Payment Provider (Square)
+
+When you choose to **save a card for next time**, we store a tokenised reference to your card with our payment processor, **Square** (a data processor), rather than on our own systems.
+
+- **What Square stores:** a tokenised reference to your card (never your full card number or CVV), plus the name and email address we already hold on your account, grouped into a Square customer profile.
+- **Lawful basis:** UK GDPR Article 6(1)(b) — necessary for the performance of the contract (you asked to save your card for future payments).
+- **Why:** so you can pay for future bookings, tips, or gift-card purchases without re-entering your card details.
+- **One-off payments:** if you do not tick "save this card", **no card is stored and no Square customer profile is created** for you — your card is used only for that single payment.
+- **Retention & removal:** the reference remains stored until you delete the card from your account (Account → Saved Cards) or your account is deleted. You can remove a saved card at any time.
+- **Square's privacy policy:** [Square Privacy Policy](https://squareup.com/gb/en/legal/privacy-no-account) applies to data Square holds on our behalf.
+
+We never store full card numbers, card security codes (CVV), or card expiry data on our own systems at any point.
+
+### 2.3 Special Category Data (Health Data)
We collect health-related information with your **explicit consent**:
- Allergy records
@@ -64,6 +78,7 @@ We collect health-related information with your **explicit consent**:
| **Inactive accounts (no balance)** | 2 years idle | GDPR storage limitation |
| **Inactive accounts (with balance)** | 5 years idle | Scottish prescriptive period |
| **Financial records** | 7 years | HMRC requirement |
+| **Saved-card references (Square)** | Until user deletes card or account is deleted (Square-side) | Contract performance (Art 6(1)(b)); card-network card-on-file rules |
| **Allergy/health records** | 7 years | Insurance requirement |
| **Dormant balances** | Indefinite (Account ID only) | Recovery mechanism |
| **Marketing preferences** | Until withdrawn | Consent |
@@ -78,6 +93,8 @@ We collect health-related information with your **explicit consent**:
5. Financial records retained 7 years (HMRC) then aggregated.
6. Allergy records retained 7 years (insurance) then deleted.
+**Saved cards:** Deleting your account also removes your saved-card references from our system and disables the corresponding card tokens at Square (see §2.2). Card transaction records for payments already made are retained per the HMRC schedule above.
+
**Inactive account deletion (automatic):**
1. Warning emails sent at 18/23 months (no balance) or 4/59 months (with balance).
2. If no activity, account deleted as above.
diff --git a/obsidian/Crussell/Terms & Conditions - Overall App.md b/obsidian/Crussell/Terms & Conditions - Overall App.md
index b47c1b1..57b02d9 100644
--- a/obsidian/Crussell/Terms & Conditions - Overall App.md
+++ b/obsidian/Crussell/Terms & Conditions - Overall App.md
@@ -104,6 +104,7 @@ All warning emails include your Account ID for future balance recovery.
- Card payments processed securely via Square.
- We do not store full card details.
- Refunds processed to original payment method within 5-10 business days.
+- **Saving a card for next time** stores a tokenised reference with our payment provider, Square (see the checkbox in the payment flow and our [[Privacy Policy]] §2.2). You can remove saved cards any time from your account. Cards are only stored when you explicitly tick "save this card".
### 3.3 Split Payments
- You may split payment across multiple methods (e.g., gift card + cash).
diff --git a/obsidian/Crussell/plans/p14-square-customer-provisioning-consent.md b/obsidian/Crussell/plans/p14-square-customer-provisioning-consent.md
new file mode 100644
index 0000000..278ee1e
--- /dev/null
+++ b/obsidian/Crussell/plans/p14-square-customer-provisioning-consent.md
@@ -0,0 +1,182 @@
+# P14 — Square Customer Provisioning & Consent
+
+**Status:** 📋 PLANNED — not started. Gated on P12 (sandbox credentials) to confirm whether Square *enforces* `card.customer_id` as Required.
+**Owner:** Implementation agent (payment integration round)
+**Estimated effort:** S-M (1-2 days backend/frontend + privacy policy copy)
+**Backlog reference:** `Future Work - Gap Backlog.md` item P14 (added alongside this plan)
+
+---
+
+## Executive Summary
+
+The final payment review (Aug 2026) flagged a P0: Square's current API docs mark **`customer_id` as Required** on (a) `POST /v2/cards` (the `card` object) and (b) `CreatePayment` **when `source_id` is a Cards-API card-on-file (`ccof:`)**. The app deliberately sends **no `customer_id`** (no Square customer provisioning — linkage uses `reference_id`; see commit 80bad01 and P11 plan Remaining Item R2 / M-8).
+
+This plan covers **what to do if `customer_id` is enforced**: provision Square customers, persist the id, and — the question this plan answers — **what consent/agreement/checkbox layer is required**. Answer: **no new standalone "Square customer" consent checkbox**; instead three transparency items (privacy policy disclosure, explicit card-save checkbox copy, data-minimisation by not minting customers for one-off payers).
+
+---
+
+## Background
+
+- The app creates a Square card-on-file for **every new-card payment** (even `save_card=false`), via `CreateCardOnFile` before the charge (`handlers.go` `CreateBookingPayment`/`CreateTipPayment`; `square_http_client.go` `createCardOnFileHTTP` → `POST /v2/cards`). The card is intentionally left as a Square-side orphan when not saved (retry idempotency via deterministic sha256 key).
+- If `POST /v2/cards` enforces `customer_id`, **every new-card payer — including guests — would get a Square customer profile** (name + email from the users/guest record) even when they never opted to save anything. That is the central data-protection problem this plan designs around.
+- A Square Customer profile is not a consumer-facing account: it is a merchant-side grouping container holding name, email, and tokenized card references (no PANs — PCI-safe).
+
+---
+
+## Consent & Legal Analysis (UK GDPR / DPA 2018 + card network rules)
+
+### What is NOT required
+
+| Item | Verdict | Reasoning |
+|---|---|---|
+| Standalone "create a Square customer profile" checkbox | **NOT required** | The profile is created as a *consequence* of the user's card-save action. The existing card-save checkbox is the consent point; the profile is bookkeeping that rides the same consent. |
+| New user agreement / Terms page | **NOT required** | The existing account Terms already govern the account; saved-card storage is disclosed via the privacy policy + checkbox copy below. |
+| Consent checkbox for *processing* name/email at Square | **NOT required** | Lawful basis is Article 6(1)(b) — necessary for performance of the contract (the user chose card-on-file for future payments). Consent is not the basis, so no checkbox. |
+
+### What IS required (transparency layer, not checkboxes)
+
+1. **Privacy Policy update** (`obsidian/Crussell/Privacy Policy.md`):
+ - Currently §2.1 states only "Payment transaction records (processed via Square, not stored by us)" — add: when a user saves a card, Square stores the card-on-file reference **and** a customer profile containing the user's name + email (Square is a data processor).
+ - State the lawful basis for card storage (contract performance / Article 6(1)(b)) and link to Square's privacy policy.
+ - State retention: card persists until the user deletes it (DeletePaymentMethod) or Square's card-expiry policy applies.
+2. **Explicit card-save checkbox copy** (`CardSelection.svelte:146`):
+ - Change `Save this card for next time` → `Save this card securely with our payment provider (Square) for next time`.
+ - Ensures informed consent per card-network card-on-file rules and Square's own requirements. Not pre-checked (already the case).
+3. **Data minimisation** (design change, see below): do **not** create a Square customer (or card-on-file) for one-off payers or guests who don't save. Charge the `cnon:` nonce directly.
+
+> ⚠️ Legal note: this is a UK card-on-file assessment consistent with ICO guidance and Visa/Mastercard card-on-file rules, not formal legal advice. Sanity-check the privacy policy wording with a professional before go-live.
+
+---
+
+## Design Decisions
+
+### D1 — Provision Square customers ONLY when the user saves a card
+- Create the Square customer lazily, at the moment `save_card=true`, via Square's `POST /v2/customers` (name + email from the local user record).
+- Persist the returned `customer_id` on `user_saved_cards` (new column) and reuse it for subsequent card saves by the same user.
+- One-off payments (`save_card=false`): **do not create a card-on-file at all** — call `CreatePayment` with the `cnon:` nonce as `source_id` directly (Square supports nonce-as-source without a card or customer). This reverses the current create-card-first pattern for non-save flows.
+
+### D2 — Guests never get a customer profile
+- Guest bookings have no account and cannot save cards (`canSaveCards` requires `verified_email`, so guests are excluded already). Ensure the backend also refuses to mint a customer for guest users — guest new-card charges must go straight to Square as one-off nonce charges.
+
+### D3 — Retry idempotency without a card-on-file
+- The current create-card-first pattern exists so a pending-record retry can re-mint the same card via the deterministic key. For one-off nonce charges, verify the retry path still dedups at Square on the **idempotency key** alone (it should — Square dedups `CreatePayment` by key; a failed first attempt that never charged is replayed safely). If the retry needs a fresh nonce because the old one is consumed, the frontend must re-tokenize on retry (it already caches the nonce per attempt — confirm the cache is cleared when the nonce is marked used).
+
+### D4 — Existing controls stay
+- Delete-card flow (`DeletePaymentMethod` → Square `POST /v2/cards/{id}/disable`) remains the "view/manage/delete" control card networks require. No change.
+
+---
+
+## Policy Pop-over for the Consent Checkbox (mirroring `/cancellation-policy`)
+
+The codebase already has a reusable policy pop-over pattern used in 8 places — **the plan reuses it for the card-save consent** so the checkbox links to the relevant policy inline, exactly like the cancellation-policy links today.
+
+### Existing pattern (ground truth — verified)
+- **`frontend/src/lib/components/ui/policyPopover.svelte`** — a generic pop-over component:
+ - Takes a `trigger` snippet (any label/button the caller renders).
+ - Renders a small pop-over with two actions: **Open** (new tab → `href`) and **Download PDF** (`href?format=pdf`).
+ - Closes on click-outside (`[data-policy-popover]`) and Escape; `target="_blank" rel="noopener noreferrer external"`.
+ - **Currently hardcodes** `href="/cancellation-policy"` and the fallback label "cancellation policy".
+- **`frontend/src/routes/cancellation-policy/+page.svelte`** — the policy page itself:
+ - Plain HTML sections (no auth), `Last updated` date in a header line.
+ - `?format=pdf` → strips the param from the URL, hides a print notice, triggers `window.print()` → user saves as PDF.
+ - Print CSS via `@media print` (hides nav + `.no-print`).
+- Used in: `BookingModal`, `RescheduleModal`, `EditRequestModal` (×2), `BookingFlow` (×2), `UserBookingModal`, `UserPaymentModal`, account page Policies block.
+
+### Step 6a — Generalise `policyPopover.svelte`
+Make the component take optional `label` (fallback text) and `href` props, defaulting to the current `/cancellation-policy` + "cancellation policy" so the 8 existing call sites keep working unchanged:
+```svelte
+const { trigger, label = 'cancellation policy', href = '/cancellation-policy' } = $props();
+```
+- `href` used for both the **Open** link and the **Download PDF** action (`${href}?format=pdf`).
+- No behavioural change for existing callers (defaults preserve current output).
+
+### Step 6b — Add the pop-over to the card-save consent
+In **`CardSelection.svelte`** next to the consent checkbox (`:135-148`), wrap the label's "Save this card securely with our payment provider (Square)" text (or the label span) so a small **"Privacy Policy"** link opens the pop-over:
+```svelte
+
+```
+- **Only rendered when the checkbox is shown** (`canSaveCards && squareCardReady`), so the link appears exactly at the point of consent — the same "at the moment of data collection" placement UK GDPR Art 13 expects.
+- Same component in any other flow that renders a save-card checkbox (UserBookingModal tip modal if it uses `CardSelection`, etc.) — **single source of truth: reuse `CardSelection`** rather than duplicating labels.
+
+### Step 6c — Create `/privacy-policy` route (the 'real' policy)
+- **Gate**: the pop-over ships only after the Privacy Policy placeholder is made 'real' (cleaned up + owner-approved; see Docs step 8). Until then, keep the current behaviour (no privacy pop-over / no link) or point the link at the placeholder page marked DRAFT — **decision: ship the route with the DRAFT content but label it "DRAFT — for review"**, so the consent flow is fully walkable end-to-end in dev while the copy is still being finalised.
+- New route **`frontend/src/routes/privacy-policy/+page.svelte`**, copying the `/cancellation-policy` structure:
+ - `` title "Privacy Policy", `@media print` CSS, `?format=pdf` handling, `Last updated` line.
+ - Content from `obsidian/Crussell/Privacy Policy.md` (incl. the new §2.2 Saved Cards & Square).
+ - Footer link back to `/contact` (match the cancellation page).
+- **Terms route** (optional, same pattern): if the Terms placeholder is also made real, add `/terms` + a pop-over near account sign-up. **Out of scope for P14 unless the owner asks** — the consent checkbox only needs the Privacy Policy link.
+
+### Step 6d — Account page "Policies" block
+Add a second `PolicyPopover` (Privacy Policy) next to the existing cancellation-policy button in `account/+page.svelte:2356-2378` so users can reach the privacy policy from their account, matching the current presentation.
+
+---
+
+## Implementation Steps (when unblocked by P12)
+
+### Backend
+1. **Schema**: add `square_customer_id TEXT` to `user_saved_cards` (nullable) + `init-script.sql`; document the manual `ALTER TABLE` for existing deployments (README migration section).
+2. **Square client** (`types.go` + `square_http_client.go`):
+ - Add `CreateCustomer(ctx, name, email) (*CustomerResult, error)` and `CustomerID` on `CardOnFile`.
+ - `createCardOnFileHTTP`: when `req.CustomerID != ""`, send `card.customer_id` (only for save flows).
+ - `createPaymentHTTP`: when `source_id` is `ccof:` (saved card), include `customer_id` from the local saved-card row.
+3. **Handlers** (`handlers.go` `CreateBookingPayment`/`CreateTipPayment`, `service.go` `CreatePaymentMethodFromToken`, till `online_square`):
+ - Restructure to **charge the nonce directly** when `save_card=false` (skip card mint).
+ - When `save_card=true`: ensure customer exists (lazy create + cache per user), pass `customer_id`, persist on the saved-card row.
+4. **Tests**: update mock (`square_dev.go`) + unit tests to cover: nonce-direct charge, customer creation on save, customer reuse, guest one-off (no customer), and the existing `_NonExact`/retry regressions.
+
+### Frontend
+5. **Checkbox copy** (`CardSelection.svelte:146`): explicit Square wording (above).
+6. **Policy pop-over on the consent checkbox** (mirror the existing `/cancellation-policy` pattern — see below).
+7. **Retry nonce handling**: verify `SquareCardInput`/flow caches the nonce per attempt and re-tokenizes after a consumed nonce (confirm current behaviour; fix if it re-sends a used nonce).
+8. **Account page**: display a small "cards are stored securely with our payment provider, Square" caption near the saved-cards section (optional but recommended for transparency).
+
+### Docs
+8. **Privacy Policy** (`obsidian/Crussell/Privacy Policy.md`): **already drafted in the placeholder** — §2.2 "Saved Cards & Payment Provider (Square)" now contains the processor disclosure, lawful basis (Art 6(1)(b)), one-off-payment statement, retention, and Square privacy-policy link; Financial Data list + retention table + deletion process updated to match. The rest of the document remains a DRAFT placeholder and must be made 'real' (owner-approved copy, live date) before the `/privacy-policy` route ships without its DRAFT banner.
+9. **Terms & Conditions** (`Terms & Conditions - Overall App.md`): §3.2 Payment Processing now cross-references the saved-card consent + Privacy Policy §2.2. (Placeholder — same 'make real' gate as above.)
+10. **README / Technical Manual**: update the payments section to reflect customer provisioning + nonce-direct one-off charges.
+
+---
+
+## Policy Pop-over Status (already-in-plan vs done)
+
+| Item | Status |
+|---|---|
+| Privacy Policy §2.2 content | ✅ Drafted into placeholder (Aug 2026) |
+| Terms §3.2 cross-ref | ✅ Added to placeholder |
+| `policyPopover.svelte` generalisation (`label`/`href` props) | ⏳ In plan (Step 6a) — not started |
+| Pop-over on consent checkbox in `CardSelection.svelte` | ⏳ In plan (Step 6b) — not started |
+| `/privacy-policy` route (HTML + PDF, mirrors `/cancellation-policy`) | ⏳ In plan (Step 6c) — not started; gated on policy being 'real' (or ships DRAFT-bannered) |
+| Account page Policies block second pop-over | ⏳ In plan (Step 6d) — not started |
+
+---
+
+## Verification
+
+- P12 sandbox smoke test **must run first**: confirm `POST /v2/cards` without `customer_id` actually 400s (it may not — integrations report cards can be created without it; the review marked this "unverified-high-risk"). Only proceed with customer provisioning if enforced.
+- Sandbox E2E: one-off nonce charge (no card, no customer created) → save-card charge (customer created once, reused) → guest charge (no customer) → delete card (profile card disabled).
+- Backend suite green; frontend build + ESLint clean.
+- Frontend: pop-over renders on the consent checkbox when `canSaveCards && squareCardReady`; **Open** and **Download PDF** both work against `/privacy-policy` (incl. `?format=pdf` print path); existing 8 `/cancellation-policy` call sites unchanged (defaults preserved).
+- Privacy policy diff reviewed by owner.
+
+---
+
+## Open Questions
+
+1. Does Square enforce `customer_id` on `POST /v2/cards` at runtime? (P12 / R2 / M-8 — the gate.)
+2. Can `CreatePayment` with a `cnon:` nonce as `source_id` be retried safely on a consumed nonce, or must the frontend re-tokenize? (Determines D3.)
+3. Should the Square customer profile include phone number (Square allows it)? Decide during provisioning — email-only is the default recommendation (data minimisation).
+
+---
+
+## What this plan explicitly does NOT do
+
+- No standalone "I agree to a Square customer account" checkbox.
+- **No `/terms` route** (only `/privacy-policy`; a Terms page can reuse the same pattern later if the owner asks).
+- No marketing use of Square customer data (out of scope; would need separate PECR/GDPR consent if ever added).