## 1. Docker Ignore - [x] 1.1 Create `.dockerignore` at project root excluding `node_modules`, `.git`, `dist`, `openspec`, and `*.md` ## 2. Nginx Configuration - [x] 2.1 Create `nginx.conf` at project root with: gzip enabled for text/html, CSS, JS, JSON; SPA fallback via `try_files $uri $uri/ /index.html`; `Cache-Control: public, max-age=31536000, immutable` for `/assets/`; `Cache-Control: no-cache` for root/index.html; listen on port 80 ## 3. Dockerfile - [x] 3.1 Create multi-stage `Dockerfile` at project root — stage 1: `node:22-alpine`, copy `app/`, install deps, run `npm run build`; stage 2: `nginx:alpine`, copy built `dist/` to nginx html dir, copy `nginx.conf` ## 4. Docker Compose - [x] 4.1 Create `docker-compose.yml` at project root with a single `app` service that builds from the Dockerfile and maps host port 8080 to container port 80 ## 5. Verification - [x] 5.1 Run `docker compose build` and verify the image builds successfully - [x] 5.2 Run `docker compose up -d` and verify the app is accessible at `http://localhost:8080` - [x] 5.3 Verify gzip compression is active on JS/CSS responses - [x] 5.4 Verify cache headers: immutable on `/assets/*`, no-cache on `/` - [x] 5.5 Verify SPA fallback: request a non-existent path and confirm index.html is returned