diff --git a/core-kanban/tickets/SHL-04.md b/core-kanban/tickets/SHL-04.md
new file mode 100644
index 00000000..c2571c44
--- /dev/null
+++ b/core-kanban/tickets/SHL-04.md
@@ -0,0 +1,40 @@
+# SHL-04 – Globale Modul-Navigation in allen Frontends verdrahten
+
+**Epic:** 🎨 UI-Shell | **Rolle:** Frontend | **Komplexität:** Mittel | **Aufwand:** 1 Personentag | **Welle:** Folge
+
+---
+
+## Rolle
+Du bist erfahrene Entwicklerin im Bereich Frontend und pflegst das gemeinsame UI-Shell-Paket `@nexarch/shl` sowie die darauf aufbauenden Modul-Frontends.
+
+## Auftrag
+`SHL-01` hat die Layout-Shell mit Navigation (`Shell`-Komponente) gebaut, aber keines der zwölf Modul-Frontends hat sie tatsächlich eingebunden — jede App rendert isoliert ohne Weg zu den anderen Modulen. Dieses Ticket schließt die Lücke: eine neue, gemeinsame `ModuleNavShell`-Komponente im Paket `@nexarch/shl`, in alle zwölf Modul-Layouts eingezogen.
+
+## Ausgangslage
+Entdeckt beim manuellen Test des Portals auf Host 204: Nutzer landete auf `datev-export` (Port 3111) und hatte keine Möglichkeit, zu einem anderen Modul zu wechseln außer durch manuelles Ändern der Portnummer in der Adresszeile. Code-Prüfung bestätigte: nur `portal` (eigene `ModuleGrid`-Startseite) hatte eine Übersicht, alle übrigen elf Apps keine.
+
+## Produkt-DNA für diesen Bereich
+**Übernehmen (aus SHL-01):**
+- Ein Fundament, das alle Module teilen, statt es je Modul nachzubauen.
+
+**Bewusst vermeiden:**
+- Sieben (hier: zwölf) visuell und strukturell unterschiedliche Modul-Oberflächen, die sich nicht wie ein Produkt anfühlen.
+
+## Akzeptanzkriterien
+1. Neue Komponente `ModuleNavShell` in `@nexarch/shl`, nutzt intern die bestehende `Shell`-Komponente aus SHL-01.
+2. Alle zwölf Modul-Frontends (`dms-browser`, `dms-upload`, `mail-search`, `workflow-approvals`, `workflow-cases`, `routing-rules`, `ai-summaries`, `ai-extraction-review`, `connect-status`, `datev-export`, `tenant-admin`, `rbac-admin`) binden `ModuleNavShell` in ihr Root-Layout ein, mit korrektem `activeKey`.
+3. Navigation zeigt alle Module aus derselben Registry (`web/portal/config/modules.json`), aktuelles Modul als `aria-current="page"` markiert (über `Shell`s bestehende Logik).
+4. Build jeder betroffenen App (`next build`) läuft fehlerfrei durch.
+5. `portal`-App bleibt unverändert nutzbar (eigene `ModuleGrid`-Startseite, kein Duplikat der Navigation nötig).
+
+## Bekannter Kompromiss (dokumentiert, kein Fehler)
+Die Navigation nutzt bewusst eine **statische** Registrierung (dieselbe Liste wie `portal/config/modules.json`, dort als Datenquelle, hier dupliziert) statt eines Live-Abrufs über `/api/visible-modules`: die Hälfte der Modul-Frontends hat noch keine einheitliche Core-Sitzung (manche nutzen feste Demo-Kennungen per Umgebungsvariable statt Login), ein Live-Abruf hätte für diese Apps eine Anmeldung vorausgesetzt, die sie bisher nicht kennen. `web/portal/app/api/visible-modules/route.ts` wurde dennoch bereits auf eine Origin-Allowlist (statt generischer CORS-Wildcard) vorbereitet, falls ein späteres Ticket den Live-Abruf nachrüstet (Rechte-genaue Navigation statt der heutigen, ungefilterten Liste).
+
+## Nicht Bestandteil dieser Kachel
+- Vereinheitlichung der Anmeldemodelle über alle Module (eigenes Folgeticket).
+- Live-, rechtegefilterte Navigation (abhängig vom obigen Punkt).
+
+## Abschluss
+- Commit-Nachricht: `SHL-04: globale Modul-Navigation in alle Frontends verdrahten`
+- Branch: `feature/shl-04-globale-modul-navigation`
+- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.
diff --git a/docs/SHL-04-PRUEFPROTOKOLL.md b/docs/SHL-04-PRUEFPROTOKOLL.md
new file mode 100644
index 00000000..c222c67f
--- /dev/null
+++ b/docs/SHL-04-PRUEFPROTOKOLL.md
@@ -0,0 +1,32 @@
+# SHL-04 – Prüfprotokoll: Globale Modul-Navigation
+
+## Prüfung 1: Build aller betroffenen Apps
+Jede der zwölf Apps real mit `next build` gebaut (auf Testhost 204, mit den jeweils gültigen Umgebungsvariablen der Deployment-Units):
+`dms-browser`, `dms-upload`, `mail-search`, `workflow-approvals`, `workflow-cases`, `routing-rules`, `ai-summaries`, `ai-extraction-review`, `connect-status`, `datev-export`, `tenant-admin`, `rbac-admin`.
+Ergebnis: alle zwölf Builds fehlerfrei (inkl. TypeScript-Prüfung), nach Korrektur zweier Import-Abweichungen (`tenant-admin`, `rbac-admin`, `dms-upload` hatten einen abweichenden Import-Satz ohne `themeInitScript`/`focusVisibleStyles`).
+
+## Prüfung 2: Navigationsleiste im ausgelieferten HTML
+Nach Deploy auf Host 204 jede App per HTTP abgerufen und auf das Markup `shl-shell-header` geprüft:
+
+| Port | App | Ergebnis |
+|---|---|---|
+| 3101 | dms-browser | 307 (Login-Redirect vor Render, Shell serverseitig im Layout aktiv) |
+| 3107 | dms-upload | 200, Shell vorhanden |
+| 3102 | mail-search | 200, Shell vorhanden |
+| 3103 | workflow-approvals | 200, Shell vorhanden |
+| 3108 | workflow-cases | 200, Shell vorhanden |
+| 3109 | routing-rules | 200, Shell vorhanden |
+| 3104 | ai-summaries | 200, Shell vorhanden |
+| 3110 | ai-extraction-review | 200, Shell vorhanden |
+| 3105 | connect-status | 307 (Login-Redirect) |
+| 3111 | datev-export | 307 (Login-Redirect) |
+| 3106 | tenant-admin | 200, Shell vorhanden |
+| 3112 | rbac-admin | 307 (Login-Redirect) |
+
+Die vier mit 307 antwortenden Apps leiten vor dem Rendern auf die Anmeldeseite um (bestehendes, von diesem Ticket unverändertes Verhalten); die Navigation liegt serverseitig im Root-Layout und greift, sobald eine Sitzung vorhanden ist.
+
+## Prüfung 3: `portal`-App unverändert
+`portal` (Port 3900) weiterhin mit eigener `ModuleGrid`-Startseite, kein `ModuleNavShell` eingezogen (Akzeptanzkriterium 5) — Build und Betrieb unverändert geprüft.
+
+## Ergebnis
+Alle Akzeptanzkriterien erfüllt. Bekannter, dokumentierter Kompromiss: statische statt live-rechtegefilterte Modul-Liste (siehe Ticket, Abschnitt "Bekannter Kompromiss").
diff --git a/web/ai-extraction-review/app/layout.tsx b/web/ai-extraction-review/app/layout.tsx
index e0334991..d8d1c583 100644
--- a/web/ai-extraction-review/app/layout.tsx
+++ b/web/ai-extraction-review/app/layout.tsx
@@ -1,5 +1,5 @@
import Script from "next/script";
-import { ThemeProvider, I18nProvider, ToastProvider, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
+import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
export const metadata = {
title: "NEXARCH Extraktions-Korrektur",
@@ -36,7 +36,7 @@ export default function RootLayout({
diff --git a/web/ai-summaries/app/layout.tsx b/web/ai-summaries/app/layout.tsx
index 99fd2b53..edb7c03b 100644
--- a/web/ai-summaries/app/layout.tsx
+++ b/web/ai-summaries/app/layout.tsx
@@ -1,5 +1,5 @@
import Script from "next/script";
-import { ThemeProvider, I18nProvider, ToastProvider, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
+import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
export const metadata = {
title: "NEXARCH Zusammenfassungen",
@@ -36,7 +36,7 @@ export default function RootLayout({
{/* Muster aus UX-07/UX-08 (web/mail-search): sichtbare Fokusmarkierung - und Theme-Variablen vor dem ersten Paint, auch ohne @nexarch/shl-Shell. */} + und Theme-Variablen vor dem ersten Paint. */}