Three stages: npm build, Go build with the real dist copied over the
placeholder before compiling, distroless/static (nonroot) runtime.
The container listens on :8080 (all interfaces, unlike the localhost
dev default). Verified locally: 16.6 MB image, /, /healthz and /api
all answer. Dev loop unchanged, Docker stays out of it.