Multi-stage build: node builds the SPA, dotnet publishes the API, the
runtime image serves the SPA from wwwroot/ and exposes /health. Frontend
Supabase URL + publishable key are baked in at build time; DB conn string
and Supabase JWKS metadata come from Coolify env vars. VITE_API_URL empty
means same-origin, so the browser hits /api/todos on the same host that
serves the SPA.
Port 5000 caused recurring collisions with stray processes across subagent
runs. 5057 is uncontested in this environment. CORS unaffected (Vite dev
origin is 5173).