Frontend: runtime ConfigContext, provider-aware AuthContext, OpenAI/AOAI model components
The browser bundle is now tenant-portable. Auth provider, Entra IDs, and feature flags come from `GET /config` at startup rather than being baked into the build - so one image can serve Supabase, local, and Entra deployments without a rebuild per customer.
contexts/ConfigContext.tsx fetches /config on mount and exposes useConfig(). It also exports getCachedAuthProvider(), a module-level function that reads the last fetched value from localStorage - this lets helpers outside React components answer "what auth mode am I in" without needing a context ref. The localStorage fallback prevents a blank-flash on hot reload during development.
AuthContext.tsx branches on the resolved provider. Supabase mode works as before. Local mode reads a JWT from localStorage. Entra mode reads from a hash fragment after the OIDC callback. Sign-out routes through GET /api/auth/logout in all three cases rather than constructing the Microsoft logout URL in the browser. The Supabase client construction is now lazy - a getSupabaseClient() factory that throws with a clear message in non-supabase deployments, instead of the previous pattern of constructing against placeholder env vars and failing silently later.
lib/auth-token.ts centralizes bearer token retrieval. Every component that used to call supabase.auth.getSession() now calls getBrowserAccessToken(), and responses go through bounceIfUnauthorized() which redirects to the login page on 401.
The second commit converts dynamic-segment routes ([id], [chatId], [reviewId]) to SSR shells that delegate to client components reading useParams() at runtime. This is a requirement of Next.js output: "export" - server-baked params resolve to the placeholder _ in a static export, so the parameter has to be read client-side. The Account → Models page adds OpenAI key field, an Azure OpenAI settings section (endpoint, API key, API version), and a deployment dropdown that queries /api/llm/azure-openai/deployments. AOAI model entries use the pattern aoai:<deploymentName> as the ID, matching the backend's model routing.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?