diff --git a/CLOUD_IMPLEMENTATION_PROMPT.md b/CLOUD_IMPLEMENTATION_PROMPT.md
new file mode 100644
index 0000000..f2afdec
--- /dev/null
+++ b/CLOUD_IMPLEMENTATION_PROMPT.md
@@ -0,0 +1,700 @@
+# NaviDocs Critical Fixes - Cloud Implementation Mission
+
+**GitHub Repository:** https://github.com/dannystocker/navidocs
+**Branch:** `navidocs-cloud-coordination`
+**Target:** Premium boat management platform for €800K-€1.5M yachts
+
+---
+
+## Mission Overview
+
+You are a Sonnet instance managing 15 Haiku agents to implement critical security, performance, and UX fixes identified in comprehensive code reviews (Codex + Gemini). This is a **3-week sprint** to production readiness.
+
+**Review Sources (on GitHub in branch `navidocs-cloud-coordination`):**
+- `HANDOVER_SESSION_2025-11-14.md` - Full context
+- `reviews/CODEX_SECURITY_ARCHITECTURE_REPORT.md` - Security review (if exists)
+- `reviews/GEMINI_PERFORMANCE_UX_REPORT.md` - Performance/UX review (if exists)
+
+**Reference Implementation Guide:**
+The detailed implementation patterns are in the Windows downloads folder, but I'll embed the key patterns here for zero-dependency execution.
+
+---
+
+## Critical Issues to Fix
+
+### 🔴 BLOCKERS (Week 1 - Must Fix Before Production)
+
+1. **JWT Secret Enforcement** - Hard-coded default secret allows token forgery
+2. **Auth Gaps** - Document/search/upload/image/stats routes use `req.user?.id || 'test-user-id'`
+3. **Meilisearch Token Fallback** - Falls back to global search key on failure
+4. **Bundle Size 2.3MB** - No lazy loading, target <500KB gzipped
+5. **Touch Targets 12-40px** - Need 60×60px minimum for marine gloves
+6. **No Pagination** - 11 `.all()` queries without limits
+7. **Small Fonts** - As low as 10px, need 24-48px for sunlight readability
+8. **Missing ARIA Labels** - 29 icon-only buttons fail WCAG
+
+### 🟡 HIGH PRIORITY (Week 2)
+
+9. **God Components** - `DocumentView.vue` (1386 lines), `SearchView.vue` (628 lines)
+10. **Service Layer** - Business logic in routes, need service extraction
+11. **Token Refresh** - No automatic 401 handling
+12. **Images Without Alt** - Accessibility failures
+13. **Legacy Auth Middleware** - Two auth implementations cause confusion
+
+---
+
+## Agent Assignment & Parallel Execution
+
+**Spawn 15 Haiku agents in PARALLEL** using a single message with 15 Task tool calls:
+
+### Security Team (Agents 1-5)
+
+**Agent 1: JWT Secret Enforcement**
+- Create `server/config/env.js` with mandatory `JWT_SECRET` validation
+- Update `server/services/auth.service.js` to use central config
+- Update `server/middleware/auth.middleware.js` to use central config
+- Remove or deprecate `server/middleware/auth.js` (legacy)
+- Add startup assertion: `assert(JWT_SECRET && JWT_SECRET.length >= 32)`
+
+**Agent 2: Document Route Auth**
+- Add `authenticateToken` middleware to all routes in `server/routes/documents.js`
+- Remove all `req.user?.id || 'test-user-id'` patterns
+- Use `req.user.userId` directly (set by middleware)
+- Add `requireOrganizationMember` where organization scoping needed
+- Test: Verify 401 on unauthenticated requests
+
+**Agent 3: Search/Upload/Image Route Auth**
+- Secure `server/routes/search.js` (both `/token` and `/search`)
+- Secure `server/routes/upload.js` and `/upload/quick-ocr`
+- Secure `server/routes/images.js` (all image endpoints)
+- Remove `test-user-id` fallbacks
+- Add organization membership checks
+
+**Agent 4: Stats & Jobs Auth**
+- Secure `server/routes/stats.js` with `requireSystemAdmin` for global stats
+- Create `/stats/organizations/:organizationId` for scoped stats
+- Secure `server/routes/jobs.js` with proper auth
+- Remove public access to operational metrics
+
+**Agent 5: Meilisearch Token Hardening**
+- Modify `server/routes/search.js` POST `/token` to fail closed
+- Only allow fallback in `NODE_ENV === 'development'`
+- Log tenant token failures to ops team
+- Return 500 with retry message in production on failure
+
+---
+
+### Performance Team (Agents 6-9)
+
+**Agent 6: Lazy Loading Routes**
+- Update `client/src/router/index.js`
+- Convert all direct imports to `() => import('./views/XYZ.vue')`
+- Apply to: HomeView, DocumentView, LibraryView, SearchView, SettingsView
+- Test: Verify bundle splits in `npm run build`
+- Target: Reduce initial bundle from 2.3MB to <1MB
+
+**Agent 7: Pagination Utility**
+- Create `server/utils/pagination.js` with `parsePagination(req, options)`
+- Support `?page=1&pageSize=50` query params
+- Max page size: 200, default: 50
+- Return `{ page, pageSize, offset }` for SQL queries
+- Add total count helper for responses
+
+**Agent 8: Apply Pagination to Routes**
+- Update `server/routes/documents.js` GET `/` with pagination
+- Update `server/routes/images.js` listings
+- Update `server/routes/jobs.js` GET `/api/jobs`
+- Replace all `.all()` with `.all(limit, offset)` pattern
+- Return `{ data: rows, page, pageSize, total }` format
+
+**Agent 9: Central API Client with Token Refresh**
+- Create `client/src/api/http.js` using Axios
+- Add request interceptor to inject `Authorization: Bearer ${accessToken}`
+- Add response interceptor for 401 handling
+- Implement automatic refresh using `/api/auth/refresh`
+- Prevent multiple concurrent refresh requests (use promise singleton)
+
+---
+
+### Architecture Team (Agents 10-12)
+
+**Agent 10: Document Service Layer**
+- Create `server/services/document.service.js`
+- Extract methods: `listForUser()`, `getForUser()`, `deleteForUser()`
+- Use pagination support from Agent 7
+- Enforce organization membership via JOIN
+- Throw 404 errors with `.status = 404` for service-level handling
+
+**Agent 11: Image Service Layer**
+- Create `server/services/image.service.js`
+- Extract image retrieval, validation, path safety logic
+- Move Meilisearch cleanup logic from routes
+- Support pagination for image lists
+
+**Agent 12: Stats Service Layer**
+- Create `server/services/stats.service.js`
+- Implement `getGlobalStats()` (admin only)
+- Implement `getOrgStats(organizationId, userId)` with membership check
+- Always join via `user_organizations` to prevent cross-tenant leaks
+
+---
+
+### UX Team (Agents 13-15)
+
+**Agent 13: Marine CSS Baseline**
+- Create `client/src/assets/marine.css`
+- Define CSS variables: `--nd-touch-target: 60px`, `--nd-font-base: 16px`, `--nd-font-large: 24px`, `--nd-font-xlarge: 32px`
+- Apply to all `button`, `[role='button']`, `a.nav-link` (min 60×60px)
+- Update `client/src/main.js` to import marine.css
+- Test: Verify all interactive elements meet 60px minimum
+
+**Agent 14: ARIA Labels & Alt Text**
+- Scan all `.vue` files for icon-only buttons
+- Add `aria-label` to all interactive elements without visible text
+- Add `aria-hidden="true"` to decorative icons inside labeled buttons
+- Add `:alt` attributes to all `
` tags
+- Use descriptive labels: "Delete item", "Close dialog", etc.
+
+**Agent 15: Typography & Contrast**
+- Update all font-size CSS < 16px to at least 16px base
+- Create `.nd-metric` class for critical numbers (32-48px, font-weight: 700)
+- Apply to key metrics in dashboard/stats components
+- Add `.nd-heading` class (24px, font-weight: 600)
+- Verify contrast ratios ≥7:1 for WCAG AAA (Navy #1E3A8A on White #FFF)
+
+---
+
+## Implementation Patterns (Zero-Dependency Reference)
+
+### Pattern 1: JWT Secret Enforcement
+
+```javascript
+// server/config/env.js
+import assert from 'node:assert';
+
+export const NODE_ENV = process.env.NODE_ENV || 'development';
+
+export const JWT_SECRET = process.env.JWT_SECRET;
+assert(
+ JWT_SECRET && JWT_SECRET.length >= 32,
+ 'JWT_SECRET environment variable is required and must be at least 32 chars'
+);
+
+export const MEILISEARCH_HOST = process.env.MEILISEARCH_HOST;
+export const MEILISEARCH_MASTER_KEY = process.env.MEILISEARCH_MASTER_KEY;
+```
+
+```javascript
+// server/services/auth.service.js
+import jwt from 'jsonwebtoken';
+import { JWT_SECRET } from '../config/env.js';
+
+export function signAccessToken(payload, options = {}) {
+ return jwt.sign(payload, JWT_SECRET, {
+ expiresIn: process.env.JWT_EXPIRES_IN || '15m',
+ ...options,
+ });
+}
+```
+
+### Pattern 2: Auth Middleware with RBAC
+
+```javascript
+// server/middleware/auth.middleware.js
+import jwt from 'jsonwebtoken';
+import { JWT_SECRET } from '../config/env.js';
+
+export function authenticateToken(req, res, next) {
+ const header = req.headers['authorization'];
+ const token = header && header.split(' ')[1];
+ if (!token) return res.sendStatus(401);
+
+ jwt.verify(token, JWT_SECRET, (err, decoded) => {
+ if (err) return res.sendStatus(401);
+ req.user = decoded; // { userId, email, organizationIds, ... }
+ next();
+ });
+}
+
+export function requireOrganizationMember(req, res, next) {
+ const orgId = req.params.organizationId || req.body.organizationId;
+ if (!orgId) return res.status(400).json({ error: 'Organization ID required' });
+
+ const isMember = req.user.organizationIds?.includes(orgId);
+ if (!isMember) return res.status(403).json({ error: 'Not a member of this organization' });
+
+ next();
+}
+```
+
+### Pattern 3: Pagination Utility
+
+```javascript
+// server/utils/pagination.js
+export function parsePagination(req, { defaultSize = 50, maxSize = 200 } = {}) {
+ const page = Math.max(parseInt(req.query.page || '1', 10), 1);
+ const pageSize = Math.min(
+ Math.max(parseInt(req.query.pageSize || String(defaultSize), 10), 1),
+ maxSize
+ );
+ const offset = (page - 1) * pageSize;
+ return { page, pageSize, offset };
+}
+```
+
+### Pattern 4: Service Layer for Documents
+
+```javascript
+// server/services/document.service.js
+import db from '../db/db.js';
+
+export function listForUser(userId, { limit, offset }) {
+ const rows = db.prepare(`
+ SELECT d.*
+ FROM documents d
+ JOIN user_organizations uo
+ ON uo.organization_id = d.organization_id
+ WHERE uo.user_id = ?
+ ORDER BY d.created_at DESC
+ LIMIT ? OFFSET ?
+ `).all(userId, limit, offset);
+
+ const { count } = db.prepare(`
+ SELECT COUNT(*) as count
+ FROM documents d
+ JOIN user_organizations uo
+ ON uo.organization_id = d.organization_id
+ WHERE uo.user_id = ?
+ `).get(userId);
+
+ return { rows, total: count };
+}
+
+export function getForUser(userId, documentId) {
+ const doc = db.prepare(`
+ SELECT d.*
+ FROM documents d
+ JOIN user_organizations uo
+ ON uo.organization_id = d.organization_id
+ WHERE d.id = ? AND uo.user_id = ?
+ `).get(documentId, userId);
+
+ if (!doc) {
+ const err = new Error('Document not found');
+ err.status = 404;
+ throw err;
+ }
+
+ return doc;
+}
+```
+
+### Pattern 5: Route Using Service + Pagination
+
+```javascript
+// server/routes/documents.js
+import { Router } from 'express';
+import { authenticateToken } from '../middleware/auth.middleware.js';
+import { parsePagination } from '../utils/pagination.js';
+import * as documentService from '../services/document.service.js';
+
+const router = Router();
+
+router.get('/',
+ authenticateToken,
+ async (req, res, next) => {
+ try {
+ const { page, pageSize, offset } = parsePagination(req);
+ const { rows, total } = documentService.listForUser(
+ req.user.userId,
+ { limit: pageSize, offset }
+ );
+ res.json({ data: rows, page, pageSize, total });
+ } catch (err) {
+ next(err);
+ }
+ }
+);
+
+router.get('/:id',
+ authenticateToken,
+ async (req, res, next) => {
+ try {
+ const doc = documentService.getForUser(req.user.userId, req.params.id);
+ res.json(doc);
+ } catch (err) {
+ next(err);
+ }
+ }
+);
+
+export default router;
+```
+
+### Pattern 6: Lazy Loading Routes
+
+```javascript
+// client/src/router/index.js
+import { createRouter, createWebHistory } from 'vue-router';
+
+const routes = [
+ {
+ path: '/',
+ name: 'home',
+ component: () => import('../views/HomeView.vue')
+ },
+ {
+ path: '/documents/:id',
+ name: 'document',
+ component: () => import('../views/DocumentView.vue'),
+ props: true
+ },
+ {
+ path: '/library',
+ name: 'library',
+ component: () => import('../views/LibraryView.vue')
+ },
+ {
+ path: '/search',
+ name: 'search',
+ component: () => import('../views/SearchView.vue')
+ }
+];
+
+export default createRouter({
+ history: createWebHistory(),
+ routes,
+});
+```
+
+### Pattern 7: Central API Client with Token Refresh
+
+```javascript
+// client/src/api/http.js
+import axios from 'axios';
+import { useAuthStore } from '@/stores/auth';
+
+const api = axios.create({
+ baseURL: '/api',
+});
+
+api.interceptors.request.use((config) => {
+ const auth = useAuthStore();
+ if (auth.accessToken) {
+ config.headers.Authorization = `Bearer ${auth.accessToken}`;
+ }
+ return config;
+});
+
+let refreshPromise = null;
+
+api.interceptors.response.use(
+ (response) => response,
+ async (error) => {
+ const { response, config } = error;
+ const auth = useAuthStore();
+
+ if (response?.status === 401 && !config._retry && auth.refreshToken) {
+ config._retry = true;
+
+ if (!refreshPromise) {
+ refreshPromise = auth.refreshAccessToken();
+ }
+
+ try {
+ await refreshPromise;
+ refreshPromise = null;
+ config.headers.Authorization = `Bearer ${auth.accessToken}`;
+ return api(config);
+ } catch (err) {
+ refreshPromise = null;
+ auth.logout();
+ window.location.href = '/login';
+ }
+ }
+
+ return Promise.reject(error);
+ }
+);
+
+export default api;
+```
+
+### Pattern 8: Marine CSS Baseline
+
+```css
+/* client/src/assets/marine.css */
+:root {
+ --nd-touch-target: 60px;
+ --nd-font-base: 16px;
+ --nd-font-large: 24px;
+ --nd-font-xlarge: 32px;
+ --nd-font-metric: 48px;
+
+ --nd-navy: #1E3A8A;
+ --nd-teal: #0D9488;
+ --nd-white: #ffffff;
+}
+
+body {
+ font-size: var(--nd-font-base);
+ line-height: 1.5;
+}
+
+/* All interactive elements */
+button,
+[role='button'],
+a.nav-link,
+.btn,
+.icon-button {
+ min-width: var(--nd-touch-target);
+ min-height: var(--nd-touch-target);
+ padding: 0.75rem 1rem;
+}
+
+/* Typography scale */
+.nd-heading {
+ font-size: var(--nd-font-large);
+ font-weight: 600;
+}
+
+.nd-metric {
+ font-size: var(--nd-font-metric);
+ font-weight: 700;
+ color: var(--nd-navy);
+}
+
+/* High contrast for marine use */
+.nd-high-contrast {
+ background: var(--nd-navy);
+ color: var(--nd-white);
+}
+```
+
+### Pattern 9: ARIA Labels
+
+```vue
+
+
+
+
+
+```
+
+```vue
+
+
+
+
+
+```
+
+---
+
+## Execution Instructions
+
+### Step 1: Clone Repository
+
+```bash
+git clone https://github.com/dannystocker/navidocs.git
+cd navidocs
+git checkout navidocs-cloud-coordination
+```
+
+### Step 2: Analyze Current State
+
+```bash
+# Find all .all() queries without limits
+grep -r "\.all()" server/routes/ | wc -l
+
+# Find test-user-id patterns
+grep -r "test-user-id" server/routes/
+
+# Find small touch targets
+grep -r "width.*px\|height.*px" client/src/components/ | grep -E "width: [1-5][0-9]px|height: [1-5][0-9]px"
+
+# Find small fonts
+grep -r "font-size.*px" client/src/ | grep -E "[0-9]{1}px|1[0-5]px"
+
+# Find missing ARIA labels
+grep -r "