- nginx: route POST /contact/ to contact.php, GET to router.php
- Dockerfile: COPY .env into image
- entrypoint.sh: source .env on container start
- .dockerignore: allow .env to be included in build context
- Raise mobile nav breakpoint from 1023px to 1199px -- desktop nav needs 1200px+ (container max) to fit 7 links + phone + CTA without clipping
- Topbar: truncate text with ellipsis on <=600px to stay within fixed 44px height
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Alias .form-group to .form-field in main.css so contact form inputs get full styling
- Hide header Get Estimate btn on <=480px (mobile drawer has CTA, prevents logo+btn+hamburger overflow)
- Tighten header-inner gap to space-4 on <=480px
- Drop redundant btn-sm padding override from 360px rule (btn now hidden at that width)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deployment-only files stay in root (Dockerfile, nginx.conf,
docker-compose.yml, .dockerignore, .gitignore, public folders,
api/). Everything reference/dev-only goes to .planning/ to keep
the deploy directory clean.
- New api/ Python service (stdlib only — no pip install, no packages):
validates fields server-side, verifies reCAPTCHA, sends via Resend
with idempotency key, rate-limited (5 req/IP/15min)
- Matches existing project tooling (build_locations.py, build_services.py)
- Front-end form.js stays vanilla JS, no JS frameworks anywhere
- docker-compose runs nginx + python:3.13-alpine api with healthcheck
- nginx proxies /api/ to Python service, strips prefix
- Dockerfile now copies only public folders into web root (was
copying everything, exposing /Dockerfile, /build_*.py, /api/.env)
- nginx.conf denies dotfiles, .env, .conf, .yml, .py, .md, .txt
and Dockerfile as defense in depth
- .dockerignore keeps sensitive files out of build context
- .gitignore protects api/.env and __pycache__ from being committed