diff --git a/app/(main)/[locale]/login/page.tsx b/app/(main)/[locale]/login/page.tsx
index fbddc90e..be9f2d8c 100644
--- a/app/(main)/[locale]/login/page.tsx
+++ b/app/(main)/[locale]/login/page.tsx
@@ -9,6 +9,7 @@ import { Input } from "@/components/ui/input";
import { useAuthStore } from "@/stores/auth-store";
import { useAccountStore } from "@/stores/account-store";
import { useThemeStore } from "@/stores/theme-store";
+import { resolveThemeLogo } from "@/lib/theme-logo";
import { useShallow } from "zustand/react/shallow";
import { useConfig } from "@/hooks/use-config";
import { apiFetch, getPathPrefix, toRouterPath, withBasePath } from "@/lib/browser-navigation";
@@ -135,6 +136,10 @@ export default function LoginPage() {
const { theme, setTheme, initializeTheme } = useThemeStore(useShallow((s) => ({ theme: s.theme, setTheme: s.setTheme, initializeTheme: s.initializeTheme })));
const { appName, jmapServerUrl: configuredServerUrl, oauthEnabled, oauthOnly, oauthClientId: globalOauthClientId, oauthIssuerUrl: globalOauthIssuerUrl, oauthScopes, rememberMeEnabled, devMode, demoMode, loginLogoLightUrl, loginLogoDarkUrl, loginCompanyName, loginImprintUrl, loginPrivacyPolicyUrl, loginWebsiteUrl, loginLogoMaxHeight, loginLogoMaxWidth, loginShowHeading, loginShowSubtitle, loginShowTotp, loginShowVersion, isLoading: configLoading, error: configError, autoSsoEnabled, embeddedMode: _embeddedMode, allowCustomJmapEndpoint, jmapServers, jmapServerAutoPickByDomain } = useConfig();
const resolvedTheme = useThemeStore((s) => s.resolvedTheme);
+ const { activeThemeId, installedThemes } = useThemeStore(useShallow((s) => ({ activeThemeId: s.activeThemeId, installedThemes: s.installedThemes })));
+ // Active theme may carry its own brand logo (VNClagoon wordmark, SRC mark);
+ // fall back to the globally configured login logo.
+ const effLoginLogo = resolveThemeLogo(installedThemes, activeThemeId, resolvedTheme === 'dark', loginLogoLightUrl, loginLogoDarkUrl);
// Login logo sizing: when a max height/width is configured, drop the fixed
// 64×64 box so the logo (e.g. a wide wordmark) can render at its true size.
@@ -740,7 +745,7 @@ export default function LoginPage() {

@@ -890,7 +895,7 @@ export default function LoginPage() {

s.resolvedTheme);
+ const activeThemeId = useThemeStore((s) => s.activeThemeId);
+ const installedThemes = useThemeStore((s) => s.installedThemes);
const { supportsCalendar } = useCalendarStore();
const { mailboxes } = useEmailStore();
const client = useAuthStore((s) => s.client);
@@ -462,7 +465,7 @@ export function NavigationRail({
)}
>
{(() => {
- const logoUrl = withBasePath(resolvedTheme === 'dark' ? (appLogoDarkUrl || appLogoLightUrl) : (appLogoLightUrl || appLogoDarkUrl));
+ const logoUrl = withBasePath(resolveThemeLogo(installedThemes, activeThemeId, resolvedTheme === 'dark', appLogoLightUrl, appLogoDarkUrl));
return logoUrl ? (
![]()
t.id === activeThemeId) : undefined;
+ if (theme) {
+ const themed = isDark
+ ? (theme.logoDarkUrl ?? theme.logoLightUrl)
+ : (theme.logoLightUrl ?? theme.logoDarkUrl);
+ if (themed) return themed;
+ }
+ return isDark ? (fallbackDark || fallbackLight) : (fallbackLight || fallbackDark);
+}
diff --git a/vnc/VNC-CHANGES.md b/vnc/VNC-CHANGES.md
index 23f17dae..c394a4bb 100644
--- a/vnc/VNC-CHANGES.md
+++ b/vnc/VNC-CHANGES.md
@@ -32,6 +32,11 @@ that merging new upstream releases stays a triage exercise, not an archaeology d
| 2026-08-03 | `stores/theme-store.ts` | default `theme`/`resolvedTheme` → `dark` | dark-first per VNClagoon styleguide |
| 2026-08-03 | `lib/builtin-themes.ts` | add `builtin-src` theme (Swiss red on white, light-first) | 2nd brand theme (SRC Advisory); VNClagoon stays default |
| 2026-08-03 | `public/branding/src-logo.svg` (new) | SRC mountain mark | SRC brand (placeholder — swap official) |
+| 2026-08-03 | `lib/plugin-types.ts` | add optional `logoLightUrl`/`logoDarkUrl` to InstalledTheme | per-theme brand logos |
+| 2026-08-03 | `lib/theme-logo.ts` (new) | `resolveThemeLogo()` helper | pick active theme's logo, fall back to global |
+| 2026-08-03 | `lib/builtin-themes.ts` | set logos on vnclagoon (wordmark) + src (mark) | logo switches with the brand theme |
+| 2026-08-03 | `app/(main)/[locale]/login/page.tsx` | login logo uses active theme's logo | brand-switch on login |
+| 2026-08-03 | `components/layout/navigation-rail.tsx` | nav-rail logo uses active theme's logo | brand-switch in app |
_Note: Vercel was tried and **abandoned** on 2026-08-03. Bulwark writes to a local
data dir (`/app/data/*`); Vercel serverless has a read-only filesystem → crash