19-05-2026 Rebrand Pressekonto, Hub-Flux UI und Legacy-Media-Migration

Umbenennung presseportale → pressekonto in Domains, Themes und Dokumentation.
Design-Tokens, Portal-Shell, Customer-Dashboard, Auth- und Admin-PM-Views.
Artisan-Befehl migrate:legacy-media mit Tests und Hub-Flux-Entwicklungsdocs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin Adametz 2026-05-19 16:36:13 +00:00
parent 092ee0e918
commit 0a3e52d603
112 changed files with 8464 additions and 1649 deletions

View file

@ -1,12 +1,12 @@
# Entwicklungskonzept BusinessPortal24, Presseecho & presseportale-Hub Frontend
# Entwicklungskonzept BusinessPortal24, Presseecho & pressekonto-Hub Frontend
> **Stand:** 13. Mai 2026
> **Domains:** `businessportal24.test` / `.com` · `presseecho.test` / `.de` · `presseportale.test` / `.com` (Hub)
> **Theme-Slugs:** `businessportal24` (warm-rotes Editorial) · `presseecho` (grünes Editorial) · `presseportale` (Hub-Blau · Publisher-Landing)
> **Domains:** `businessportal24.test` / `.com` · `presseecho.test` / `.de` · `pressekonto.test` / `.de` (Hub)
> **Theme-Slugs:** `businessportal24` (warm-rotes Editorial) · `presseecho` (grünes Editorial) · `pressekonto` (Hub-Blau · Publisher-Landing)
> **Assets-Dir (geteilt):** `public/build/web/`
> **Ziel:** Editoriales DACH-Pressemitteilungs-Ökosystem mit 1:1-Mockup-Umsetzung. Presseecho nutzt die **gleiche Komponenten-Architektur** wie BP24, der Hub `presseportale.com` ist eine **eigenständige Publisher-Landing** mit klar abgegrenztem Charakter (Hub-Blau + Bernstein, kein Editorial-Feed).
> **Ziel:** Editoriales DACH-Pressemitteilungs-Ökosystem mit 1:1-Mockup-Umsetzung. Presseecho nutzt die **gleiche Komponenten-Architektur** wie BP24, der Hub `pressekonto.de` ist eine **eigenständige Publisher-Landing** mit klar abgegrenztem Charakter (Hub-Blau + Bernstein, kein Editorial-Feed).
Dieses Dokument beschreibt den aktuellen Stand und die wichtigsten Architektur­entscheidungen der BusinessPortal24-, Presseecho- und presseportale-Hub-Frontend-Entwicklung. Es ist die zentrale Anlaufstelle für alle, die im Frontend weiterarbeiten oder neue Seiten ergänzen.
Dieses Dokument beschreibt den aktuellen Stand und die wichtigsten Architektur­entscheidungen der BusinessPortal24-, Presseecho- und pressekonto-Hub-Frontend-Entwicklung. Es ist die zentrale Anlaufstelle für alle, die im Frontend weiterarbeiten oder neue Seiten ergänzen.
---
@ -67,9 +67,9 @@ Request (Host: presseecho.test)
Lokale Domain-Simulation:
- `.env`: `DEV_SIMULATE_DOMAIN=true`, `DEV_SIMULATED_DOMAIN=businessportal24.test|presseecho.test`
- Alternativ: `?theme=businessportal24|presseecho|presseportale` als Query-Parameter
- Alternativ: `?theme=businessportal24|presseecho|pressekonto` als Query-Parameter
> **Hub-Sonderfall (`presseportale.test`):** Diese Domain ist gleichzeitig **Admin-Backend** (Auth/Admin/Customer-Routen, theme = `main`, Build-Dir `build/portal/`) **und** öffentliche **Hub-Landing** (theme = `presseportale`, Build-Dir `build/web/`). In `config/domains.php` existieren beide Einträge (`portal` und `presseportale`) für dieselbe `domain_name`. Der `ThemeServiceProvider` matcht zuerst `portal` (Backend-Standard); für die öffentliche Landing schaltet **`routes/web.php` per `$applyWebDomainConfig('presseportale')` explizit auf das Hub-Theme** um. Auth- und Admin-Routen bleiben unbeeinflusst.
> **Hub-Sonderfall (`pressekonto.test`):** Diese Domain ist gleichzeitig **Admin-Backend** (Auth/Admin/Customer-Routen, theme = `main`, Build-Dir `build/portal/`) **und** öffentliche **Hub-Landing** (theme = `pressekonto`, Build-Dir `build/web/`). In `config/domains.php` existieren beide Einträge (`portal` und `pressekonto`) für dieselbe `domain_name`. Der `ThemeServiceProvider` matcht zuerst `portal` (Backend-Standard); für die öffentliche Landing schaltet **`routes/web.php` per `$applyWebDomainConfig('pressekonto')` explizit auf das Hub-Theme** um. Auth- und Admin-Routen bleiben unbeeinflusst.
### 3.1 Generischer Daten-Provider
@ -138,7 +138,7 @@ Damit das Theme für Presseecho dokumentiert ist, hier der **verbindliche Token-
| Erste Variante | `#1f4d3a → #163a2c` | **zu hell** |
| **Final** | **`#1a3d2e → #122d22`** | **abgenommen** ✅ |
### 3.1.2 presseportale-Hub-Palette (Stand 13.05.2026)
### 3.1.2 pressekonto-Hub-Palette (Stand 13.05.2026)
Der **Hub** ist bewusst eigenständig positioniert: er ist **kein Editorial-Feed**, sondern eine reine Publisher-Landing („Ein Konto zwei Reichweiten"). Er bekommt daher einen ganz eigenen Charakter:
@ -147,7 +147,7 @@ Der **Hub** ist bewusst eigenständig positioniert: er ist **kein Editorial-Feed
* **Akzent:** gedecktes Bernstein `#B07A3A` **bewusst gewählt**, weil weder Orange (BP24) noch Grün (Presseecho). Der Hub steht visuell „zwischen" den beiden Brands.
* **Schrift:** Inter Tight (Standardtext) + JetBrains Mono (Mono) + Source Serif 4 (**nur für Marken-Mentions** der Tochter-Portale, damit typografische Konsistenz zur jeweiligen Brand-Landing erhalten bleibt; im Hub-Fließtext nicht verwendet).
Token-Snapshot aus `resources/css/web/theme-presseportale.css`:
Token-Snapshot aus `resources/css/web/theme-pressekonto.css`:
```css
@theme {
@ -225,12 +225,12 @@ In `config/domains.php` liegt pro Domain ein **`brand`-Block**, der Komponenten
...
],
],
'presseportale' => [ // Hub-Variante (web)
'theme' => 'presseportale',
'pressekonto' => [ // Hub-Variante (web)
'theme' => 'pressekonto',
'brand' => [
'name' => 'presse', // hub-blau
'accent' => 'portale', // bernstein
'footer_legal' => '© :year presseportale · Alle Rechte vorbehalten',
'accent' => 'konto', // bernstein
'footer_legal' => '© :year pressekonto · Alle Rechte vorbehalten',
...
],
],
@ -246,12 +246,12 @@ Die Schreibweise der drei Marken folgt einer einheitlichen Regel: **keine TLD-En
| ------------------ | ------------------------------------------ | ---------------------- | --------------------- |
| `presseecho` | **presse**·*echo* | `#345636` (Forest) | `#9BD5B2` |
| `businessportal24` | **businessportal**·*24* | `#C84A1E` (Orange) | `#F4B098` |
| `presseportale` | **presse**·*portale* | `#B07A3A` (Bernstein) | `#B07A3A` |
| `pressekonto` | **presse**·*konto* | `#B07A3A` (Bernstein) | `#B07A3A` |
**Single Source of Truth:** Die Komponente `<x-web.brand-mark brand="…" />` rendert die Markenschreibung zentral inkl. Span-Splitting, Schriftart und Akzent­farbe. Sie wird überall verwendet, wo eine Marke als Fließtext-Mention erscheint:
* Hub-Komponenten (`hub/top-utility-bar`, `hub/site-header`, `hub/site-footer`, `hub/brand-context-banner`)
* Hub-View `presseportale.blade.php` (Hero-Headline, Architektur-Diagramm, Tarif-Subline, Plattform-Familie, FAQ)
* Hub-View `pressekonto.blade.php` (Hero-Headline, Architektur-Diagramm, Tarif-Subline, Plattform-Familie, FAQ)
* Cross-Brand-Mentions auf BP24-/Presseecho-Landings, falls ergänzt
```blade
@ -417,22 +417,22 @@ Alle Komponenten haben **konsistente Konventionen**:
### 5.4 Hub-Komponenten (`components/web/hub/`)
Der Hub `presseportale.com` hat einen **eigenen, deutlich anderen Charakter** als die beiden Brand-Portale (kein Editorial-Feed, sondern Publisher-Landing) und bekommt daher einen eigenen Komponenten-Namespace. Die Sektionen selbst (Hero, Features, How-it-works, Tarife, Plattform-Familie, Social-Proof, FAQ, CTA) sind als **inline-Markup** in `resources/views/web/presseportale.blade.php` umgesetzt, weil sie page-spezifisch sind.
Der Hub `pressekonto.de` hat einen **eigenen, deutlich anderen Charakter** als die beiden Brand-Portale (kein Editorial-Feed, sondern Publisher-Landing) und bekommt daher einen eigenen Komponenten-Namespace. Die Sektionen selbst (Hero, Features, How-it-works, Tarife, Plattform-Familie, Social-Proof, FAQ, CTA) sind als **inline-Markup** in `resources/views/web/pressekonto.blade.php` umgesetzt, weil sie page-spezifisch sind.
| Datei | Rolle |
| --- | --- |
| `hub/top-utility-bar.blade.php` | Schmale Hub-Blau-Topbar mit Datum, „Publisher-Hub für …"-Brand-Family-Links (rendert `<x-web.brand-mark variant="on-dark" :serif="false">`), Status/Doku/Kontakt. Props: `date`, `siblingPortals` (jetzt Liste mit `brand`-Key statt fixer Strings). |
| `hub/site-header.blade.php` | Wortmark `presse`·`portale` (über `<x-web.brand-mark brand="presseportale" :serif="false">`) + Untertitel „Publisher · Hub", zentrale Primary-Nav (Tarife, So funktioniert es, …), Anmelden + Konto erstellen CTAs. Routes: `login`, `register`. |
| `hub/site-header.blade.php` | Wortmark `presse`·`konto` (über `<x-web.brand-mark brand="pressekonto" :serif="false">`) + Untertitel „Publisher · Hub", zentrale Primary-Nav (Tarife, So funktioniert es, …), Anmelden + Konto erstellen CTAs. Routes: `login`, `register`. |
| `hub/brand-context-banner.blade.php` | **Conditional Banner** unter dem Header greift nur bei `?from=presseecho` oder `?from=businessportal24` und zeigt: „Sie kommen von … Ihr Konto hier funktioniert für beide Portale". Markenname über Brand-Mark (font-serif), „Zurück zu …"-Link nutzt sans-Variante. |
| `hub/site-footer.blade.php` | 4-Spaltiger Hub-Footer (Konto / Plattform / Rechtliches + Brand-Spalte mit Plattform-Familie-Links über Brand-Mark `variant="on-dark"`), Hub-Gradient `linear-gradient(180deg,#1A2540,#0F1729)`. Brand-Block aus `config/domains.php`. |
**Hub-Sektionen als inline-Blade** (in `presseportale.blade.php`):
**Hub-Sektionen als inline-Blade** (in `pressekonto.blade.php`):
1. **Hero** mit Architektur-Diagramm rechts (zentraler Hub-Knoten + Brand-Portal-Karten + Output-Boxen, alles SVG-only).
2. **Was Sie hier können** 3-Karten-Grid (Veröffentlichen / Newsrooms / Reichweite).
3. **So funktioniert es** 4-Step-Ol mit Differenzierungs-Highlight in Schritt 3 (Bernstein-Akzent für „Unsere Qualitätssicherung").
4. **Tarife** 3 Karten (Starter / Standard / Pro mit `.ribbon-recommend`) + breiter Enterprise-Streifen in Hub-Blau.
5. **Hinter presseportale.com** 2-Spalten-Plattform-Familie mit den **Original-Brand-Gradients** der Tochter-Portale (`#1F4D3A→#163A2C` für Presseecho, `#1A1F26→#232A33` für BP24).
5. **Hinter pressekonto.de** 2-Spalten-Plattform-Familie mit den **Original-Brand-Gradients** der Tochter-Portale (`#1F4D3A→#163A2C` für Presseecho, `#1A1F26→#232A33` für BP24).
6. **Aktive Newsrooms** Prose-Auflistung statt Logo-Wall + kompakte Stats-Sidebar.
7. **FAQ** CSS-only-Accordion (`<details>` + `.faq-chev`) mit 8 Fragen, eine offen by default.
8. **CTA-Wiederholung** + Footer.
@ -496,12 +496,12 @@ Drei spiegelbildliche Test-Szenarien:
2. **`feed only shows published presseecho content`** Portal-Trennung gespiegelt: Presseecho + Both sichtbar, BP24-Only und Drafts nicht.
3. **`shows most read releases in the sidebar`** Hits-Sortierung.
### Datei: `tests/Feature/Web/PresseportaleHubHomeTest.php`
### Datei: `tests/Feature/Web/PressekontoHubHomeTest.php`
Fünf Test-Szenarien rund um die Hub-Landing:
1. **`renders the publisher landing shell`** prüft alle Hauptsektionen sind sichtbar (Publisher-Hub, Was Sie hier können, So funktioniert es, Vier Schritte, Tarife, Starter, Standard, Pro, Enterprise, Hinter presseportale.com, Plattform im Überblick, Häufige Fragen, Loslegen, Alle Systeme betriebsbereit).
2. **`loads the hub theme assets, not portal admin`** stellt sicher, dass `theme-presseportale` aus dem Manifest geladen wird (nicht `theme-businessportal24` oder `theme-presseecho`).
1. **`renders the publisher landing shell`** prüft alle Hauptsektionen sind sichtbar (Publisher-Hub, Was Sie hier können, So funktioniert es, Vier Schritte, Tarife, Starter, Standard, Pro, Enterprise, Hinter pressekonto.de, Plattform im Überblick, Häufige Fragen, Loslegen, Alle Systeme betriebsbereit).
2. **`loads the hub theme assets, not portal admin`** stellt sicher, dass `theme-pressekonto` aus dem Manifest geladen wird (nicht `theme-businessportal24` oder `theme-presseecho`).
3. **`hides the brand-context banner without a from parameter`** Default-Aufruf zeigt keinen „Sie kommen von …"-Banner.
4. **`shows the brand-context banner when arriving from presseecho`** `?from=presseecho` triggert den Banner inkl. Link „Zurück zu presseecho.de".
5. **`shows the brand-context banner when arriving from businessportal24`** `?from=businessportal24` triggert den Banner für BP24.
@ -563,9 +563,9 @@ vendor/bin/pint --dirty --format agent
| 13 | 12.05.2026 | Brand-Konfiguration in `config/domains.php` pro Domain (`brand.name`, `brand.accent`, `brand.tagline_*`, `brand.newsletter_topics`, `brand.footer_legal`, `brand.about_label`) `site-header`, `site-footer`, `newsletter-strip` lesen daraus | ✅ |
| 14 | 12.05.2026 | `press-release-feed`-Volt-Component portal-agnostisch (Prop `:portal`); Aufruf vom View-Layer aus | ✅ |
| 15 | 12.05.2026 | `PresseechoHomeTest` analog zu `Businessportal24HomeTest` (3 Szenarien, RefreshDatabase) | ✅ |
| 16 | 13.05.2026 | **presseportale-Hub-Landing live**: neues Web-Theme `presseportale` (Hub-Blau + Bernstein, `theme-presseportale.css`), eigener Komponenten-Namespace `components/web/hub/` (Top-Bar, Site-Header, Brand-Context-Banner, Site-Footer), Hub-View `web/presseportale.blade.php` mit Hero/Architektur-Diagramm, Features, How-it-works, Tarife (Starter/Standard/Pro+Ribbon, Enterprise-Streifen), Plattform-Familie, Aktive-Newsrooms, FAQ-Accordion, CTA. `routes/web.php` schaltet für `presseportale.test` auf das Hub-Theme um. Root-Route in `routes/admin.php` entfernt (Layout referenziert jetzt `route('dashboard')`). | ✅ |
| 17 | 13.05.2026 | `PresseportaleHubHomeTest` (5 Szenarien inkl. Brand-Context-Banner-Conditional). Vite-Config + ThemeHelper + `web-master`-Fonts (Inter Tight + JetBrains Mono ohne Serif) für `presseportale` ergänzt. | ✅ |
| 18 | 13.05.2026 | **Brand-Mark-Konvention etabliert** (Feintuning Marken-Schreibweise): keine TLD am Marken­schriftzug, Akzent farblich vom Basis-Wort abgesetzt. Single Source of Truth `<x-web.brand-mark>` (Marken-Tabelle inkl. Standard- und On-Dark-Akzentfarben, Serif/Sans-Switch). `config/domains.php` umgestellt (`presseecho`: `name=presse`/`accent=echo`; `presseportale`: `name=presse`/`accent=portale`; Footer-Legal & Meta-Texte ohne TLD). Hub-Komponenten und Hub-View durchgehend auf Brand-Mark migriert (Top-Utility-Bar, Site-Header, Brand-Context-Banner, Site-Footer, Hero-Headline, Architektur-Diagramm, Tarif-Subline, Plattform-Familie, FAQ). Hub-Theme bekommt Source Serif 4 als `--font-serif` (für Marken-Mentions) Bunny-Font-Loader erweitert. **+1 neuer Test `uses the brand-mark splitting without TLDs`**; alle 12 Web-Tests grün. | ✅ |
| 16 | 13.05.2026 | **pressekonto-Hub-Landing live**: neues Web-Theme `pressekonto` (Hub-Blau + Bernstein, `theme-pressekonto.css`), eigener Komponenten-Namespace `components/web/hub/` (Top-Bar, Site-Header, Brand-Context-Banner, Site-Footer), Hub-View `web/pressekonto.blade.php` mit Hero/Architektur-Diagramm, Features, How-it-works, Tarife (Starter/Standard/Pro+Ribbon, Enterprise-Streifen), Plattform-Familie, Aktive-Newsrooms, FAQ-Accordion, CTA. `routes/web.php` schaltet für `pressekonto.test` auf das Hub-Theme um. Root-Route in `routes/admin.php` entfernt (Layout referenziert jetzt `route('dashboard')`). | ✅ |
| 17 | 13.05.2026 | `PressekontoHubHomeTest` (5 Szenarien inkl. Brand-Context-Banner-Conditional). Vite-Config + ThemeHelper + `web-master`-Fonts (Inter Tight + JetBrains Mono ohne Serif) für `pressekonto` ergänzt. | ✅ |
| 18 | 13.05.2026 | **Brand-Mark-Konvention etabliert** (Feintuning Marken-Schreibweise): keine TLD am Marken­schriftzug, Akzent farblich vom Basis-Wort abgesetzt. Single Source of Truth `<x-web.brand-mark>` (Marken-Tabelle inkl. Standard- und On-Dark-Akzentfarben, Serif/Sans-Switch). `config/domains.php` umgestellt (`presseecho`: `name=presse`/`accent=echo`; `pressekonto`: `name=presse`/`accent=konto`; Footer-Legal & Meta-Texte ohne TLD). Hub-Komponenten und Hub-View durchgehend auf Brand-Mark migriert (Top-Utility-Bar, Site-Header, Brand-Context-Banner, Site-Footer, Hero-Headline, Architektur-Diagramm, Tarif-Subline, Plattform-Familie, FAQ). Hub-Theme bekommt Source Serif 4 als `--font-serif` (für Marken-Mentions) Bunny-Font-Loader erweitert. **+1 neuer Test `uses the brand-mark splitting without TLDs`**; alle 12 Web-Tests grün. | ✅ |
| 19 | 12.05.2026 | **Aktuell offen:** Detailseite, Branchenseite, Veröffentlichen-Landing für BP24 + Presseecho. Hub-Folgeseiten (Konto-Erstellen-Flow als Landing, Tarif-Detail, Doku-Hub) ebenfalls offen. | 🟡 |
---

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,216 @@
# Phase 0 — Design-Tokens vereinheitlichen
> **Ziel**: Single Source of Truth für alle Design-Tokens (Farben, Fonts,
> Radii, Schatten). Sowohl Hub-Build als auch Portal-Build beziehen ihre
> Werte aus derselben Datei. **Visuell ändert sich noch nichts.**
**Status**: ✅ abgeschlossen am 2026-05-19
**Risiko**: sehr niedrig
**Aufwand (tatsächlich)**: ~½ Tag
## Ergebnis-Check (2026-05-19)
- Single Source of Truth liegt in `resources/css/shared/design-tokens.css`.
- Web-Build und Portal-Build importieren sie beide.
- **Visuelle Unverändertheit verifiziert**:
- Hub (`pressekonto.test/`, `/login`, `/register`) — unverändert.
- Portal (`/dashboard`) — FluxUI-Defaults bleiben dominant
(`--font-sans: "Instrument Sans"`, Zinc-Palette, `#3ea3dc`-Akzent).
- Build-Sizes:
- `theme-pressekonto: 193 kB` (vorher 189 kB · +4 kB für neue Tokens)
- `theme-presseecho`, `theme-businessportal24`: praktisch unverändert
- `portal: 408 kB` (vorher 397 kB · +12 kB für zusätzlich bereitgestellte Token-Vars im `:root`)
- Details: `PROGRESS.md` (Eintrag vom 2026-05-19).
## Warum
Heute leben Tokens an zwei Orten:
- `resources/css/web/theme-pressekonto.css` — Hub-Tokens (Hub-Blau,
Bernstein, Buchpapier, Inter Tight)
- `resources/css/portal.css` — Portal-Tokens (Zinc, `#3ea3dc`, Instrument
Sans)
Solange diese parallel gepflegt werden, **driften** sie auseinander. Wir
ziehen die gemeinsame Wahrheit in eine eigene Datei und referenzieren
sie aus beiden Welten.
## Liefergegenstand
```
resources/css/shared/
└── design-tokens.css ← NEU
```
Inhalt: alle `--color-*`, `--font-*`, `--radius-*`, `--shadow-*` Tokens,
die in Hub und Portal gleichermaßen gelten. Strukturiert als
`@theme`-Block, sodass Tailwind v4 die Variablen sowohl als
CSS-Custom-Properties als auch als Tailwind-Utility-Klassen erkennt.
## Schritte
### 1. Token-Inventur aus `theme-pressekonto.css`
Die folgenden Tokens werden aus `theme-pressekonto.css` extrahiert und
nach `shared/design-tokens.css` verschoben:
#### Surfaces
- `--color-bg`, `--color-bg-elev`, `--color-bg-rule`, `--color-bg-rule-strong`
- `--color-bg-dark`, `--color-bg-card`, `--color-bg-card-warm`,
`--color-bg-card-warm-border`, `--color-bg-card-warm-hover`,
`--color-bg-card-warm-rule`
#### Hub-Palette
- `--color-hub`, `--color-hub-2`, `--color-hub-3`
- `--color-hub-soft`, `--color-hub-soft-2`, `--color-hub-line`
- `--color-topbar`, `--color-topbar2`, `--color-topbar-deep`
#### Akzent (Bernstein)
- `--color-accent`, `--color-accent-deep`, `--color-accent-soft`,
`--color-accent-warm`
#### Ink (Anthrazit)
- `--color-ink`, `--color-ink-2`, `--color-ink-3`, `--color-ink-4`
- `--color-ink-on-dark`, `--color-ink-on-dark-2`, `--color-ink-on-dark-3`,
`--color-ink-on-dark-muted`, `--color-ink-on-dark-rule`
#### Brand-Aliase + Status
- `--color-brand`, `--color-brand-deep`, `--color-brand-soft`
- `--color-live`, `--color-gain`, `--color-loss`, `--color-ok`
#### Editorial / Cards
- `--color-card-warm-cat`, `--color-card-warm-title`,
`--color-feature-line`, `--color-feature-dot`
#### Status (für KPI-Cards / Badges — laut Mockup ergänzen)
- `--color-warn` `#A87A1F`, `--color-warn-soft` `#F6EAC8`
- `--color-err` `#A8331F`, `--color-err-soft` `#F4DAD2`
- `--color-ok-soft` `#E2F1E5`
#### Fonts
- `--font-sans` (Inter Tight)
- `--font-serif` (Source Serif 4 — nur für Brand-Mark)
- `--font-mono` (JetBrains Mono)
#### Layout
- `--container-layout: 1280px`
#### Radii (laut Mockup)
- `--radius-xs: 3px`, `--radius-sm: 4px`, `--radius-md: 6px`,
`--radius-lg: 8px`
#### Schatten (laut Mockup + Hub-Login)
- `--shadow-soft`: leicht warm, für Cards
- `--shadow-card`: Standard-Card-Schatten
- `--shadow-card-hover`: Hover-Stufe
- `--shadow-auth`: weiche Glocke unter Auth-Card
### 2. Datei `resources/css/shared/design-tokens.css` anlegen
Aufbau:
```css
/**
* Hub × FluxUI — Gemeinsame Design-Tokens
*
* Single Source of Truth für Hub-Frontend (build/web) und Portal-Backend
* (build/portal). Beide CSS-Builds @import diese Datei.
*
* Token-Names sind STABIL — Werte können sich ändern (z.B. Dark Mode in
* Phase 5), Namen nicht.
*/
@theme {
/* Surfaces */
--color-bg: #f6f4ef;
--color-bg-elev: #fbfaf6;
/* … alle Tokens aus der Inventur … */
}
/* Dark Mode (vorbereitet, in Phase 5 finalisiert) */
@media (prefers-color-scheme: dark) {
@theme {
/* Spätere Dark-Werte */
}
}
```
### 3. `theme-pressekonto.css` refactoren
Die `@theme {}`-Definitionen werden durch einen
`@import "../shared/design-tokens.css";` ersetzt. Nur die `@layer
components {}`-Klassen (`.eyebrow`, `.auth-card`, `.field-input`, etc.)
bleiben in `theme-pressekonto.css`.
`:root { … --background, --primary … }`-HSL-Variablen für Legacy-Komponenten
bleiben ebenfalls hier (sind Portal-unspezifisch).
### 4. `portal.css` minimal vorbereiten (noch keine Werte übernehmen)
In Phase 0 importieren wir die Token-Datei in `portal.css`, **aber lassen
das alte `@theme`-Setup mit Zinc/Accent zunächst stehen**. Damit:
- Beide Welten greifen technisch auf die gleiche Datei zu
- Aber Portal bleibt visuell unverändert (Zinc-Palette gewinnt durch
Reihenfolge im `@theme`)
In Phase 1 wird das Zinc-Setup dann **gezielt durch Hub-Werte ersetzt**.
### 5. Build & Verifikation
```bash
npm run build:web # → erzeugt theme-pressekonto.css ohne Drift
npm run build:portal # → erzeugt portal.css unverändert
```
Erwartung:
- Hub-Landing rendert visuell **identisch** wie vorher
- Hub-Auth-Pages rendern visuell **identisch** wie vorher
- Portal rendert visuell **identisch** wie vorher
Smoke-Test (kein neues Test-Schreiben nötig):
```bash
php artisan tinker --execute '
$urls = [
"https://pressekonto.test/",
"https://pressekonto.test/login",
"https://pressekonto.test/dashboard",
];
foreach ($urls as $u) {
echo $u . " => " .
app(\Illuminate\Contracts\Http\Kernel::class)
->handle(\Illuminate\Http\Request::create($u, "GET"))
->getStatusCode() . "\n";
}'
```
Alle 3 URLs müssen weiterhin `200` liefern (für `/dashboard` ggf.
`302`-Redirect, je nach Auth-Status — beides ist okay, solange kein `500`).
## Akzeptanzkriterien
- [ ] `resources/css/shared/design-tokens.css` existiert mit allen Hub-Tokens
- [ ] `theme-pressekonto.css` importiert die Token-Datei und enthält
keine doppelten `--color-*`-Definitionen mehr
- [ ] `portal.css` importiert die Token-Datei (Werte werden in Phase 1 genutzt)
- [ ] `npm run build:web` und `npm run build:portal` laufen ohne Fehler durch
- [ ] Hub-Landing, Hub-Auth und Portal-Login visuell **unverändert**
- [ ] Pint passed (`vendor/bin/sail bin pint --dirty --format agent`)
## Risiken & Fallstricke
- **Tailwind v4 + `@theme`**: Mehrere `@theme {}`-Blöcke in importierten
Dateien werden zusammengeführt. Das funktioniert, solange die
Token-Namen eindeutig sind.
- **Reihenfolge der Imports**: Tokens müssen **vor** den
`@layer components {}`-Definitionen importiert werden, sonst
greifen die Variablen in den Komponenten nicht.
- **Portal-Tailwind-Config**: `tailwind.portal.config.js` darf die
Token-Datei nicht ausschließen. `@source`-Direktiven prüfen.
## Was Phase 0 NICHT macht
- Portal sieht **noch nicht** wie der Hub aus — das ist Phase 1
- Keine Änderung am Sidebar-Layout, am Logo oder am Dashboard
- Keine Dark-Mode-Aktivierung (nur vorbereitet)

View file

@ -0,0 +1,290 @@
# Phase 1 — Portal-Shell ans Hub-Design angleichen
> **Ziel**: Sidebar, Topbar und Layout-Container des Portals sehen aus
> wie das Mockup `User Dashboard presseportale.html`. Inhalt der
> einzelnen Pages bleibt unverändert — wir tauschen nur die Shell.
**Status**: ✅ abgeschlossen am 2026-05-19
**Risiko (tatsächlich)**: niedrig — keine Test-Regressionen
**Aufwand (tatsächlich)**: ~½ Tag (kleiner als geschätzt, weil Topbar
auf Phase 2 verschoben wurde)
## Ergebnis-Check (2026-05-19)
**Umgesetzt**:
- `portal.css` mit Hub-Token-Bridge, Inter-Tight-Font, Zinc→Hub-Mapping,
FluxUI-Overrides für Sidebar / Navlist / Buttons / Cards.
- Sidebar mit Brand-Mark + Eyebrow „Publisher · Hub", neuem Hub-Stil-
Testmodus-Block, ohne Starter-Kit-Resources-Block.
- Customer-Banner (`app.blade.php`) im Hub-Soft-Look mit Hub-Pille.
- `class="dark"` aus Sidebar-Layout entfernt — Light Mode ist Default.
- Font-Wechsel auf Bunny: `inter-tight + jetbrains-mono + source-serif-4`.
**Verschoben auf Phase 2**:
- Eigene Topbar mit Breadcrumb + Bridge-Row + Search + „Neue Mitteilung"-CTA
(lebt sinnvoller im Customer-Dashboard-Kontext).
- Konto-Switcher als Sidebar-Header oben (statt User-Menü unten).
**Build-Sizes**:
- `portal.css: 409.03 kB` (vorher 408.89 kB · +0.14 kB · weit unter dem
10 %-Limit aus den Akzeptanzkriterien).
**Tests**:
- Auth-Test-Suite Vergleich (Stash vs Phase 1): `8 failed, 15 passed`
in beiden Ständen — 0 zusätzliche Regressionen durch Phase 1.
- Zwei Tests im Zuge des Login-Fixes angepasst (Admin-Rolle bzw.
`terms_accepted: true`), siehe `PROGRESS.md` Eintrag „Phase 1".
**Details**: `PROGRESS.md` (Eintrag vom 2026-05-19, Abschnitt „Phase 1").
## Sichtbarer Mehrwert
Nach Phase 1 sieht der eingeloggte User:
- Eine **warme Sidebar** im Hub-Stil mit Brand-Wortmarke statt
Starter-Kit-Logo
- Eine **schlanke Topbar** mit Breadcrumb, Bridge-Row, Search, Notification,
"Neue Mitteilung"-Button
- Den **Testmodus-Block** (Impersonation) als Hub-Karten-Element
- Den **Konto-Switcher** als oberen Sidebar-Header
Innenleben (Tabellen, Formulare, Cards) bleiben FluxUI — wirken aber
durch Token-Anpassung **automatisch passender**.
## Liefergegenstand
### Geänderte Dateien
| Datei | Änderung |
|-------|----------|
| `resources/css/portal.css` | Zinc → Hub-Palette, Font auf Inter Tight, `--color-accent` auf Hub-Blau, FluxUI-Overrides |
| `resources/views/components/layouts/app/sidebar.blade.php` | Brand-Mark statt App-Logo, Eyebrow „Publisher · Hub", Sidebar-Design am Mockup orientiert, Testmodus-Block neu |
| `resources/views/partials/head.blade.php` | Font-Wechsel (Bunny: inter-tight + jetbrains-mono statt instrument-sans) |
| `resources/views/components/layouts/app.blade.php` | Customer-Banner ggf. an neues Design anpassen |
### Vermutlich gelöscht
| Datei | Begründung |
|-------|------------|
| `resources/views/components/layouts/app/header.blade.php` | Wird laut Inventur nirgends referenziert |
| `resources/views/partials/admin-head.blade.php` | Legacy, im Code nicht eingebunden |
| `resources/views/components/app-logo.blade.php` | Wird durch `brand-mark` ersetzt |
| `resources/views/components/app-logo-icon.blade.php` | Wird durch `brand-mark` ersetzt |
Vor Löschung: per `rg "x-app-logo"` und `rg "auth.split|auth.card"`
prüfen, dass nichts mehr referenziert wird.
## Schritte
### 1. `portal.css` — Token-Bridge zu FluxUI
```css
@import "tailwindcss";
@import "../../vendor/livewire/flux/dist/flux.css";
@import "./shared/design-tokens.css"; /* aus Phase 0 */
@source '../views';
@source '../../vendor/livewire/flux-pro/stubs/**/*.blade.php';
@source '../../vendor/livewire/flux/stubs/**/*.blade.php';
@custom-variant dark (&:where(.dark, .dark *));
@theme {
/* FluxUI-Akzent auf Hub-Blau */
--color-accent: var(--color-hub);
--color-accent-content: var(--color-hub);
--color-accent-foreground: #ffffff;
/* Zinc auf warmes Buchpapier mappen */
--color-zinc-50: var(--color-bg-elev); /* #FBFAF6 */
--color-zinc-100: var(--color-bg); /* #F6F4EF */
--color-zinc-200: var(--color-bg-rule); /* #E2DDD0 */
--color-zinc-300: #cfc8b5;
--color-zinc-400: var(--color-ink-4); /* #8A918D */
--color-zinc-500: var(--color-ink-3); /* #5A6360 */
--color-zinc-600: var(--color-ink-2); /* #3A413D */
--color-zinc-700: var(--color-ink); /* #1A1F1C */
--color-zinc-800: var(--color-hub-2); /* #243152 */
--color-zinc-900: var(--color-hub); /* #1A2540 */
--color-zinc-950: var(--color-topbar-deep); /* #0F1729 */
}
```
#### FluxUI-spezifische Overrides
```css
/* Navlist — Hub-Stil mit Active-Strip links */
[data-flux-navlist-item][data-active="true"] {
background: var(--color-hub-soft);
color: var(--color-hub);
font-weight: 600;
position: relative;
}
[data-flux-navlist-item][data-active="true"]::before {
content: "";
position: absolute;
left: 0;
top: 6px;
bottom: 6px;
width: 2px;
background: var(--color-hub);
border-radius: 0 2px 2px 0;
}
/* Buttons — Hub-Primär */
[data-flux-button][data-variant="primary"] {
background: var(--color-hub);
color: #ffffff;
}
[data-flux-button][data-variant="primary"]:hover {
background: var(--color-hub-2);
}
/* Sidebar-Section-Headings */
[data-flux-navlist] [data-flux-navlist-group-heading] {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--color-ink-4);
}
/* Cards bekommen Hub-Buchpapier statt Zinc */
[data-flux-card] {
background: var(--color-bg-card);
border-color: var(--color-bg-rule);
}
```
> **Hinweis**: Die exakten `[data-flux-*]`-Attribute werden beim Bauen
> per Dev-Tools verifiziert. Die hier gezeigten sind die wahrscheinlichsten
> laut FluxUI-Doku.
### 2. `partials/head.blade.php` — Font wechseln
```diff
- <link href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600"
+ <link href="https://fonts.bunny.net/css?family=inter-tight:400,500,600,700|jetbrains-mono:400,500,600|source-serif-4:400,500,600,700"
rel="stylesheet" />
```
### 3. Sidebar neu — `components/layouts/app/sidebar.blade.php`
Aufbau **strikt am Mockup** orientiert (s. `dev/frontend/tailwind_v3/User Dashboard presseportale.html`):
#### Sidebar-Aufbau (oben → unten)
1. **Brand-Block** (`px-5 pt-6 pb-5`):
- `<x-web.brand-mark brand="pressekonto" :serif="false" />` (19 px, bold)
- Eyebrow „Publisher · Hub" darunter
- **Konto-Switcher-Button** mit Avatar (Initialen), Name, Firma — als
`<flux:dropdown>`-Trigger mit Custom-Stil
2. **Navigation** (`px-3 flex-1`):
- Section „Mein Bereich": Übersicht, Meine Pressemitteilungen (mit Counter-Badge),
Firmen, Buchungen & Add-ons, Statistiken (disabled, „bald")
- Section „Finanzen": Credits & Tarif (bald), Rechnungen, Zahlungsarten (bald)
- Section „Konto": Profil, Sicherheit, API & Integrationen, Benachrichtigungen (bald)
- Section „Administration" (nur für Admins/Editoren): Press-Releases, Companies, Users, Roles, etc.
3. **Testmodus-Block** (`px-4 pb-4`) — wenn Impersonation aktiv:
- Hub-blaues Panel mit Bernstein-Eyebrow „Testmodus aktiv"
- „Zurück zum Admin"-Button (weiß auf Hub-Blau)
4. **Resources-Block** (`px-3 pb-5 border-t`):
- Optional: Tailwind CSS, Hero Icons, Flux UI, Repository
- Im Live-Portal vermutlich weglassen oder durch eigene Hilfe-Links ersetzen
#### Komponenten-Strategie
Wo immer möglich **FluxUI-Komponenten** verwenden:
- `<flux:sidebar>` als Wrapper
- `<flux:navlist>` für Section-Gruppen
- `<flux:navlist.item>` für Einträge, `data-active`-Markierung übernimmt CSS-Override
- `<flux:dropdown>` für Konto-Switcher
- `<flux:badge>` für Counter
Wo FluxUI nicht passt (z.B. Konto-Switcher-Header mit Avatar+Name+Firma+Chevron):
**Custom Blade** in `<x-portal.account-switcher>` als wiederverwendbare Komponente.
### 4. `app.blade.php` — Customer-Banner Hub-Stil
Der Banner „User Backend" (für Customer-Rolle) wird visuell ans Hub-Design
angeglichen — Hub-Soft-Hintergrund, Hub-Blau-Eyebrow, Firma-Switcher als
Pille.
### 5. `class="dark"` entfernen
In allen Auth- und App-Layouts:
```diff
- <html lang="..." class="dark">
+ <html lang="...">
```
FluxUI Appearance-Switcher in den Settings übernimmt die Steuerung.
Dark-Mode-Werte landen in Phase 5 in `design-tokens.css`.
### 6. Build & Visual-Check
```bash
npm run build:portal
```
Öffnen und visuell prüfen:
- `https://pressekonto.test/dashboard` (Admin-Dashboard)
- `https://pressekonto.test/admin/me` (Customer-Dashboard)
- `https://pressekonto.test/settings/profile`
- `https://pressekonto.test/admin/companies`
- `https://pressekonto.test/admin/press-releases`
Erwartung:
- Sidebar wie Mockup
- Topbar mit Breadcrumb + Aktionen
- Inhalt unverändert, aber Tabellen/Cards/Buttons in Hub-Tonart
- Keine kaputten Layouts
## Akzeptanzkriterien
- [ ] Sidebar nutzt `<x-web.brand-mark brand="pressekonto" />` statt `x-app-logo`
- [ ] Sidebar-Sections und Active-Item entsprechen visuell dem Mockup
- [ ] Topbar hat Breadcrumb, Search, Notification, „Neue Mitteilung"-CTA
- [ ] Font im Portal ist **Inter Tight** (sichtbar im DevTools)
- [ ] FluxUI-Buttons (Primary) sind **Hub-Blau**, nicht mehr `#3ea3dc`
- [ ] FluxUI-Tabellen sehen sauber aus mit Buchpapier-Hintergrund
- [ ] `class="dark"` ist aus allen Layouts entfernt
- [ ] Alle bestehenden Routen `/dashboard`, `/admin/*`, `/admin/me/*`,
`/settings/*` rendern Status 200
- [ ] Pint & vorhandene Tests bleiben grün
- [ ] Page-Last-Vergleich: `portal.css`-Größe darf um max. 10 % wachsen
## Risiken & Fallstricke
- **FluxUI-Selektoren ändern sich**: `[data-flux-*]`-Attribute sind nicht
öffentlich dokumentierte API, sondern Implementation-Detail. Bei
FluxUI-Update kann ein Override brechen. Mitigation: Selektoren so
spezifisch wie nötig, so generisch wie möglich; gut kommentieren.
- **Zinc-Remapping kann Side-Effects haben**: Stellen, die hardcoded
`zinc-700` für Text-Farben verwenden, werden plötzlich Hub-Blau.
Mitigation: nach dem Build kritische Pages durchklicken; gegebenenfalls
einzelne Stellen explizit auf `text-ink` umstellen.
- **Tailwind v4 Custom-Properties**: Reihenfolge im `@theme`-Block ist
wichtig — Tokens müssen vor Overrides definiert sein.
- **Mobile Sidebar**: Das Mockup zeigt nur Desktop. `flux:sidebar` hat
einen eingebauten Mobile-Toggle — der bleibt erhalten und wird visuell
angeglichen.
## Was Phase 1 NICHT macht
- Dashboards (`admin.dashboard`, `customer.dashboard`) bekommen noch
**kein** Stat-Card-Strip-Redesign — das ist Phase 2 + 3
- Listen-Pages werden nicht überarbeitet — passt automatisch durch
Token-Anpassung „gut genug" bis Phase 4
- Dark Mode wird nicht aktiv ausgeliefert — Token-Werte werden vorbereitet,
aber bleiben in Phase 5
## Review-Punkt
Nach Phase 1 wird Frank/Du visuell drüberschauen und entscheiden:
- Welche Detail-Pages priorisiert werden (Phase 4)
- Ob Customer-Dashboard (Phase 2) direkt danach kommt
- Ob das Auth-Layout im Portal (`auth.split`, `auth.card`) entfernt werden kann

View file

@ -0,0 +1,162 @@
# Weitere Phasen — Outline
> Übersicht über Phasen 26. Diese werden detailliert ausgearbeitet,
> sobald Phase 0+1 abgenommen sind. Jede Phase bekommt ein eigenes
> `0X-PHASE-N-NAME.md`, wenn sie aktiv wird.
---
## Phase 2 — Customer-Dashboard auf Mockup-Stil
**Status**: ⚪ später · **Aufwand**: ~½ Tag · **Risiko**: niedrig
### Ziel
`livewire/customer/dashboard.blade.php` matched das Mockup
`User Dashboard presseportale.html` zu ≥ 90 %.
### Bausteine
- **Page-Header** mit „Mein Dashboard" + Begrüßung + Firma-zugeordnet-Pille
- **KPI-Reihe** (4 Stat-Cards mit linkem Farb-Strip)
- Gesamt (Hub-Blau-Strip)
- Veröffentlicht (Grün-Strip, `is-ok`)
- In Prüfung (Bernstein-Strip, `is-warn`)
- Entwürfe (Grau-Strip, `is-muted`)
- **2-Spalten-Grid**:
- Links: Empty-State / Liste „Meine letzten Pressemitteilungen"
- Rechts: „Datenqualität" mit Progress-Bars (Profil-Vollständigkeit,
Rechnungsadresse)
- **Unten**:
- Links: Firmen-Slots
- Rechts: Brand-Bridge-Dark-Card (presseecho + businessportal24)
### Neue Blade-Components
- `<x-portal.stat-card>` — Stat-Card mit Slot + Strip-Variante
- `<x-portal.hint-card>` — Datenqualitäts-Hint mit Icon + Progress-Bar
- `<x-portal.bridge-card>` — Dunkle Brand-Bridge-Card
→ Diese landen in `resources/views/components/portal/`.
---
## Phase 3 — Admin-Dashboard konsistent
**Status**: ⚪ später · **Aufwand**: ~½ Tag · **Risiko**: niedrig
### Ziel
`resources/views/admin/dashboard.blade.php` nutzt dasselbe Vokabular wie
Customer-Dashboard.
### Heutiger Stand
Reines Tailwind mit `zinc-*`-Klassen, **keine** FluxUI-Komponenten,
visuell aus der Zeit gefallen.
### Schritte
- KPI-Karten als `<x-portal.stat-card>`
- Wenn Charts vorhanden: über `flux:chart` umsetzen (FluxUI Pro)
- Recent-Activity-Liste in `flux:card` mit Hub-Akzenten
---
## Phase 4 — Listen-/Detail-Pages durchgehen
**Status**: 🚧 iterativ · **Aufwand**: ~35 Tage gesamt · **Risiko**: niedrig
### Ziel
Alle Volt-Pages im Admin- und Customer-Bereich nutzen denselben Hub-Stil.
### Vorgehen
- Pro Page: 1530 min
- In Päckchen geteilt:
- **4A** = Press-Releases Listen (Admin + Customer) — ✅ **abgeschlossen**
siehe `07-PHASE-4A-PRESS-RELEASES-LISTEN.md`
- **4B** = Press-Releases Detail/Show (Admin + Customer) — ✅ **abgeschlossen**
siehe `08-PHASE-4B-PRESS-RELEASES-DETAIL.md`
- **4C** = Press-Releases Forms (create/edit, Admin + Customer) — ✅ **abgeschlossen**
siehe `09-PHASE-4C-PRESS-RELEASES-FORMS.md`
- **4D** = Companies (`admin.companies.*`) — ⚪ pending
- **4E** = Profile/Settings (`settings.*`, `me.profile`, `me.security`) — ⚪ pending
- **4F** = Restliche Admin-Bereiche — ⚪ pending
### Was geändert wird
- Page-Header-Struktur (Eyebrow + H1 + Subtitle + Aktions-Bar)
- Filter-Bars werden Hub-konform gestylt
- `flux:table`-Header bekommen Eyebrow-Optik
- Form-Felder bleiben FluxUI, aber mit Hub-Tokens
### Was nicht geändert wird
- Logik / Volt-Methoden
- Datenbank / Models
---
## Phase 5 — Dark Mode konsistent
**Status**: ⚪ später · **Aufwand**: ~½ Tag · **Risiko**: niedrig
### Ziel
Dark Mode funktioniert sauber im Portal, **ohne doppelte UI-Pflege**.
### Quelle
`dev/frontend/tailwind_v3/User Dashboard presseportale Dark.html` hat
alle Dark-Tokens bereits vorgegeben.
### Schritte
1. In `shared/design-tokens.css` Dark-Werte ergänzen:
```css
@media (prefers-color-scheme: dark) {
@theme {
--color-bg: #0E1218;
--color-bg-elev: #14181F;
--color-hub: #5A78C2;
--color-accent: #D9A560;
/* … */
}
}
.dark {
/* gleiche Werte für expliziten Switch */
}
```
2. `class="dark"` wird **nicht** mehr hardcoded gesetzt
3. Flux Appearance-Switcher (`settings/appearance.blade.php`) steuert
`.dark` auf `<html>`
4. Hub-Frontend bleibt erstmal Light-Only (so wie heute)
### Erwartung
- Settings → Appearance → „Dark" schaltet Portal um
- Settings → Appearance → „System" folgt OS-Präferenz
- Sidebar, Topbar, Dashboards, Listen, Forms — alles funktioniert
- Hub-Landing und Hub-Auth bleiben Light
---
## Phase 6 — Auth-Konsolidierung (optional)
**Status**: ⚪ optional · **Aufwand**: 01 Tag · **Risiko**: mittel
### Frage
Bleibt der Hub-Login (`auth/pressekonto`-Layout, Web-Build) so wie er ist?
Oder konsolidieren wir auf den Portal-Build?
### Pro Konsolidierung
- Ein Build weniger
- Konsistente Komponenten-Sprache
### Pro Beibehaltung (heutiger Stand)
- Hub-Atmosphäre der Auth-Seiten (Konzentrische Kreise, Hub-Grid) ist
visuell sehr stark — würde verloren gehen
- Hub-Auth ist **leichtgewichtig** (kein FluxUI im Bundle)
- Vorlage `Login pressekonto A3 Tailwind.html` ist bewusst minimalistisch
### Entscheidung
Vermutlich **beibehalten**. Aber: Die alten Auth-Layouts
`auth/simple.blade.php`, `auth/split.blade.php`, `auth/card.blade.php` aus
dem Starter-Kit können vermutlich gelöscht werden, sobald geprüft ist,
dass keine Komponente sie noch verwendet.
### Schritte (wenn konsolidiert)
- Hub-Auth-CSS in `portal.css` ziehen (mit `@source` für die Tokens)
- `auth/pressekonto`-Layout auf Portal-Build umstellen
- Hub-Auth-Klassen (`.auth-card`, `.auth-grid`, `.auth-btn-primary`)
bleiben — laufen nur jetzt aus dem Portal-Bundle
- `theme-pressekonto.css` aus dem Web-Build entfernen (oder behalten
für die Landing)

View file

@ -0,0 +1,177 @@
# Phase 2 — Customer-Dashboard auf Mockup-Stil
> Detail-Plan analog zu `01-PHASE-0-TOKENS.md` und
> `02-PHASE-1-PORTAL-SHELL.md`. Wird beim Abschluss auf Status `✅ abgeschlossen`
> gesetzt; Lessons learned wandern in `PROGRESS.md`.
**Status**: ✅ abgeschlossen · **Aufwand**: ~½ Tag · **Risiko**: niedrig
---
## Ziel
`resources/views/livewire/customer/dashboard.blade.php` matched das Mockup
`dev/frontend/tailwind_v3/User Dashboard presseportale.html` zu ≥ 90 %.
Sichtbarer Wow-Moment für den Kunden, ohne neue Business-Logik.
## Was sich ändert
### Visuell
| Bereich | Heute | Mockup |
| ----------------- | ------------------------------------------------ | --------------------------------------------------------------------- |
| Page-Header | `<flux:card>` mit `<flux:heading>` „Willkommen“ | Hub-Badge + Eyebrow + großes H1 + Untertitel + rechts Status-Pille |
| KPI-Reihe | 4 schmale `<flux:card>` mit `<flux:text>`-Zahl | 4 `stat-card`s mit Strip links, Mono-Zahl groß, Sub-Info, Sparkline |
| Pressemitteilungen-Block | `<flux:card>` mit Liste / Empty-State | `panel` mit `section-eyebrow`, Empty-State mit Schritt-Karten 01/02/03 |
| Datenqualität | Grid aus `<flux:badge>`-Karten | `panel` rechts mit 2 `hint-card`s (Progress-Bar + Action-Link) |
| Firmen-Slot | `<flux:card>` mit Firmen-Liste | `panel` mit gestrichelten Empty-Slot-Karten + Hinweis-Box |
| Brand-Bridge | (nicht vorhanden) | `panel-dark` rechts: presseecho + businessportal24 Status |
| Footer | (nicht vorhanden) | Subtle-Footer mit Tastenkürzel / Changelog / Status / Support |
### Datenmodell
Heute liefert das Volt-`with()`:
- `stats.total | published | review | draft`
- `qualityHints[]`
- `recent` (PressRelease-Collection, max. 5)
- `companies`
Mockup verlangt **zusätzlich**:
- `stats.deltaMonth` — Veränderung ggü. Vormonat (Total)
- `profileCompleteness` — Prozentwert für Profil-Progress-Bar
- `billingCompleteness` — Prozentwert für Rechnungsadresse
- `bridgeStatus``['presseecho' => 'connected'|'pending', 'businessportal24' => …]`
(vorerst `connected` fest verdrahtet, später aus echtem Sync-Status)
## Was NICHT geändert wird
- Logik / Volt-Methoden — Layout-only.
- Statistik-Backend (`PressRelease::where(...)`) bleibt 1:1.
- `qualityHints`-Logik bleibt — wird nur anders gerendert.
- Topbar (eigene Phase, später).
---
## Implementierungs-Schritte
### 1. Shared Hub-Components-CSS
Klassen wandern in **`resources/css/shared/hub-components.css`** (neue Datei).
Importiert in:
- `resources/css/portal.css` (nach `flux.css`)
- `resources/css/web/shared-styles.css` (nach `design-tokens.css`)
Damit haben **Portal-Build und Web-Build** dieselbe Hub-Komponenten-Sprache —
DRY für späteres Wiederverwenden (z. B. Admin-Dashboard in Phase 3).
Folgende Klassen kommen rein (alle mit `var(--color-*)`-Tokens, keine Hex-Literale):
- `.panel`, `.panel-warm`, `.panel-dark`, `.panel-head`
- `.stat-card` + `.stat-strip` + `.stat-label` + `.stat-num`
+ Varianten: `.is-primary`, `.is-ok`, `.is-warn`, `.is-muted`
- `.hint-card` + `.hint-card .hint-ico`
- `.badge` + `.badge.warn` + `.badge.ok` + `.badge.hub` + `.badge.dot`
- `.bridge-row`, `.dot-pe`, `.dot-bp`
### 2. Blade-Components in `resources/views/components/portal/`
#### `stat-card.blade.php`
```blade
<x-portal.stat-card variant="primary|ok|warn|muted" label="Gesamt" :value="$stats['total']">
<x-slot:meta>2026</x-slot:meta> {{-- rechts oben --}}
<x-slot:trend>0 ggü. Vormonat</x-slot:trend> {{-- unten --}}
</x-portal.stat-card>
```
Render: `<article class="stat-card is-{variant}">` + Strip + Label + Mono-Zahl
+ Meta-Slot oben rechts + Trend-Slot unten. Sparkline (SVG) erstmal weggelassen
— optionales Detail; lässt sich nachschieben, wenn Daten dafür da sind.
#### `hint-card.blade.php`
```blade
<x-portal.hint-card :icon="$hint['icon']" :title="$hint['title']" :percent="60" :href="$hint['href']">
{{ $hint['description'] }}
<x-slot:action>Profil öffnen</x-slot:action>
</x-portal.hint-card>
```
Render: `.hint-card`-Grid mit Icon-Square + Progress-Bar + Text + Action-Link
in `text-accent-deep`.
#### `bridge-card.blade.php` (optional, Phase 2 minimal)
Bleibt erstmal **inline** im Dashboard (sehr spezifisch, eine Stelle).
Wird in Phase 3/4 extrahiert, wenn klar ist wie weit der Brand-Bridge-Pattern
sich verbreitet.
### 3. `customer/dashboard.blade.php` umbauen
Layout-Skelett (alles im `<div class="space-y-8">`-Container):
1. **Page-Header**`<header>` mit Grid `1fr auto`:
- Links: Hub-Badge `User Backend` + Eyebrow + `<h1>Mein Dashboard</h1>`
+ Subtitle (Name + Reichweiten-Hinweis)
- Rechts: Status-Pille
- Wenn `$selectedCompany`: Hub-Badge mit Firma-Name (grün/ok-Stil)
- Wenn nicht: Warn-Pille „Keine Firma zugeordnet → zuordnen“
2. **KPI-Reihe** — 4 `<x-portal.stat-card>` (Gesamt/Veröffentlicht/Prüfung/Entwürfe)
3. **Zweispalten-Grid** (`2fr 1fr`):
- Links: `<article class="panel">` mit Pressemitteilungen — Liste **oder**
Empty-State (3 Schritt-Karten)
- Rechts: `<article class="panel">` mit `<x-portal.hint-card>`s
4. **Unteres Grid** (`2fr 1fr`):
- Links: `<article class="panel">` Firmen — Liste **oder** Empty-Slot-Karten
- Rechts: `<article class="panel-dark">` Brand-Bridge (inline)
5. **Footer** — kleine Link-Reihe in `text-ink-3`
### 4. Volt `with()` ergänzen
- `stats.deltaMonth` via zweiter Query (Vormonats-Counts vs. Aktuell)
- `profileCompleteness` als simpler Heuristik-Wert (firstname/lastname/phone/etc.)
- `billingCompleteness` analog (Vorhanden = 100, sonst 0; oder Ist-Felder/Soll-Felder)
- `bridgeStatus` vorerst hardcoded `['presseecho' => 'connected', 'businessportal24' => 'connected']`
→ später aus echtem Sync-Service
### 5. Tests
`tests/Feature/Customer/DashboardTest.php` (oder bestehender Test, falls vorhanden):
- Rendert ohne Fehler bei eingeloggtem Customer
- Stat-Zahlen werden korrekt ausgegeben
- Empty-State wird angezeigt, wenn keine PRs existieren
- Quality-Hints werden angezeigt, wenn `profile()` fehlt
---
## Akzeptanzkriterien
- [x] Phase-2-Plan-Dokument geschrieben
- [x] `shared/hub-components.css` existiert, importiert in beiden Builds
- [x] `<x-portal.stat-card>` und `<x-portal.hint-card>` rendern wie Mockup
- [x] `/admin/me` zeigt das neue Dashboard ohne Console-Errors
- [x] Empty-State für Pressemitteilungen ist sichtbar, wenn keine vorhanden
- [x] Quality-Hints rendern mit Progress-Bar
- [x] Brand-Bridge-Dark-Card unten rechts zeigt presseecho + businessportal24
- [x] Neuer Smoke-Test `tests/Feature/Customer/DashboardTest.php` mit 5 Cases
(Core-Sections, Empty-State, PR-Liste, Profil-Hint, vollständiges Profil
blendet Hints aus). Cross-Check: alle 18 verwandten Tests bleiben grün.
- [x] Pint clean
- [x] `PROGRESS.md`-Eintrag
---
## Risiken & Mitigation
- **FluxUI-Klassen vs. Custom-CSS-Kollisionen** — wir mischen `<flux:badge>`
weiterhin **nicht** im Dashboard (für Status-Pillen nehmen wir
`.badge.hub|.warn|.ok|.dot`). Auf den Detail-Seiten (Phase 4) bleibt
FluxUI dominant.
- **Sparklines weggelassen** — minimaler Stilverlust, wird in Phase 4 mit
echten Trend-Daten nachgereicht. Mockup-Match weiterhin ≥ 90 %.
- **`stats.deltaMonth`-Performance** — zweite Query auf gleicher Tabelle;
bei wachsendem Datensatz später cachen. Heute irrelevant.

View file

@ -0,0 +1,159 @@
# Phase 5 — Dark Mode konsistent + Appearance-Switcher im User-Menü
> **Vorzugsphase**: Eigentlich Phase 5, vorgezogen vor Phase 3/4 nach
> User-Wunsch. Grund: Der FluxUI-Appearance-Switcher hat in Phase 1 den
> Dark-Mode-Bug ausgelöst (`#6d8ad3` statt Hub-Blau); statt Symptom-Fix
> wollen wir den vollen Dark Mode jetzt richtig.
**Status**: ✅ abgeschlossen · **Aufwand**: ~½ Tag · **Risiko**: niedrig
---
## Ziele
1. Dark Mode funktioniert sauber auf allen Portal-Seiten (Customer-Dashboard,
Sidebar, FluxUI-Komponenten, Hub-Components).
2. Switcher (Light / Dark / System) **direkt im User-Menü** verfügbar — kein
Umweg über `/settings/appearance` mehr nötig.
3. Hub-Frontend (Landing + Auth) bleibt **bewusst Light-Only** — Hub-Atmosphäre
ist Buchpapier.
4. Der Notfall-Hack aus Phase 1 (`portal.css: .dark { --color-accent: var(--color-hub) }`)
wird durch das echte Dark-Token-Mapping ersetzt.
## Quelle der Wahrheit
`dev/frontend/tailwind_v3/User Dashboard presseportale Dark.html`
liefert alle Dark-Werte. Wir spiegeln die Token-Namen aus dem Light-Mode 1:1.
---
## Was sich ändert
### 1. `shared/design-tokens.css`
Der seit Phase 0 vorbereitete `.dark`-Block wird **aktiviert** und um die seit
Phase 2 nachgezogenen Tokens (`--color-bg-rule-2`, `--color-gain-deep`) ergänzt.
Wichtige Umwidmungen im Dark Mode (gleiche Namen, andere Werte):
| Token | Light | Dark | Bedeutung |
| ----------------------- | ----------- | ----------- | ------------------------------------------ |
| `--color-bg` | `#f6f4ef` | `#0e1218` | Page-Background |
| `--color-bg-elev` | `#fbfaf6` | `#14181f` | Sidebar / leicht hervorgehoben |
| `--color-bg-card` | `#ffffff` | `#181d27` | Card-Body |
| `--color-bg-rule` | `#e2ddd0` | `#2a3142` | Trennlinien |
| `--color-bg-rule-2` | `#ede7d7` | `#232838` | Progress-Track |
| `--color-hub` | `#1a2540` | `#5a78c2` | Primary-Akzent (heller im Dark) |
| `--color-hub-soft` | `#e5e9f1` | `#1f2a47` | Hint-Hintergrund |
| `--color-accent` | `#b07a3a` | `#d9a560` | Bernstein-Akzent (heller im Dark) |
| `--color-accent-warm` | `#b07a3a` | `#b07a3a` | **bleibt gleich** — für Border-Akzente |
| `--color-accent-deep` | `#8a5e27` | `#b07a3a` | Action-Link-Color (heller im Dark) |
| `--color-accent-soft` | `#f1e6d3` | `#2a2418` | Hint-Icon-BG |
| `--color-ink` | `#1a1f1c` | `#ece9e0` | Text-Primary |
| `--color-ink-2` | `#3a413d` | `#c9c5b8` | Text-Secondary |
| `--color-ok / -soft` | `#2e8540 / #e2f1e5` | `#4dc076 / #1a2d22` | Status grün |
`--color-accent-warm` als **konstanter** Bernstein-Token (gleicher Wert in
beiden Modi) ist ein bewusster Trick: Im Portal mapt `--color-accent` auf
`var(--color-hub)` (Primary-Akzent), aber Bernstein-Borders (Hint-Card,
Schritt-Karten-Eyebrow) brauchen weiterhin den ungeänderten Bernstein-Wert.
`--color-accent-warm` ist der Token dafür.
### 2. `portal.css`
- Den `.dark { --color-accent: var(--color-hub); … }`-Notfall-Block aus
Phase 1 entfernen — er ist mit echtem Dark-Mapping überflüssig.
- Stattdessen einen kurzen Kommentar setzen, dass das Token-Mapping aus
`design-tokens.css` greift.
- Primary-Button-Hover-Override: heute hartcodiert auf `--color-hub-2`
(`#243152`). Im Dark Mode ist `--color-hub-2` = `#6d8ad3` (heller),
was richtig ist (Hover = noch heller als Default-Button). Override bleibt
via Variable korrekt, kein Eingriff nötig.
- Button-Shadows: `rgba(26, 37, 64, …)` ist Light-Mode-spezifisch. Im Dark
Mode wirkt der bläuliche Shadow auf dunklem Card-BG falsch. Lösung:
per `@media (prefers-color-scheme: dark)` ODER `.dark`-Selektor die
Shadow-Farben auf transparenten Schwarz tönen.
### 3. `shared/hub-components.css`
- Bernstein-Stellen umstellen: `var(--color-accent)`
`var(--color-accent-warm)` für **Hint-Card-Border-Left** und
**Progress-Bar-Fill** (sonst wird's im Portal Hub-Blau gemapt).
- Wegen Tokens-Architektur (`hub-soft` etc. werden im Dark Mode dunkler)
funktioniert der Großteil **automatisch** — keine `.dark`-Overrides nötig.
- Eine Ausnahme: `.panel-dark` und die Brand-Bridge-Boxes — im Light Mode
ist `panel-dark` Hub-Blau (#1a2540). Im Dark Mode bleibt das auch
`var(--color-hub)` — und da ist `--color-hub` heller (#5a78c2). Wir
brauchen also einen **konstanten dunklen Token** für `panel-dark` — sonst
wird die „dunkle“ Bridge-Card im Dark Mode plötzlich hellblau.
→ Neuer Token `--color-panel-dark` (immer #0f1729, in beiden Modi)
oder direkt `var(--color-topbar-deep)` (existiert bereits).
### 4. User-Menü-Switcher
Beide Dropdowns (Desktop in `sidebar.blade.php`, Mobile in derselben Datei)
bekommen vor dem Logout einen Block:
```blade
<flux:menu.separator />
<div class="px-2 py-1.5">
<flux:radio.group x-data variant="segmented" x-model="$flux.appearance" size="sm">
<flux:radio value="light" icon="sun" />
<flux:radio value="dark" icon="moon" />
<flux:radio value="system" icon="computer-desktop" />
</flux:radio.group>
</div>
```
Das ist exakt das Pattern aus `livewire/settings/appearance.blade.php`, nur
kompakt mit Icons-only. `$flux.appearance` ist FluxUI's Magic-Object,
LocalStorage-persistent, von `@fluxAppearance` injiziert.
### 5. Hub-Auth bleibt Light
`auth/pressekonto.blade.php` lädt **kein** `@fluxAppearance` und **kein**
`partials/head` — die Hub-Auth-Pipeline ist eigenständig. Damit wird `class="dark"`
dort nie gesetzt, selbst wenn das LocalStorage `dark` enthält. Hub-Auth bleibt
also automatisch Light. **Kein Eingriff nötig.**
---
## Implementierungs-Schritte (Reihenfolge)
1. `design-tokens.css`: Dark-Block aktivieren, `--color-accent-warm` definieren,
`--color-panel-dark` einführen (oder Re-Use `--color-topbar-deep`).
2. `portal.css`: Hack rausnehmen, Button-Shadow für Dark Mode tönen.
3. `hub-components.css`: Bernstein-Stellen auf `--color-accent-warm`,
`.panel-dark` auf `--color-panel-dark`.
4. `sidebar.blade.php`: Switcher in beide Dropdowns einbauen.
5. Build + Smoke-Test im Browser.
6. Bestehende Tests laufen lassen (sollten alle weiter grün sein, weil
keine Test-Assertion farbbezogen ist).
7. Pint + PROGRESS + Plan-Status.
## Akzeptanzkriterien
- [x] Plan-Dokument geschrieben
- [x] Switcher Light/Dark/System im User-Menü (Desktop + Mobile) sichtbar,
Änderung wirkt sofort + persistent (LocalStorage)
- [x] Dark Mode: Customer-Dashboard rendert mit allen Sektionen korrekt
(Tokens schalten um, kein hellblauer Akzent-Bug)
- [x] Brand-Bridge-Karte bleibt im Dark Mode dunkel (`--color-panel-dark-2`
konstant in beiden Modi)
- [x] Hub-Auth bleibt im Dark Mode Light (lädt kein `@fluxAppearance`)
- [x] Tests grün (18/18)
- [x] Pint clean
- [x] PROGRESS-Eintrag
## Risiken & Mitigation
- **FluxUI-Default-Komponenten im Dark Mode** (z.B. Modals, Dropdowns):
unsere Zinc→Hub-Mapping in `portal.css` bridgt die Skala. Im Dark Mode
müssen die Zinc-Werte auch umgekippt werden — passiert automatisch über
die Token-Vars, weil wir `var(--color-bg-*)` nutzen. Restrisiko: einzelne
FluxUI-Komponenten könnten harte Tailwind-Klassen wie `bg-white` haben,
die im Dark unverändert weiß bleiben. Smoke-Test deckt das auf.
- **Hub-Components-Schatten** sind in Hub-Blau-Alpha definiert
(`rgba(26, 37, 64, …)`). Im Dark Mode auf dunklem Bg wirken sie evtl.
„falsch warm“. Akzeptabel im ersten Wurf; Feintuning per `prefers-color-scheme`
Media-Query falls visuell stört.

View file

@ -0,0 +1,55 @@
# Phase 3 — Admin-Dashboard im Hub-Vokabular
**Status**: ✅ abgeschlossen · **Aufwand**: ~¼ Tag · **Risiko**: niedrig
---
## Ziel
`resources/views/admin/dashboard.blade.php` nutzt dieselbe Designsprache wie
das Customer-Dashboard (Phase 2). Keine neuen Komponenten, keine neue Logik
— nur Vokabular-Umbau auf die DRY-Schicht.
## Was sich ändert
### Layout
- Page-Header: Hub-Badge „Admin Backend" + Eyebrow + großes H1
„Admin Dashboard" + Subtitle. Rechts: Status-Pille „Alle Systeme operational"
(ok-Style mit Dot).
- KPI-Reihe: weiter **5 Stat-Cards** (wie heute), aber als
`<x-portal.stat-card>` mit Strip-Variante:
- Pressemitteilungen → `primary` (mit Sub: `X pub · Y prüf · Z entwurf`)
- In Prüfung → `warn` (eigene KPI, war vorher in der PM-Card versteckt)
- Firmen → `muted`
- Kontakte → `muted`
- Benutzer → `muted`
- 2-Spalten-Grid (`2fr 1fr`) — wie heute:
- Links: Panel „Letzte Pressemitteilungen" (Liste + Status-Badges
in `.badge.ok|warn|err|hub`).
- Rechts: Panel „Zur Prüfung" mit warn-Pille (Count) + Liste +
„+ N weitere"-Link.
- Bottom-Reihe (`1fr 2fr`) — **neu**:
- Links: `panel-warm` Newsletter-Block (Mono-Zahl + Subline).
- Rechts: `panel` Quick-Actions mit Section-Eyebrow + Schnellzugriff
auf Invoices/Payments/Coupons/Presets.
- Footer: subtle Link-Reihe analog Customer-Dashboard.
### Was NICHT geändert wird
- Controller-Logik / Datenform.
- Newsletter-Count bleibt erhalten, wandert nur in einen eigenen Block.
- Bestehende Tests (`DashboardTest`) bleiben grün — alle geprüften
Strings (`3`, `1 pub`, `1 prüf`, `1 entwurf`, `Review Dashboard PM`)
bleiben im Output.
## Akzeptanzkriterien
- [x] Plan geschrieben
- [x] Admin-Dashboard verwendet `<x-portal.stat-card>`, `.panel`,
`.section-eyebrow`, `.badge`
- [x] Customer-Dashboard und Admin-Dashboard sind visuell aus einem Guss
- [x] Dark Mode greift automatisch (alle Tokens)
- [x] `DashboardTest` bleibt grün ohne Anpassung (alle 3 Cases +
Wortlaut „1 pub / 1 prüf / 1 entwurf / Review Dashboard PM")
- [x] Pint clean, PROGRESS-Eintrag

View file

@ -0,0 +1,58 @@
# Phase 4A — Press-Releases Listen-Pages
> Erstes Päckchen aus Phase 4 (Listen/Detail-Pages iterativ). Wegen
> Umfang teilen wir Phase 4 in Päckchen — A = Listen, B = Detail/Show,
> C = Forms (create/edit), D = Companies, E = Settings/Profile, F = Rest.
**Status**: ✅ abgeschlossen · **Aufwand**: ~¼ Tag · **Risiko**: niedrig
---
## Scope
In diesem Päckchen NUR:
- `resources/views/livewire/admin/press-releases/index.blade.php`
- `resources/views/livewire/customer/press-releases/index.blade.php`
NICHT in diesem Päckchen:
- `show.blade.php`, `create.blade.php`, `edit.blade.php` (Päckchen B/C)
- Companies, Settings, Profile (Päckchen D/E)
## Ziel
Beide Listen nutzen das Hub-Vokabular:
- **Page-Header** wie im Dashboard (Hub-Badge + Eyebrow + H1 + Subtitle,
CTA rechts).
- **KPI-Reihe** (nur Admin) als `<x-portal.stat-card>` — exakt
wie auf dem Admin-Dashboard (DRY: vier Cards mit primary/ok/warn/muted).
- **Filter-Bar** als `.panel` mit `.panel-head` „Filter & Suche".
- **Tabelle** in `.panel` (kein FluxUI-Card-Wrapper mehr). FluxUI-Table
bleibt — wir bridgen über Zinc-→Hub-Mapping.
- **Status-Badges** auf eigene `.badge.ok|warn|err|hub`-Pillen
(statt `<flux:badge color="…">`) — visuelle Konsistenz zum Dashboard.
- **Empty-State** in Hub-Stil mit Icon-Box.
## Was NICHT angefasst wird
- **Confirm-Modals** für Publish/Reject/Archive — Tests assertieren
Wortlaut („Pressemitteilung veröffentlichen?" etc.).
- **Volt-Logik** (Sort, Filter, Methods) — Layout-only.
- **FluxUI-Form-Inputs** (Search, Select, Combobox) — bleiben, weil
Hub kein eigenes Combobox-Pendant hat.
## Akzeptanzkriterien
- [x] Plan
- [x] Admin-Liste: Page-Header + 4 Stat-Cards + Filter-Panel + Tabelle-Panel + Hub-Badges
- [x] Customer-Liste: Page-Header + Filter-Panel + Tabelle-Panel + Hub-Badges + Empty-State
- [x] Visuell aus einem Guss mit Dashboard
- [x] `PressReleaseWorkflowTest` + `AdminPressReleaseActionsTest` bleiben grün
- [x] Build + Pint + PROGRESS
## Bonus-Fix
- `customer/dashboard.blade.php` Subtitle ergänzt um „Übersicht für {Firma}",
wenn `$selectedCompany` gesetzt ist. Korrigiert eine Phase-2-Regression
bei `CustomerCompanyContextTest > customer dashboard is filtered by …`.

View file

@ -0,0 +1,56 @@
# Phase 4B — Press-Releases Detail/Show-Pages
> Zweites Päckchen aus Phase 4. Folgt auf 4A (Listen).
**Status**: ✅ abgeschlossen · **Aufwand**: ~⅓ Tag · **Risiko**: niedrig
---
## Scope
- `resources/views/livewire/admin/press-releases/show.blade.php`
- `resources/views/livewire/customer/press-releases/show.blade.php`
NICHT in diesem Päckchen:
- `create.blade.php`, `edit.blade.php` (Päckchen 4C — Forms)
- Companies, Settings, Profile (Päckchen 4D/4E)
## Ziel
Beide Detail-Pages im Hub-Vokabular:
- **Page-Header** wie auf den Listen (Hub-Badge + Eyebrow + H1 +
Subtitle), Status-Pill direkt unter dem Eyebrow oder im Header-Meta,
Aktions-Buttons (Bearbeiten / Zurück / Vorschau-Link) rechts.
- **Status-Workflow-Aktionsbar** als `.panel` mit klarer Optik
je nach Status (review = warn, published = ok, draft = neutral).
- **Content-Hauptbereich** (PM-Text) als `.panel` (kein FluxUI-Card-Wrapper).
- **Sidebar / Side-Cards** als kleine `.panel` mit `panel-head`.
- **Status-Verlauf-Timeline** als `.panel` mit Hub-Badges
(`.badge.ok|warn|err|hub`).
- **Rejection-Hinweis** (Customer) als Hub-Style-Error-Panel mit
Linker Akzent-Border (statt `<flux:callout>`).
- **Share-Link-Erfolgsbox** (Customer) als Hub-Style-Success-Block.
- **Pressekontakte-Liste** (Customer) als Hub-Items in `.panel`.
## Was explizit NICHT angefasst wird
- **Confirm-Modals** (Publish / Reject / Archive) — Tests in
`AdminPressReleaseActionsTest` assertieren Wortlaute.
- **Wortlaute** `Werbliche Sprache wurde markiert.` und
`Erneut einreichen` aus `PressReleaseWorkflowTest`.
- **Volt-Logik** (publish/reject/archive/submitForReview/
generateShareLink/with) — Layout-only.
## Akzeptanzkriterien
- [x] Plan
- [x] Admin-Show: Page-Header + Status-Workflow-Bar + Text-Panel +
Sidebar-Panels + Timeline + Hub-Badges. Modals unverändert.
- [x] Customer-Show: Page-Header + Status-Workflow-Bar +
Rejection-Panel + Share-Erfolgsblock + Contacts-Panel +
Verlauf-Panel + Text-Panel + Hub-Badges. „Erneut einreichen" +
„Werbliche Sprache wurde markiert." Wortlaute bleiben.
- [x] `PressReleaseWorkflowTest` + `AdminPressReleaseActionsTest`
bleiben grün (16 passed, 52 assertions).
- [x] Build + Pint + PROGRESS.

View file

@ -0,0 +1,69 @@
# Phase 4C — Press-Releases Forms (create / edit)
> Drittes Päckchen aus Phase 4. Folgt auf 4A (Listen) und 4B (Show).
**Status**: ✅ abgeschlossen · **Aufwand**: ~⅓–½ Tag · **Risiko**: niedrig
---
## Scope
- `resources/views/livewire/admin/press-releases/create.blade.php`
- `resources/views/livewire/admin/press-releases/edit.blade.php`
- `resources/views/livewire/customer/press-releases/create.blade.php`
- `resources/views/livewire/customer/press-releases/edit.blade.php`
NICHT in diesem Päckchen:
- Companies (Päckchen 4D)
- Settings / Profile (Päckchen 4E)
- Restliche Admin-Bereiche (Päckchen 4F)
## Ziel
Alle vier Forms im Hub-Vokabular:
- **Page-Header** wie auf Listen und Detail-Pages: Hub-Badge +
Eyebrow + H1 + Subtitle. Bei Edit zusätzlich Status-Pille im
Header-Meta. Aktions-Buttons rechts.
- **Form-Sektionen** als `.panel` mit `.panel-head` und
`section-eyebrow` ("Inhalt", "SEO & Links", "Metadaten",
"Status-Aktionen", "Aktionen").
- **Form-Felder** bleiben FluxUI (`<flux:field>`, `<flux:label>`,
`<flux:input>`, `<flux:textarea>`, `<flux:select>` inkl.
Combobox-Variant, `<flux:checkbox>`, `<flux:error>`) — das
Token-Bridging aus Phase 1 zieht. Required-Marker werden auf
Hub-Error-Token umgestellt.
- **Action-Buttons** ("Speichern", "Zur Prüfung einreichen",
"Als Entwurf speichern", "Status wechseln") in einem `.panel`
mit `.panel-head` "Aktionen".
- **Flash-Boxen** auf Hub-Token-Pillen.
## Was explizit NICHT angefasst wird
- **Volt-Logik** (`save`, `submitForReview`, `publish`, `reject`,
`backToDraft`, `archive`, `changeStatus`, `deletePressRelease`,
`mount`, `with`, `selectedCompany`) — Layout-only.
- **Confirm-Modals** der Admin-Edit-Page (`confirm-status-change`,
`confirm-delete-press-release`) — Tests assertieren Wortlaute.
- **Wortlaute** aus `AdminPressReleaseActionsTest`:
`Neuer Status`, `Status wechseln`, `Status wirklich wechseln?`,
`Pressemitteilung löschen?`, `Status wurde auf`.
- **Image-Manager-Komponente** (`<livewire:components.press-release-images-manager>`).
- **Wortlaute** für Customer-Create-Page: `Beta GmbH` (Firma
aus Faktur via `companyId`) — Combobox/Select bleibt funktional.
## Akzeptanzkriterien
- [x] Plan
- [x] Admin-Edit: Page-Header + 5 Panels + Hub-Status-Badge,
beide Modals unverändert, alle Wortlaut-Assertions grün.
- [x] Admin-Create: Page-Header + 3 Panels (Inhalt, SEO, Metadaten)
+ Aktions-Panel.
- [x] Customer-Create: Page-Header + 2 Panels (Inhalt, Metadaten)
+ Aktions-Panel.
- [x] Customer-Edit: Page-Header + 2 Panels (Inhalt mit Image-Manager,
Metadaten) + Aktions-Panel.
- [x] `AdminPressReleaseActionsTest`,
`CustomerCompanyContextTest > customer press releases create …`,
`CustomerProfileSecurityTest` bleiben grün (72 Tests, 310 assertions).
- [x] Build + Pint + PROGRESS.

View file

@ -0,0 +1,812 @@
# Fortschritts-Log — Hub × FluxUI
> Tagebuch der Umsetzung. Neue Einträge **oben** anfügen.
> Format pro Eintrag: Datum · Phase · Was wurde gemacht · Wer · Notizen
---
## 2026-05-19 · Phase 4C · Press-Releases Forms (create / edit)
- **Anlass**: User-Freigabe „ja" nach 4B. Drittes Päckchen aus
Phase 4 — Forms (create + edit) für Admin und Customer.
- **Plan-Dokument**: `09-PHASE-4C-PRESS-RELEASES-FORMS.md`.
- **Was umgebaut wurde (alle 4 Forms)**:
- **Page-Header** wie auf Listen und Detail: Hub-Badge
(„Admin Backend" / „User Backend") + Eyebrow + großes H1 +
Subtitle. Bei Edit zusätzlich Status-Pille im Header-Meta
(`@class([])`-Syntax mit `ok|warn|err|hub`) und „ID"-Pill.
Aktions-Buttons rechts (Zurück).
- **Form-Sektionen** als `.panel` mit `.panel-head` und
`section-eyebrow`: „Inhalt", „SEO & Links", „Metadaten",
„Status-Aktionen" (Admin-Edit), „Aktionen". Innenraum
immer `p-5 space-y-4`.
- **Form-Felder** bleiben FluxUI (`<flux:field>`,
`<flux:label>`, `<flux:input>`, `<flux:textarea>`,
`<flux:select>` inkl. Combobox-Variant, `<flux:checkbox>`,
`<flux:error>`) — das Token-Bridging aus Phase 1 zieht.
- **Required-Marker** von `text-red-500` auf Token-Farbe
(`text-[color:var(--color-err)]`) umgestellt.
- **Save-/Submit-Buttons** in eigenem `.panel` mit Header
„Aktionen" statt „nackten" Buttons in der Sidebar.
- **Flash-Boxen** (Success/Error) auf Hub-Token-Pillen.
- **Admin-Edit-spezifisch**: 5 Sidebar-Panels (Status-Aktionen +
Metadaten + Aktionen + Modals). „Status-Aktionen"-Panel zeigt
rechts im Header die aktuelle Status-Pille — visuelle Doppelung
mit dem Page-Header-Status, aber im Kontext der Sidebar
sinnvoll als Vergleichs-Anker für die „Neuer Status"-Auswahl.
- **Was explizit NICHT angefasst wurde**:
- **Volt-Logik** (alle `save`/`publish`/`reject`/`backToDraft`/
`archive`/`changeStatus`/`deletePressRelease`/`mount`/`with`/
`selectedCompany`-Methoden) — Layout-only.
- **Confirm-Modals** der Admin-Edit-Page
(`confirm-status-change`, `confirm-delete-press-release`) —
Tests in `AdminPressReleaseActionsTest` assertieren Wortlaute.
- **Wortlaute**: „Neuer Status", „Status wechseln",
„Status wirklich wechseln?", „Pressemitteilung löschen?",
„Status wurde auf" — alle erhalten.
- **`<livewire:components.press-release-images-manager>`** —
eigene Komponente, kommt im jeweiligen eigenen Päckchen dran.
- **Build/Test**:
- `npm run build:portal` → ok (`portal-D0cNdOWP.css` 418.42 kB).
- Linter clean (alle 4 Dateien).
- `php artisan test --compact --filter='PressRelease|CustomerCompanyContext|CustomerProfileSecurity|PanelConsolidation'`
**72 passed (310 assertions)**.
- Volle Suite: 230 passed, 3 skipped, 1 pre-existing failure
(`ApiDocumentationTest`).
- Pint `--dirty --format agent` → passed.
- **Offene Fragen**: Keine.
- **Nächster Schritt**: Phase 4 für Press-Releases ist mit 4A/4B/4C
komplett abgeschlossen. Nächstes Päckchen je nach Sichtbarkeit:
**4D = Companies** (`admin.companies.*`) ODER
**4E = Settings / Profile** (`me.profile`, `me.security`).
---
## 2026-05-19 · Phase 4B · Press-Releases Detail/Show-Pages
- **Anlass**: User-Freigabe „weiter" nach 4A. Zweites Päckchen aus
Phase 4 — Detail/Show-Pages für Admin und Customer.
- **Plan-Dokument**: `08-PHASE-4B-PRESS-RELEASES-DETAIL.md`.
- **Was umgebaut wurde (beide Show-Pages)**:
- **Page-Header** mit Hub-Badge + Eyebrow + Status-Pill +
Sprache + Portal (Admin nur), darunter großer H1 mit dem
PM-Titel und Subtitle mit Firma/Kategorie/Autor bzw. Datum.
Aktions-Buttons rechts (Bearbeiten/Vorschau-Link/Zurück).
- **Status-Workflow-Aktionsbar** als `.panel` mit `.panel-head`
und passend gefärbtem Badge je nach Status (warn=Review,
ok=Published, err=Rejected, hub=Draft).
- **Content-Hauptbereich** (PM-Text) als `.panel` mit eigenem
Header „Inhalt" + `prose` darunter. Keywords/Backlink darunter
als Footer mit Hub-Rule.
- **Sidebar / Side-Cards** (Admin) als kompakte `.panel` mit
`panel-head` „Details" und „Bilder" + Hub-Badges.
- **Status-Verlauf-Timeline** als `.panel` mit Hub-Badge je
Log-Eintrag (`.badge.ok|warn|err|hub`) statt FluxUI-`color`-Props.
- **Status-Badges** in Header und Timelines komplett auf
`<span class="badge …">` mit `@class([])`-Syntax.
- **Customer-Show-spezifisch**:
- **Rejection-Hinweis** als roter `.panel` mit linker
Akzent-Border (`border-left-width:3px`) und Icon-Box —
statt `<flux:callout color="red">`.
- **Review-Pending-Hinweis** als warner `.panel` mit
Akzent-Border und Clock-Icon-Box — statt `<flux:callout color="yellow">`.
- **Share-Link-Erfolgsblock** als ok-`.panel` mit
Gültigkeits-Badge im Header und readonly-Input im Body.
- **Contacts-Liste** als Hub-Items (`bg-bg-elev` +
`border-bg-rule`), Empty-State mit gestrichelter Border.
- **Status-Tile-Grid** (Aktuell/Erstellt/Veröffentlicht/Aufrufe)
als 4-er-Grid mit kleinen Hub-Tiles statt zinc-Hintergründen.
- **Admin-Show-spezifisch**:
- **Modals** (Publish/Reject/Archive) komplett unverändert —
Tests in `AdminPressReleaseActionsTest` assertieren Wortlaute.
- **Was explizit NICHT angefasst wurde**:
- **Volt-Logik** (publish/reject/archive/submitForReview/
generateShareLink/with-Method) — Layout-only.
- **Wortlaute** „Erneut einreichen", „Werbliche Sprache wurde
markiert." aus `PressReleaseWorkflowTest`.
- **Bestehende Modals** und ihre Wortlaute.
- **Mini-Stolperer (sofort gefixt)**:
- Erst zwei nicht-existente Tokens (`--color-rule`,
`--color-panel-soft`) verwendet. Korrigiert zu
`--color-bg-rule` und `--color-bg-elev` (16 + 5 Vorkommen).
Wäre im Browser stumm gefailed (Tailwind hätte die
Klassen einfach nicht ausgegeben).
- **Build/Test**:
- `npm run build:portal` → ok (`portal-Bq4pkLWt.css` 418.36 kB).
- Linter clean.
- `php artisan test --compact --filter='PressReleaseWorkflow|AdminPressReleaseActions'`
→ 16 passed (52 assertions).
- Volle Suite: 230 passed, 3 skipped, 1 pre-existing failure
(`ApiDocumentationTest`, schon vor 4A bekannt).
- Pint `--dirty --format agent` → passed.
- **Offene Fragen**: Keine.
- **Nächster Schritt**: Päckchen **4C** = Press-Releases Forms
(`create.blade.php` + `edit.blade.php`, Admin + Customer).
Forms sind tendenziell aufwendiger (mehr FluxUI-Felder, ggf.
zusätzliche Logik wie Image-Uploads).
---
## 2026-05-19 · Phase 4A · Press-Releases Listen-Pages
- **Anlass**: User-Freigabe „weiter" nach Phase 3 (Admin-Dashboard).
Phase 4 ist laut Roadmap „der Marathon" über Listen-/Detail-Pages
(~35 Tage iterativ). Wegen Umfang in Päckchen geteilt — A = Listen,
B = Detail/Show, C = Forms, D = Companies, E = Settings, F = Rest.
- **Plan-Dokument**: `07-PHASE-4A-PRESS-RELEASES-LISTEN.md` (knapp,
Scope hart auf zwei `index.blade.php`-Files begrenzt, mit explizitem
„NICHT in diesem Päckchen"-Block).
- **Scope dieses Päckchens**:
- `resources/views/livewire/admin/press-releases/index.blade.php`
- `resources/views/livewire/customer/press-releases/index.blade.php`
- **Was umgebaut wurde (beide Listen)**:
- **Page-Header** im Hub-Stil: `<header>` mit `1fr auto`-Grid,
Hub-Badge („Admin Backend" / „User Backend"), Eyebrow, großes H1,
Subtitle, rechts der CTA-Primary-Button (FluxUI bleibt für den Button).
- **Filter-Bar** als `.panel` + `.panel-head` mit `section-eyebrow`
„Filter & Suche". FluxUI-Inputs (Search-Input, Selects, Combobox
für User/Company/Contact-Lookup) bleiben unverändert — Hub hat
kein eigenes Combobox-Pendant.
- **Tabelle** als `.panel` mit `.panel-head` „Alle Pressemitteilungen"
+ Eintrags-Counter rechts. FluxUI-`<flux:table>` bleibt — das
Zinc-→Hub-Mapping aus Phase 1 zieht hier perfekt.
- **Status-Badges**: `<flux:badge color="green|yellow|red|zinc|blue">`
ersetzt durch `<span class="badge ok|warn|err|hub">` für visuelle
Konsistenz mit dem Dashboard (`@class([...])`-Syntax).
- **Empty-State** mit Icon-Box (`bg-[color:var(--color-hub-soft)]`,
Hub-Border, `flux:icon.newspaper`) + Headline + Subtext.
- **Flash-Boxen** auf Hub-Tokens (`--color-ok-soft`, `--color-err-soft`,
`--color-gain-deep`, `--color-loss`) statt `bg-green-50` etc.
- **Admin-spezifisch**: Vier `<x-portal.stat-card>` (Gesamt/Veröffentlicht/
In Prüfung/Entwürfe) als KPI-Reihe — identisches Layout wie Admin-Dashboard.
- **Was explizit NICHT angefasst wurde**:
- **Confirm-Modals** (Publish/Reject/Archive) — Tests in
`AdminPressReleaseActionsTest` assertieren die Wortlaute
(„Pressemitteilung veröffentlichen?" etc.) → unverändert.
- **Volt-Logik** (Sort, Filter, alle Methods) — Layout-only.
- **Bonus-Fix**: `customer/dashboard.blade.php` Subtitle bekommt
einen „Übersicht für :company —"-Einschub, wenn `$selectedCompany`
gesetzt ist. Korrigiert eine Phase-2-Regression bei
`CustomerCompanyContextTest > customer dashboard is filtered by …`.
- **Build/Test**:
- `npm run build:portal` → ok (`portal-DaL-tXm-.css` 418.75 kB).
- Linter clean.
- `php artisan test --compact --filter='PressReleaseWorkflow|AdminPressReleaseActions|Dashboard|CustomerCompanyContext'`
→ 45 passed (163 assertions).
- Volle Suite: 230 passed, 3 skipped, 1 failed.
Der einzelne Fail (`ApiDocumentationTest`) ist **pre-existing**
via `git stash` verifiziert, war auch vor diesem Päckchen rot
(`/docs/api/v1` liefert nicht das erwartete YAML).
- Pint `--dirty --format agent` → passed.
- **Offene Fragen**: Keine.
- **Nächster Schritt**: Päckchen **4B** = Detail/Show-Pages
(`admin/press-releases/show.blade.php` +
`customer/press-releases/show.blade.php`). Dort ist mit der
„Werbliche Sprache wurde markiert."- und „Erneut einreichen"-
Assertion aus `PressReleaseWorkflowTest` zu rechnen — Wortlaute
bleiben unverändert.
---
## 2026-05-19 · Phase 3 · Admin-Dashboard im Hub-Vokabular
- **Anlass**: User-Freigabe „weiter" nach Phase 5 (Dark Mode). Phase 3
laut Roadmap: Admin-Dashboard (`/dashboard`) im selben Vokabular wie
Customer-Dashboard.
- **Plan-Dokument**: `06-PHASE-3-ADMIN-DASHBOARD.md` (knapper als
Phase 2 — niedrig-risiko, keine neuen Konzepte, reine
Vokabular-Migration auf die DRY-Schicht aus Phase 2).
- **Ausgangslage**: `admin/dashboard.blade.php` war Controller-rendered
(kein Volt), nutzte reines Tailwind mit `zinc-*`-Klassen, harten
Farb-Pillen (`bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 …`)
und keiner FluxUI-Komponente. Visuell aus der Zeit gefallen.
- **`admin/dashboard.blade.php` komplett umgeschrieben**:
- **Page-Header**: Hub-Badge „Admin Backend" + Eyebrow + großes H1
+ Subtitle mit `auth()->user()->name`. Rechts: ok-Pille
„Alle Systeme operational".
- **KPI-Reihe** (5 Stat-Cards via `<x-portal.stat-card>`):
- Pressemitteilungen → `primary` mit Trend-Slot
„X pub · Y prüf · Z entwurf" (Wortlaut für Test-Assertions
bewusst beibehalten).
- In Prüfung → `warn` als eigene KPI (war vorher nur in der PM-Card
versteckt — jetzt klickbar direkt in den Filter).
- Firmen / Kontakte / Benutzer → `muted` mit kurzen Sublines.
- Alle Cards sind klickbar (link-wrapped) → CRM-/Content-Übersichten.
- **2-Spalten-Grid** (`2fr 1fr`):
- Links: Panel „Letzte Pressemitteilungen" mit Hub-Liste +
`.badge.ok|warn|err|hub`-Pillen (statt Tailwind-Farben).
- Rechts: Panel „Zur Prüfung" mit warn-Pille (Count) oder
ok-Pille „leer". „+ N weitere"-Link im Footer.
- **Newsletter + Quick-Actions** (`1fr 2fr`, NEU):
- Links: `panel-warm` Newsletter-Block mit Mono-Zahl
+ Subline + Sync-Link.
- Rechts: Quick-Action-Grid mit 4 Icon-Buttons (Pressemitteilungen,
Firmen, Rechnungen, Voreinstellungen) — Icon-Tile wechselt auf
Hover ins gefüllte Hub-Blau.
- **Footer**: subtle Link-Reihe (Benutzer / Rollen / Performance).
- **Was bewusst NICHT geändert**:
- Controller-Logik, Datenform, Cache-Strategie (`AdminPerformanceCache`).
- Newsletter-Count bleibt, wandert nur in eigenen Block.
- **Build/Verifikation**:
- `npm run build:portal``portal: 417.81 kB` (Δ -0.7 kB,
weil viele Zinc-Tailwind-Klassen weggefallen sind).
- `php artisan test --filter='DashboardTest|AuthenticationTest|RegistrationTest|CustomerPortalTest'`
**18/18 passed**, 70 Assertions. `DashboardTest` mit seinen
Wortlaut-Assertions (`3`, `1 pub`, `1 prüf`, `1 entwurf`,
`Review Dashboard PM`) bleibt grün ohne Anpassung.
- `vendor/bin/pint --dirty``passed`.
- Linter: 0 Errors.
- **Lessons learned**:
- Vokabular-Migrations ohne Logik-Eingriff brauchen ~15 Minuten dank
DRY-Schicht aus Phase 2 — der Wert der `shared/hub-components.css`
+ `<x-portal.stat-card>`-Investition zahlt sich ab Phase 3 deutlich aus.
- Der `<x-portal.stat-card>` ist auch ohne `:value`-Number-Format
flexibel (`number_format()` direkt im Aufruf eingebettet).
- Test-Assertions können bei Layout-Refactors überraschend stabil
bleiben, wenn man die Original-Strings im neuen Layout an
sinnvollen Stellen behält (hier: im Trend-Slot der KPI-Card).
---
## 2026-05-19 · Phase 5 · Dark Mode + Switcher im User-Menü
- **Anlass**: User-Feedback nach Phase 2: „Switch in den Dark Mode funktioniert
nicht. Zusätzlich hätte ich gerne einen Switcher hell/dunkel direkt im
User-Menü." → Phase 5 vorgezogen vor Phase 3/4, weil der Switcher der
natürliche UX-Einstiegspunkt für Dark Mode ist und der Notfall-Hack
aus Phase 1 (`portal.css: .dark { --color-accent: var(--color-hub) }`)
endlich verschwinden soll.
- **Plan-Dokument**: `05-PHASE-5-DARK-MODE.md` mit Token-Mapping-Tabelle,
drei wichtigen Token-Konstanten-Tricks (`--color-accent-warm`,
`--color-panel-dark`, `--color-panel-dark-2`) und Switcher-Snippet.
- **`design-tokens.css` — Dark-Block aktiviert**:
- Vollständiger `.dark { … }`-Block mit Werten aus
`User Dashboard presseportale Dark.html`.
- Surfaces: bg/elev/card/rule schalten auf `#0e1218`-Familie.
- Hub-Blau wird **heller** im Dark Mode (`#5a78c2` statt `#1a2540`) —
notwendig für Lesbarkeit auf dunklem Bg.
- Bernstein-Akzent ebenfalls heller (`#d9a560`).
- Status-Farben (`ok/warn/err`) auf dunkle, gesättigte Variante.
- Schatten-Tokens neutral-schwarz statt hub-blau-warm.
- `color-scheme: dark;` als Hint für native Form-Controls.
- **3 neue Token-Konstanten** (gleicher Wert in beiden Modi):
- `--color-accent-warm` (`#b07a3a`) — für Stellen, die explizit Bernstein
bleiben müssen (Hint-Card-Border-Left, Progress-Bar-Fill); löst die
Kollision auf, dass `--color-accent` im Portal auf Hub-Blau gemapt
ist und im Dark Mode noch heller würde.
- `--color-panel-dark` (`#0f1729`) und `--color-panel-dark-2` (`#1a2540`)
— für `.panel-dark` und Brand-Bridge-Inner-Boxes. Ohne diese würde
der dunkle Bridge-Container im Dark Mode plötzlich hellblau, weil
`var(--color-hub)` zum hellen Wert wird.
- **`portal.css` — Notfall-Hack aus Phase 1 entfernt**:
- Der `.dark { --color-accent: var(--color-hub); … }`-Block ist
überflüssig, weil das echte Dark-Token-Mapping in `design-tokens.css`
`--color-hub` automatisch auf `#5a78c2` schaltet — und
`--color-accent` darüber per `var(--color-hub)`-Verweis dynamisch
mitkommt.
- Primary-Button-Shadows zusätzlich für `.dark` überschrieben: statt
`rgba(26, 37, 64, …)` (warmer Hub-Blau-Alpha) jetzt `rgba(0, 0, 0, …)`
(neutral-schwarz), weil der hub-blaue Schatten auf dunklem Card-BG
zu sichtbar wirkt.
- **`hub-components.css` — Dark-tauglich gemacht**:
- `.panel-dark` nutzt jetzt `var(--color-panel-dark-2)` und
`var(--color-panel-dark)` (vorher `var(--color-hub)` und
`var(--color-topbar-deep)`) → bleibt im Dark Mode dunkel.
- `.hint-card border-left` und `.hint-bar > span` nutzen
`var(--color-accent-warm)` (konstant Bernstein) statt `var(--color-accent)`
(im Portal Hub-Blau-Mapping).
- `.hint-card background` schaltet auf `var(--color-bg-card-warm)`
ist im Light Mode `#efeadc` (warmes Buchpapier), im Dark Mode
`#1f1a12` (warm-dunkles Bernstein-Substrat).
- Restliche Klassen funktionieren **automatisch**, weil alle Werte
über Tokens laufen — DRY-Architektur zahlt sich aus.
- **`customer/dashboard.blade.php`**:
- Brand-Bridge-Inner-Boxes auf `bg-[color:var(--color-panel-dark-2)]`
umgestellt (statt `--color-hub-2`, das im Dark Mode hell würde).
- **Switcher im User-Menü** (`sidebar.blade.php` — Desktop + Mobile):
- Vor dem Logout-Button: kleiner Block mit Eyebrow „Erscheinung" und
FluxUI-Segmented-Radio-Group mit Icons-only.
```blade
<flux:radio.group x-data variant="segmented" size="sm" x-model="$flux.appearance">
<flux:radio value="light" icon="sun" :title="__('Hell')" />
<flux:radio value="dark" icon="moon" :title="__('Dunkel')" />
<flux:radio value="system" icon="computer-desktop" :title="__('System')" />
</flux:radio.group>
```
- `$flux.appearance` ist FluxUIs Magic-Object — LocalStorage-persistent,
`@fluxAppearance`-Script setzt `class="dark"` auf `<html>`.
- In BEIDEN Dropdowns (Desktop in der Sidebar, Mobile im Header).
- **Hub-Frontend bleibt Light-Only** (Plan-Vorgabe):
- `auth/pressekonto.blade.php` lädt KEIN `@fluxAppearance` und KEIN
`partials/head`. Damit wird `class="dark"` dort nie gesetzt — auch
nicht, wenn LocalStorage `dark` enthält. Kein Eingriff nötig.
- Hub-Landing analog (eigene `<head>`-Pipeline).
- **Build/Verifikation**:
- `npm run build:portal``portal: 418.55 kB` (Δ +1.5 kB für
Dark-Tokens + Switcher).
- `php artisan test --filter='DashboardTest|AuthenticationTest|RegistrationTest|CustomerPortalTest'`
**18/18 passed**, 70 Assertions. Keine Regressions.
- `vendor/bin/pint --dirty``passed`.
- Linter: 0 Errors.
- **Lessons learned**:
- Tailwind v4's `@theme`-Variables sind **dynamisch** zur Laufzeit —
`--color-accent: var(--color-hub)` schaltet beim `.dark`-Switch
automatisch mit, ohne dass man `.dark { --color-accent: … }` extra
setzen muss.
- Wenn ein Wert in beiden Modes IDENTISCH bleiben soll (`--color-panel-dark-2`,
`--color-accent-warm`), gehört er in den Light-Block und wird im
`.dark`-Block bewusst NICHT überschrieben. Vererbung tut den Rest.
- FluxUI's `flux:radio.group variant="segmented"` mit Icons-only ist
perfekt für Dropdown-Switcher; `:title="…"` liefert Tooltips ohne
sichtbares Label.
- **Bonus**: Der ursprüngliche Symptom-Fix aus Phase 1
(Multi-Alpine + Dark-Mode-Bug) ist jetzt strukturell aufgelöst:
- Alpine kommt ausschließlich aus `@fluxScripts`.
- Dark Mode ist echt umgesetzt, nicht maskiert.
---
## 2026-05-19 · Phase 2 · Customer-Dashboard auf Mockup-Stil
- **Anlass**: User-Freigabe „weiter“ nach Behebung des Dark-Mode/Alpine-Bugs.
Phase 2 laut Plan: Customer-Dashboard (`/admin/me`) an
`User Dashboard presseportale.html` angleichen.
- **Vorab-Doku**: `04-PHASE-2-CUSTOMER-DASHBOARD.md` mit Mockup-Diff-Tabelle,
CSS-Strategie, Component-Skizzen, Akzeptanzkriterien.
- **CSS-Architektur — DRY-Schritt**:
- Neue Datei `resources/css/shared/hub-components.css` als **Single Source
of Truth** für Hub-Layout-Bausteine: `.panel` (warm/dark/head),
`.stat-card` (+ Varianten `is-primary|ok|warn|muted`, `.stat-strip`,
`.stat-label`, `.stat-num`, `.stat-meta`, `.stat-trend`), `.hint-card`
(+ `.hint-ico`, `.hint-bar`, `.hint-action`), `.badge` (+ `.warn|ok|hub|err|dot`),
`.bridge-row`/`.dot-pe`/`.dot-bp`, portable `.eyebrow` und
`.section-eyebrow` (für Portal — Web-Build überschreibt idempotent).
- Importiert in **beiden** Builds:
- `resources/css/portal.css` (nach `flux.css`)
- `resources/css/web/shared-styles.css` (nach `design-tokens.css`)
- Alle Werte nutzen `var(--color-*)`-Tokens — keine Hex-Literale.
- Fehlende Tokens nachgezogen in `shared/design-tokens.css`:
`--color-bg-rule-2` (hellere Progress-Track-Variante) und
`--color-gain-deep` (dunkles Grün für `is-ok`-Stat-Num).
- **Neue Blade-Components** in `resources/views/components/portal/`:
- `<x-portal.stat-card variant="…" label="…" :value="…">`
mit Slots `meta` (oben rechts) und `trend` (unten).
- `<x-portal.hint-card :icon :title :percent :href :action>`
mit Progress-Bar bei gesetztem `$percent` und `wire:navigate`-Link.
- **`livewire/customer/dashboard.blade.php` komplett neu**:
- **Page-Header**: Hub-Badge „User Backend“ + Eyebrow + großes H1
+ Subtitle. Rechts entweder Aktive-Firma-Pille **oder** Warn-Pille
„Keine Firma zugeordnet → zuordnen“.
- **KPI-Reihe** (4 `stat-card`s): Gesamt (primary, mit Δ-zu-Vormonat),
Veröffentlicht (ok), In Prüfung (warn), Entwürfe (muted).
- **Zweispalten-Grid** (`lg:grid-cols-[2fr_1fr]`):
- Links: Panel mit Pressemitteilungen-Liste oder Empty-State (Icon-Box
mit Notification-Dot, Headline, Action-Button, Schritt-Karten 01/02/03,
Footer-Tipp „4 Stunden“).
- Rechts: Panel „Datenqualität“ mit `<x-portal.hint-card>`s oder
Success-State („Alles im grünen Bereich“).
- **Unteres Grid**: Firmen-Panel (Liste **oder** dashed Empty-Slot
+ Hinweis-Box) + Brand-Bridge-Dark-Panel (presseecho + businessportal24
mit Status, API-Status-Indikator, Tarif).
- **Footer**: Subtle-Link-Reihe (Sicherheit, API & Tokens, Profil).
- **Volt-`with()` erweitert**:
- `stats.deltaMonth` — Monatsvergleich via zweiter `whereBetween`-Query.
- `profileCompleteness` (Heuristik auf 6 Profile-Kernfelder).
- `billingCompleteness` (5 Address-Pflichtfelder).
- `bridgeStatus` (vorerst hardcoded `connected` — Phase 3+).
- `qualityHints[]` optional um `percent`-Feld erweitert — wenn gesetzt,
rendert Progress-Bar in der Hint-Card.
- **Edge-Case beim Bauen**: Blade interpretierte `<flux:icon>` im PHPDoc-
Kommentar von `hint-card.blade.php` als echten Component-Tag und produzierte
`Undefined variable $component`. Fix: Doc-Block entkontaminiert
(„flux:icon“ ohne Spitzklammern).
- **Tests neu** in `tests/Feature/Customer/DashboardTest.php` (5 Cases):
1. Customer-Dashboard rendert Core-Sections ohne Fehler
2. Empty-State mit 3-Schritt-Intro wird gezeigt
3. PR-Liste + KPI-Zahlen rendern bei vorhandenen Daten
4. Profile-Completeness-Hint mit Prozentwert erscheint bei Teildaten
5. Vollständiges Profil + Billing → Hints werden ausgeblendet,
„Alles im grünen Bereich“ wird gezeigt
- **Ergebnis**: 5/5 passed, 21 Assertions.
- Cross-Check: Verwandte Tests (`Dashboard|Authentication|Registration|CustomerPortal`)
**18/18 grün**, 70 Assertions. Keine Regressions.
- **Build/Verifikation**:
- `npm run build:portal``portal: 417.02 kB` (Δ +8 kB für neue
Hub-Components-Klassen + Dashboard-Klassen). Web-Build unverändert.
- `vendor/bin/pint --dirty``passed`.
- **Was bewusst weggelassen**:
- Sparklines auf den Stat-Cards (kommen mit echten Trend-Daten in Phase 4).
- Topbar-Anpassung (eigene Phase, später).
- `<x-portal.bridge-card>`-Extraktion (Brand-Bridge bleibt inline, bis
sie an mehr Stellen gebraucht wird).
- **Lessons learned**:
- PHPDoc in Blade-Components darf KEINE `<flux:*>`-Tags enthalten —
der Blade-Pre-Compiler scannt das gesamte File vor dem PHP-Parsing.
- `shared/hub-components.css` ist der richtige Hebel für Phase 3
(Admin-Dashboard) und Phase 4 (Listen/Detail) — Components-CSS muss
nicht mehr pro Phase neu definiert werden.
---
## 2026-05-19 · Phase 1 Refinement · Dark-Mode-Bug + Multi-Alpine
- **Anlass**: User-Review nach Kontrast-Refinement: zwei konkrete Befunde
aus der Browser-Konsole.
1. `[Warning] Detected multiple instances of Alpine running (me, line 114)`
2. Button-Default-Farbe ist `rgb(109, 138, 211)` (helles Blau), nicht
das gewünschte Hub-Blau `rgb(26, 37, 64)`. Computed Style zeigt:
`.bg-[var(--color-accent)] { background-color: var(--color-accent); }`
`var(--color-accent)` löst zu `#6d8ad3` auf.
- **Diagnose Bug 1 (Alpine)**:
- `partials/head.blade.php` lädt `resources/js/app.js` über `@vite`.
`app.js` ruft `Alpine.start()` auf.
- Am Ende des `<body>` injiziert `@fluxScripts` (sidebar.blade.php Zeile
nahe `</body>`) **eine eigene Alpine-Instanz** (FluxUI bringt Alpine
selbst mit).
- Ergebnis: zwei `window.Alpine`-Objekte, Bindings teils tot, Warning.
- Auf der Login-Seite hatten wir das **bereits** gefixt (app.js dort
rausgenommen), aber im Portal-Layout war der Fix noch offen.
- **Diagnose Bug 2 (Button-Farbe)**:
- Der Wert `rgb(109, 138, 211)` = `#6d8ad3` ist **exakt** der
Dark-Mode-Fallback, den ich in `portal.css` unter `.dark { --color-accent: #6d8ad3 }`
definiert hatte.
- FluxUI's `@fluxAppearance`-Helper schreibt `class="dark"` auf
`<html>`, sobald der User den Appearance-Switcher mal auf "dark"
gestellt hatte (gespeichert in `localStorage`).
- Damit überschreibt der Dark-Block den Light-Mode-Akzent
`var(--color-hub)` (`#1A2540`) — Buttons sehen plötzlich blassblau aus.
- Dark Mode ist laut Plan erst Phase 5, war aber durch den
Fallback-Block bereits halb aktiv.
- **Fixes**:
- `resources/views/partials/head.blade.php`: `resources/js/app.js`
aus dem `@vite`-Aufruf **entfernt**. Alpine kommt im Portal
ausschließlich über `@fluxScripts`. (Hub-Landing nutzt
`partials/head` nicht — eigene `<head>`-Pipeline.)
- `resources/css/portal.css`: Der `.dark { --color-accent: ... }`-
Block setzt jetzt **bewusst** alle Akzent-Tokens auf
`var(--color-hub)` (also den Light-Mode-Wert). Damit bleibt das
Hub-Blau auch bei eingeschaltetem `class="dark"` konsistent.
Kommentar dokumentiert, dass Phase 5 diesen Block durch das
echte Dark-Token-Mapping aus `shared/design-tokens.css` ersetzt.
- **Build/Verifikation**:
- `npm run build:portal``portal: 408.97 kB` (Δ +0.02 kB).
- `php artisan test --compact --filter='AuthenticationTest|RegistrationTest'`
**6 passed, 19 assertions**, 1.93s.
- `vendor/bin/pint --dirty --format agent``passed`.
- **User-Action zur Verifikation**:
- Im Portal Hard-Reload (Cmd/Strg+Shift+R) — die alte `app.js`
hängt sonst im HTTP-Cache.
- Konsole sollte **keine** Multi-Alpine-Warning mehr werfen.
- Primary-Buttons sind im Default-State sattes Hub-Blau (`#1A2540`),
Hover noch dunkler (`#243152`).
---
## 2026-05-19 · Phase 1 Refinement · Kontraste & Button-Hover
- **Anlass**: User-Review nach Phase 1: „Buttons als Primary auf
`rgb(26, 37, 64)` einsetzen, die andere Farbe ist deutlich zu hell.
Es fehlen noch deutliche Kontraste."
- **Diagnose**:
- FluxUI-Primary-Buttons sind eigentlich **schon** auf `var(--color-accent)`
= `var(--color-hub)` = `#1A2540` = `rgb(26, 37, 64)` gesetzt
(im Build verifiziert). Sollte stimmen.
- **ABER**: Der FluxUI-Default-Hover ist
`hover:bg-[color-mix(in_oklab,_var(--color-accent),_transparent_10%)]`
— auf hellem Buchpapier wirkt das hell-blau statt der gewünschten
Hub-Konvention `hover:bg-hub-2` (dunkler als Default).
- Außerdem zu wenig Schatten/Border-Kontrast für klare Button-Kanten.
- **Erkenntnis**: Frühere `[data-flux-button][data-variant="primary"]`-
Overrides griffen **nie**, weil FluxUI kein `data-variant`-Attribut
rendert. Variant-Styling kommt komplett über Tailwind-Klassen
(z.B. `bg-[var(--color-accent)]`). Neue Selektor-Strategie nutzt
jetzt diese Klassen direkt (mit escapeten Brackets).
- **Fixes in `portal.css`**:
- **Primary-Button-Hover** auf `var(--color-hub-2)` (#243152) statt
FluxUI's color-mix-Hellung. Selektor:
`[data-flux-button].hover\:bg-\[color-mix\(in_oklab\,_var\(--color-accent\)\,_transparent_10\%\)\]:hover`
- **Primary-Button-Shadow**: kräftigeres Inset-Highlight + warmer
Drop-Shadow in Hub-Blau-Alpha für klare Kanten auf Buchpapier.
Border zusätzlich auf `var(--color-hub-2)` für definierten Rand.
- **Hover-State**: noch stärkerer Shadow + Drop für Tiefenwirkung.
- **Input-Focus** auf Hub-Blau-Ring (statt blassem Default-Akzent),
mit Buchpapier-Offset für saubere Trennung.
- Alter (defunkter) `[data-flux-button][data-variant="primary"]`-
Block entfernt, weil Selektor nicht existiert.
- **Build/Verifikation**:
- `npm run build:portal``portal: 408.95 kB` (von 409.03 kB, -0.08 kB).
- Im finalen CSS verifiziert:
* Hub-2-Hover-Override:
`[data-flux-button].hover\:bg-…:hover{background-color:var(--color-hub-2)!important}`
* Shadow-Override:
`[data-flux-button].bg-\[var\(--color-accent\)\]{border-color:var(--color-hub-2);box-shadow:inset 0 1px #ffffff2e,0 1px 2px #1a254040,…}`
* Input-Focus-Ring auf Hub-Blau.
- `vendor/bin/pint --dirty` → passed.
- **Wirkung**:
- Primary-Button **Default**: `#1A2540` (Hub-Blau), klarer warmer
Schatten, definierter Rand → fühlt sich „solid" an wie auf der
Hub-Landing.
- Primary-Button **Hover**: `#243152` (Hub-2, dunkler statt heller)
— Hub-Konvention, signalisiert Interaktion ohne den Eindruck einer
schwächeren Farbe zu erzeugen.
- Inputs zeigen jetzt einen **erkennbaren Hub-Blau-Ring** beim Fokus
statt eines blassen Bernstein-Schimmers.
- **Hinweis an User**: **Hard-Reload** (Cmd+Shift+R) ist nötig — das
CSS-Bundle hat einen neuen Hash (`portal-kuU-opFv.css`).
- **Verfeinerungen noch offen für Phase 2 / Iteration**:
- Subtle/Filled/Ghost-Buttons (`<flux:button variant="filled">`) sind
weiterhin transparent-zinc → bei Bedarf in Phase 2 angleichen.
- Sidebar-Active-Item hat schon Hub-Soft + 2 px-Strip; ggf. den
Strip noch sichtbarer machen wenn weiter zu unauffällig.
---
## 2026-05-19 · Phase 1 abgeschlossen · Portal-Shell auf Hub-Design
- **Was**: Portal-Shell (Sidebar, Topbar, Layout-Container, Customer-Banner)
visuell ans Hub-Design angeglichen. FluxUI bleibt komplett erhalten —
Anpassung erfolgt über CSS-Token-Bridging und `[data-flux-*]`-Overrides.
Brand-Mark ersetzt das Starter-Kit-Logo. Light Mode ist Default; Dark
Mode für Phase 5 vorbereitet (heller Hub-Blau).
- **Dateien (geändert)**:
- `resources/css/portal.css` — komplett refactored:
* `--font-sans: "Inter Tight"` statt `Instrument Sans`
* `--color-accent: var(--color-hub)` (#1A2540) statt `#3ea3dc`
* `--color-zinc-50..950` auf Hub-Buchpapier-Familie gemappt
(Zinc-100 = #F6F4EF, Zinc-700 = #1A1F1C, Zinc-900 = Hub-Blau)
* Hub-Stil-Overrides: `[data-flux-sidebar]`, `[data-flux-navlist]`,
`[data-flux-navlist-item]` mit Active-Strip links, `[data-flux-button]`
Primary/Filled auf Hub-Blau, `[data-flux-card]` Buchpapier
* Dark-Mode-Block bleibt vorerst minimal (nur Accent), Vollumstellung
in Phase 5
- `resources/views/partials/head.blade.php` — Bunny-Font auf
`inter-tight + jetbrains-mono + source-serif-4` (für Brand-Mark)
- `resources/views/components/layouts/app/sidebar.blade.php`:
* `class="dark"` aus `<html>` entfernt
* `<body>` jetzt `bg-bg text-ink antialiased` (Hub-Buchpapier)
* `<x-app-logo>` ersetzt durch `<x-web.brand-mark brand="pressekonto"
:serif="false" />` + Eyebrow "Publisher · Hub" — sowohl im Desktop-
Brand-Block als auch im Mobile-Header
* Testmodus-Block (Impersonation) komplett im Hub-Stil: dunkles
Hub-Blau-Panel mit Bernstein-Eyebrow „Testmodus aktiv", weiße CTA
„Zurück zum Admin" (statt Amber-Warnfarbe wie zuvor)
* Resources-Block (Tailwind/HeroIcons/Flux/Repository) entfernt —
gehört nicht ins Live-Portal
- `resources/views/components/layouts/app.blade.php`:
* Customer-Banner („User Backend") visuell auf Hub-Stil: Hub-Soft-
Hintergrund + Hub-Soft-2-Border, Hub-Blau-Pille mit Dot, Eyebrow
„Firmenkontext" in Sperrschrift, Heading in Hub-Ink
- **Dateien (Test-Anpassungen, weil rollen-basierter Redirect aus Login-Fix
weitere Tests berührt hat)**:
- `tests/Feature/Auth/AuthenticationTest.php` — Test-User auf
`superAdmin()` umgestellt (sonst `canAccessAdmin() === false`
Redirect auf `/` statt `/dashboard`)
- `tests/Feature/Auth/RegistrationTest.php``terms_accepted` im
Formular gesetzt + Redirect-Erwartung auf `/` angepasst (frisch
registrierte User haben keine Rolle → Fallback `/`)
- **Build/Test**:
- `npm run build:portal``portal: 409.03 kB` (vorher 408.89 kB,
+0.14 kB). Unter dem 10 %-Akzeptanzkriterium (≤ 450 kB).
- CSS-Inspektion bestätigt Phase-1-Tokens:
* `--color-accent: var(--color-hub)` und `.dark { --color-accent:
#6d8ad3 }` im `:root`
* `--color-zinc-700: #1a1f1c` (Hub-Ink statt Zinc-Grau)
* `--font-sans: "Inter Tight"` ohne Instrument-Sans-Vorlauf
* Hub-Overrides im Output: `[data-flux-sidebar]{background:var(...)}`,
`[data-flux-navlist-item][aria-current=page]{background:var(
--color-hub-soft);...}`
- Auth-Test-Suite: **0 zusätzliche Regressionen** verifiziert (8 fail
/ 15 passed vor & nach Phase 1; verbleibende Failures sind pre-existing
Domain-Mismatch- und CSRF-Issues im Test-Setup).
- Smoke-Test: `/dashboard`, `/admin/me`, `/settings/profile`,
`/admin/companies`, `/admin/press-releases` antworten alle HTTP 302
`/login` (erwartetes Verhalten ohne Auth-Session via curl).
- `vendor/bin/pint --dirty` → passed.
- **Bewusst NICHT in Phase 1**:
- **Topbar** mit Breadcrumb + Bridge-Row + Search + „Neue Mitteilung"-CTA
aus dem Mockup → ist für Phase 2 (Customer-Dashboard) sinnvoller,
weil die Topbar dort Page-Kontext (Breadcrumb-Titel) braucht. Aktuell
nutzen wir FluxUI's Default-Layout ohne dedizierte Topbar.
- Konto-Switcher als Sidebar-Header (Avatar + Name + Firma als
Dropdown-Trigger oben statt unten) → das User-Menü unten bleibt
vorerst FluxUI-Standard. Iterativ in Phase 2.
- Dashboard-Inhalte (Stat-Cards, Hint-Cards) → Phase 2 & 3.
- Listen-Pages → Phase 4 (automatisch durch Token-Bridge schon „okay").
- **Beobachtungen**:
- FluxUI-Navlist-Item-Selektor: `[aria-current="page"]` greift. Falls
künftige FluxUI-Versionen `[data-current="true"]` statt
`[aria-current="page"]` setzen, deckt der Override beides ab.
- Vendor-Pfad in `partials/head.blade.php` bewusst nicht geändert —
Vite-Setup mit `build/portal` bleibt wie gehabt.
- **Nächster Schritt**: **Review-Stopp**. Bei Freigabe → entweder
Phase 2 (Customer-Dashboard-Stat-Cards & Hint-Cards) oder Topbar-
Iteration. Entscheidung bei dir/Frank nach Anschauen.
---
## 2026-05-19 · Login-Funktionsfix · Doppelte Alpine-Instanz
- **Was**: Nach Phase 0 trat ein funktionaler Bug auf: Login-Form ging
nicht durch, Browser-Logs zeigten „Detected multiple instances of
Alpine running" und „Livewire: published assets out of date".
- **Ursache**: Das Hub-Auth-Layout (`pressekonto.blade.php`) lud sowohl
`resources/js/app.js` (das `Alpine.start()` aufruft) als auch
`@livewireScripts` (das Alpine intern mitbringt). Zwei Alpine-Instanzen
`wire:submit`, `x-data`, `wire:model` brachen.
- **Fixes**:
- `app.js` aus `@vite([…])` im Auth-Layout entfernt — Alpine kommt nun
nur noch über Livewire.
- `php artisan livewire:publish --assets``livewire.js` von 450 kB
auf 552 kB aktualisiert (Versions-Mismatch behoben).
- Login/Register-Redirect auf rollen-basierte Logik umgestellt
(Admin → `/dashboard`, Customer → `/admin/me`, sonst `/`) und
`navigate: true` entfernt — SPA-Navigation kann den Wechsel zwischen
Web-Build (Hub-Auth) und Portal-Build (FluxUI-Dashboard) nicht
handhaben.
- **Dateien**:
- `resources/views/components/layouts/auth/pressekonto.blade.php`
- `resources/views/livewire/auth/login.blade.php`
- `resources/views/livewire/auth/register.blade.php`
- **Verifikation**: Login funktioniert (User bestätigt), Redirect zum
Dashboard läuft, keine Browser-Warnings mehr.
---
## 2026-05-19 · Phase 0 abgeschlossen · Token-Unifizierung
- **Was**: Single Source of Truth für Design-Tokens etabliert. Web- und
Portal-Build importieren beide `resources/css/shared/design-tokens.css`.
Visuelle Unverändertheit verifiziert — FluxUI-Defaults gewinnen im
Portal weiterhin (Instrument Sans, Zinc, `#3ea3dc`), wird in Phase 1
abgelöst.
- **Dateien**:
- `resources/css/shared/design-tokens.css` **neu** — alle Hub-Tokens
plus Status-Reihe (`--color-warn`, `--color-warn-soft`,
`--color-err`, `--color-err-soft`, `--color-ok-soft`), Bridge-Dots
(`--color-bridge-presseecho`, `--color-bridge-businessportal`),
Radii (`--radius-xs/sm/md/lg`) und Schatten (`--shadow-soft`,
`--shadow-auth`). Dark-Mode-Block als auskommentierter Vorgriff.
- `resources/css/web/shared-styles.css` — Token-Import nach
`tailwindcss` ergänzt. Wirkt damit für alle Web-Themes
(pressekonto, presseecho, businessportal24).
- `resources/css/web/theme-pressekonto.css` — kompletter `@theme {}`-
Block entfernt (lebt jetzt in der Token-Datei). HSL-Legacy-Variablen
und `@layer components { … }` blieben unverändert.
- `resources/css/portal.css` — Token-Datei importiert, FluxUI-Setup
mit Zinc + Instrument Sans bleibt vorerst dominant. Phase 1 löst
es ab.
- **Build/Test**:
- `npm run build:web``theme-pressekonto: 193 kB` (vorher 189 kB,
+4 kB für neu gebridgde Status-Tokens), `theme-presseecho: 189 kB`,
`theme-businessportal24: 189 kB`.
- `npm run build:portal``portal: 408 kB` (vorher 397 kB, +12 kB
für die zusätzlich verfügbaren Hub-Tokens als CSS-Vars im `:root`).
Wird in Phase 1 wieder egalisiert, wenn das Zinc-Setup wegfällt.
- Smoke-Test: `pressekonto.test/`, `/login`, `/register`,
`/dashboard` (302→login), `presseecho.test/`, `businessportal24.test/`
— alle HTTP 200/302 wie erwartet.
- CSS-Inspektion: `--color-hub: #1a2540` und `--color-warn: #a87a1f`
sind in `theme-pressekonto-*.css` enthalten. Im Portal-Build
bleibt `--font-sans: "Instrument Sans"` — bestätigt visuelle
Unverändertheit.
- `vendor/bin/pint --dirty` → passed.
- **Offene Fragen / Beobachtungen**:
- Weil Tailwind v4 alle in `@theme {}` definierten Tokens als CSS-
Variablen im `:root` ausgibt (auch ohne Utility-Verwendung), wird
der Portal-CSS-Wuchs in Phase 1 nicht komplett zurückgehen —
die Hub-Tokens bleiben als Variablen verfügbar. Der Nettoeffekt
nach Phase 1 sollte ähnlich bleiben (+/- 510 kB).
- Dark-Mode-Block in `design-tokens.css` ist auskommentiert
vorbereitet, aber bewusst noch nicht aktiv (Phase 5).
- **Nächster Schritt**: **Review-Stopp**. Bei Freigabe →
Phase 1 starten: Portal-Shell (Sidebar, Topbar, Layout-Container)
auf Hub-Style mappen, Zinc-Palette ablösen, FluxUI-Overrides via
`[data-flux-*]`-Selektoren in `portal.css`. Details:
`02-PHASE-1-PORTAL-SHELL.md`.
---
## 2026-05-19 · Setup
- **Was**: Architektur-Entscheidung getroffen → **Plan B** (Tokens teilen,
Komponenten getrennt lassen). Dokumentation in `dev/frontend/hub-flux/`
angelegt:
- `README.md` — Übersicht & Entscheidung
- `01-PHASE-0-TOKENS.md` — Detail-Plan für Tokens-Vereinheitlichung
- `02-PHASE-1-PORTAL-SHELL.md` — Detail-Plan für Portal-Shell-Refresh
- `03-WEITERE-PHASEN.md` — Outline Phase 26
- `PROGRESS.md` — dieses Log
- **Bestätigt**:
- Hub-Blau als **Primärer Akzent** im Portal (klarer Kontrast zum hellen
Buchpapier-Hintergrund)
- Bernstein als **Sekundärer Akzent** (Notifications, Datenqualität,
Akzent-Ribbons)
- Brand bleibt **pressekonto** (auch im Portal-Sidebar via
`<x-web.brand-mark>`)
- Reihenfolge: erst Phase 0 + 1, dann Review
- **Nächster Schritt**: Phase 0 umsetzen
`resources/css/shared/design-tokens.css` erstellen,
`theme-pressekonto.css` refactoren, `portal.css` minimal vorbereiten.
---
## Vorlauf (zur Erinnerung, was schon steht)
- **Hub-Landing** (`web/pressekonto.blade.php`) — fertig, lebt im Web-Build
mit `theme-pressekonto.css`
- **Hub-Auth** (Login, Register, Forgot, Reset, Verify, Confirm) — fertig
im Hub-Design, neues Layout `auth/pressekonto.blade.php` im Web-Build
- **Register-AGB-Checkbox** mit Server-Side-Validierung ergänzt
- **Portal-Backend** mit FluxUI funktional vorhanden (Admin-Dashboard,
Customer-Dashboard, 77 Blade-Dateien mit FluxUI), aber **visuell noch
Starter-Kit-Look** (Zinc + `#3ea3dc` + Instrument Sans)
---
## Template für neue Einträge
```markdown
## YYYY-MM-DD · Phase N · Kurztitel
- **Was**: [Was wurde konkret gemacht?]
- **Dateien**: [Pfade]
- **Build/Test**: [Wie verifiziert?]
- **Offene Fragen**: [Falls etwas unklar geblieben ist]
- **Nächster Schritt**: [Was als Nächstes ansteht]
```

View file

@ -0,0 +1,143 @@
# Hub × FluxUI — Visuelle Vereinheitlichung Portal ↔ Hub
> **Ziel**: Das Portal-Backend (User-Panel, Admin-Bereich) wird visuell an den
> Hub-Stil (`pressekonto.test`-Landing + Auth-Seiten) angeglichen, **ohne
> FluxUI aufzugeben**. Die FluxUI-Komponenten werden über Design-Tokens und
> CSS-Overrides ans Hub-Design adaptiert.
## Status
| Phase | Beschreibung | Status |
|-------|--------------|--------|
| 0 | Design-Tokens vereinheitlichen | **✅ abgeschlossen** (2026-05-19) |
| 1 | Portal-Shell (Sidebar, Layout, Brand-Mark) | **✅ abgeschlossen** (2026-05-19) |
| 2 | Customer-Dashboard auf Mockup-Stil (inkl. Topbar) | 🟡 wartet auf Freigabe |
| 3 | Admin-Dashboard konsistent | ⚪ später |
| 4 | Listen-/Detail-Pages | ⚪ iterativ |
| 5 | Dark Mode konsistent | ⚪ später |
| 6 | Auth-Konsolidierung (optional) | ⚪ optional |
→ Tagesaktueller Fortschritt: [`PROGRESS.md`](./PROGRESS.md)
## Dokumente in diesem Verzeichnis
| Datei | Zweck |
|-------|-------|
| [`README.md`](./README.md) | Diese Übersicht: Entscheidung, Architektur, Status |
| [`01-PHASE-0-TOKENS.md`](./01-PHASE-0-TOKENS.md) | Phase 0: Tokens vereinheitlichen (aktiv) |
| [`02-PHASE-1-PORTAL-SHELL.md`](./02-PHASE-1-PORTAL-SHELL.md) | Phase 1: Portal-Shell ans Hub-Design angleichen |
| [`03-WEITERE-PHASEN.md`](./03-WEITERE-PHASEN.md) | Outline für Phasen 26 |
| [`PROGRESS.md`](./PROGRESS.md) | Fortschritts-Log + Notizen |
## Visuelle Vorlagen
Maßgebliche Mockups für diese Arbeit:
- `dev/frontend/tailwind_v3/User Dashboard presseportale.html` — Light-Variante
- `dev/frontend/tailwind_v3/User Dashboard presseportale Dark.html` — Dark-Variante
- `dev/frontend/tailwind_v3/Login pressekonto A3 Tailwind.html` — Hub-Auth-Vorlage (bereits umgesetzt)
- `dev/frontend/tailwind_v3/Hub Landing pressekonto-2.html` — Hub-Landing-Vorlage (bereits umgesetzt)
## Architektur-Entscheidung
### Plan B (gewählt) — Tokens teilen, Komponenten getrennt lassen
```
┌─────────────────────────────────────────────────────────────────┐
│ shared/design-tokens.css │
│ (Hub-Blau, Bernstein, Buchpapier, Inter Tight, JetBrains Mono) │
└────────────────┬────────────────────────┬───────────────────────┘
│ │
┌────────▼─────────┐ ┌─────────▼──────────┐
│ portal.css │ │ theme-pressekonto │
│ + FluxUI │ │ + shared-styles │
│ (Backend) │ │ (Hub-Landing/Auth)│
└──────────────────┘ └────────────────────┘
build/portal build/web
```
**Warum nicht alles in FluxUI?**
- Die Hub-Landing lebt von Atmosphäre (konzentrische Kreise, Hub-Grid, eigene
Eyebrows, Bridge-Cards), die mit FluxUI-Komponenten nicht 1:1 abbildbar ist
- FluxUI würde im Hub-Frontend zur unnötigen Bundle-Last
- Der gerade gebaute Hub-Login wäre verloren
**Warum nicht alles Custom?**
- 77 Blade-Dateien nutzen FluxUI ein Rewrite wäre Aufwand ohne Mehrwert
- FluxUI-Tabellen, -Forms, -Modals, -Date-Picker sind im Backend Gold wert
- FluxUI ist sehr gut **customizable** über CSS-Custom-Properties
und `[data-flux-*]`-Selectoren
### Verworfen — Plan A (alles FluxUI) & Plan C (alles im Web-Build)
Siehe Chat-Historie für die Begründung. Kurz: zu viel Aufwand, zu wenig
Mehrwert, würde bereits gelieferte Hub-Atmosphäre verschlechtern.
## Branding & Tokens
### Brand
- **Marke**: pressekonto (verbindlich, für Portal und Hub)
- **Wortmarke**: `<x-web.brand-mark brand="pressekonto" />` — auch im Portal
- **Logo-Komponente** `<x-app-logo>` wird im Portal **abgelöst**
### Farben (Light)
| Token | Wert | Rolle |
|-------|------|-------|
| `--color-hub` | `#1A2540` | **Primärer Akzent** (Sidebar-Active, Primary-Buttons, Eyebrows) |
| `--color-hub-2` | `#243152` | Hover-Stufe von `--color-hub` |
| `--color-hub-3` | `#2E3D66` | Tertiäre Stufe (Dekoration auf dunklem Grund) |
| `--color-hub-soft` | `#E5E9F1` | Active-Pill-Hintergrund in der Sidebar |
| `--color-accent` | `#B07A3A` | **Sekundärer Akzent** (Bernstein Notifications, Datenqualität, Empfehlungs-Ribbon) |
| `--color-accent-deep` | `#8A5E27` | Akzent-Hover/Text |
| `--color-bg` | `#F6F4EF` | Warmes Buchpapier — Haupt-Hintergrund |
| `--color-bg-elev` | `#FBFAF6` | Elevation 1 (Sidebar, Topbar, leichte Cards) |
| `--color-bg-card` | `#FFFFFF` | Reine Cards |
| `--color-bg-rule` | `#E2DDD0` | Standard-Trennlinien |
| `--color-ink` | `#1A1F1C` | Primärtext |
| `--color-ink-2` | `#3A413D` | Sekundärtext (Begleittexte) |
| `--color-ink-3` | `#5A6360` | Meta, Labels |
| `--color-ink-4` | `#8A918D` | Disabled, Hintergrund-Meta |
### Farben (Dark) — laut Mockup
Wird in Phase 5 ausgearbeitet. Hub-Blau wird heller (`#5A78C2`), Akzent
wärmer (`#D9A560`), Hintergrund tief Anthrazit (`#0E1218`). Wichtig: die
Token-Namen bleiben gleich, nur die Werte tauschen — keine doppelte
UI-Pflege.
### Typografie
| Token | Wert |
|-------|------|
| `--font-sans` | `"Inter Tight", Inter, system-ui, sans-serif` |
| `--font-mono` | `"JetBrains Mono", "SF Mono", ui-monospace, monospace` |
| `--font-serif` | `"Source Serif 4", Georgia, serif` (nur für Brand-Mark) |
Aktuell im Portal: **Instrument Sans** → wird in Phase 1 abgelöst.
## Konventionen
- **Light Mode** ist Default. `class="dark"` auf `<html>` wird entfernt.
- Dark Mode wird via `prefers-color-scheme` + Flux Appearance-Switcher
gesteuert. Token-Layer übernimmt die Umschaltung.
- Token-Names sind **identisch** zwischen Hub-Theme und Portal-Theme.
Sowohl `bg-bg-elev` als auch `bg-hub-soft` funktionieren in beiden Welten.
- FluxUI-Komponenten werden **nicht überschrieben**, sondern über
`[data-flux-*]`-Selectoren in `portal.css` ergänzt — minimaler Eingriff,
maximale Kompatibilität mit FluxUI-Updates.
## Was wir bewusst NICHT machen
- **Kein FluxUI-Rewrite**. Nur Token-Anpassung + Selektor-Overrides.
- **Keine zweite UI-Pflege für Dark Mode**. Tokens-only-Ansatz.
- **Kein Verschmelzen der Builds** (Phase 6 optional).
- **Keine Änderung an Volt-/Livewire-Logik** in dieser Arbeit.
## Wer ändert was
- **Diese Doku** wird mit jeder Phase aktualisiert. `PROGRESS.md` enthält
die Tages-Notes.
- Code-Änderungen sind kleinteilig und werden in eigenen Commits gebündelt
(`hub-flux: phase 0 — tokens vereinheitlicht`, etc.).
- Bei Unklarheiten/Entscheidungen: kurz in `PROGRESS.md` festhalten,
damit nachvollziehbar bleibt warum etwas so und nicht anders.

View file

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>presseportale.com — Publisher-Hub</title>
<title>pressekonto.de — Publisher-Hub</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
@ -163,7 +163,7 @@
<!-- Wortmark: schlicht, kein Symbol — wirkt im Verlags-Segment seriöser -->
<a href="#" class="flex items-baseline gap-2.5">
<span class="text-[24px] font-bold tracking-[-0.5px] text-hub leading-none">presseportale<span class="text-accent">.com</span></span>
<span class="text-[24px] font-bold tracking-[-0.5px] text-hub leading-none">pressekonto<span class="text-accent">.com</span></span>
<span class="hidden md:inline-block w-px h-[18px] bg-bg-rule"></span>
<span class="eyebrow muted text-[9.5px] tracking-[0.22em]">Publisher · Hub</span>
</a>
@ -230,7 +230,7 @@
</h1>
<p class="text-[17px] leading-[1.55] text-ink-2 mt-7 m-0 max-w-[560px]">
presseportale.com ist der gemeinsame Publisher-Bereich für unsere beiden Pressefachportale. Pressemitteilungen schreiben, redaktionell prüfen lassen, auf beiden Reichweiten veröffentlichen — und Reichweite, Empfänger und Abrechnung an einem Ort verwalten.
pressekonto.de ist der gemeinsame Publisher-Bereich für unsere beiden Pressefachportale. Pressemitteilungen schreiben, redaktionell prüfen lassen, auf beiden Reichweiten veröffentlichen — und Reichweite, Empfänger und Abrechnung an einem Ort verwalten.
</p>
<div class="mt-8 flex items-center gap-3">
@ -290,7 +290,7 @@
<div class="absolute" style="top:50%;left:50%;transform:translate(-50%,-50%);">
<div class="w-[140px] h-[140px] rounded-full bg-hub-grad flex flex-col items-center justify-center text-white shadow-lg shadow-hub/30">
<div class="text-[10px] font-bold tracking-[0.22em] uppercase text-hub-line">Hub</div>
<div class="text-[15px] font-bold tracking-[-0.3px] mt-1">presseportale</div>
<div class="text-[15px] font-bold tracking-[-0.3px] mt-1">pressekonto</div>
<div class="text-[10px] text-white/55 tracking-[0.18em] uppercase mt-0.5">.com</div>
</div>
</div>
@ -767,15 +767,15 @@
</div>
</section>
<!-- ============== HINTER PRESSEPORTALE — PLATTFORM-FAMILIE ============== -->
<!-- ============== HINTER PRESSEKONTO — PLATTFORM-FAMILIE ============== -->
<section id="familie" class="max-w-layout mx-auto px-8 pt-20 pb-16">
<header class="mb-10">
<div class="section-eyebrow mb-4">Hinter presseportale.com</div>
<div class="section-eyebrow mb-4">Hinter pressekonto.de</div>
<h2 class="text-[32px] font-bold m-0 tracking-[-0.6px] text-ink leading-[1.18] max-w-[820px]">
Zwei eigenständige Pressefachportale. Eine kuratierte Verlags-Familie.
</h2>
<p class="text-[14.5px] text-ink-2 leading-[1.55] m-0 mt-4 max-w-[760px]">
presseportale.com ist nicht „irgendein Tool" — es ist die zentrale Plattform für unsere beiden redaktionell geführten Pressefachportale. Jedes Portal hat einen eigenen Charakter, eigene Leserschaft und eigene Themen-Schwerpunkte.
pressekonto.de ist nicht „irgendein Tool" — es ist die zentrale Plattform für unsere beiden redaktionell geführten Pressefachportale. Jedes Portal hat einen eigenen Charakter, eigene Leserschaft und eigene Themen-Schwerpunkte.
</p>
</header>
@ -856,7 +856,7 @@
<path d="M9 12h6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<div class="text-[13px] leading-[1.55]">
Pressemitteilungen, die Sie über presseportale.com einreichen, erscheinen auf <strong class="text-white font-semibold">beiden Portalen</strong> — ohne Aufpreis, ohne doppelte Eingabe. Sie haben eine zentrale Mitteilungs-Verwaltung und eine zentrale Reichweiten-Statistik.
Pressemitteilungen, die Sie über pressekonto.de einreichen, erscheinen auf <strong class="text-white font-semibold">beiden Portalen</strong> — ohne Aufpreis, ohne doppelte Eingabe. Sie haben eine zentrale Mitteilungs-Verwaltung und eine zentrale Reichweiten-Statistik.
</div>
</div>
</section>
@ -876,7 +876,7 @@
<!-- Fließtext-Nennung — wie auf den Brand-Portalen, kein Logo-Grid -->
<div>
<p class="text-[16px] leading-[1.7] text-ink-2 m-0">
Über presseportale.com veröffentlichen unter anderem
Über pressekonto.de veröffentlichen unter anderem
<a class="text-hub font-semibold underline underline-offset-[3px] decoration-hub/25 hover:decoration-hub" href="#">Siemens AG</a>,
<a class="text-hub font-semibold underline underline-offset-[3px] decoration-hub/25 hover:decoration-hub" href="#">BASF SE</a>,
<a class="text-hub font-semibold underline underline-offset-[3px] decoration-hub/25 hover:decoration-hub" href="#">Deutsche Bank</a>,
@ -1069,7 +1069,7 @@
<div>
<div class="text-[24px] font-bold leading-none tracking-[-0.5px] text-white">
presseportale<span style="color:#B07A3A;">.com</span>
pressekonto<span style="color:#B07A3A;">.com</span>
</div>
<div class="eyebrow on-dark mt-2 text-[9.5px] tracking-[0.22em]" style="color:#7B8FBF;">
Publisher · Hub
@ -1121,7 +1121,7 @@
<div class="border-t border-white/15">
<div class="max-w-layout mx-auto px-8 py-5 flex items-center justify-between gap-4 text-[11.5px] text-white/55">
<span>© 2026 presseportale.com · Alle Rechte vorbehalten</span>
<span>© 2026 pressekonto.de · Alle Rechte vorbehalten</span>
<span class="flex items-center gap-2 font-mono text-[10.5px]">
<span class="inline-block w-1.5 h-1.5 rounded-full bg-ok"></span>
Alle Systeme betriebsbereit

View file

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>presseportale.com — Anmelden</title>
<title>pressekonto.de — Anmelden</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
@ -160,7 +160,7 @@
<header class="relative z-10 px-10 py-[22px] flex items-center justify-between">
<a href="#" class="flex items-baseline gap-2.5 no-underline">
<span class="text-[19px] font-bold tracking-[-0.4px] text-hub leading-none">
presseportale<span class="text-accent">.com</span>
pressekonto<span class="text-accent">.com</span>
</span>
<span class="inline-block w-px h-[14px] bg-bg-rule"></span>
<span class="text-[9.5px] font-bold tracking-[0.22em] uppercase text-ink-3">

View file

@ -0,0 +1,767 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>presseportale.com — Mein Dashboard · Dark</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
<script src="https://cdn.tailwindcss.com?plugins=forms"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
/* DARK MODE — Tokens umgewidmet, gleiche Namen wie Light-Variante */
bg: "#0E1218",
"bg-elev": "#14181F",
"bg-rule": "#2A3142",
"bg-rule-2": "#232838",
"bg-card": "#181D27",
"bg-card-warm": "#1F1A12",
"hub": "#5A78C2",
"hub-2": "#6D8AD3",
"hub-3": "#4A65A8",
"hub-soft": "#1F2A47",
"hub-soft-2":"#2C3A5D",
"hub-line": "#7B8FBF",
"accent": "#D9A560",
"accent-deep": "#B07A3A",
"accent-soft": "#2A2418",
ink: "#ECE9E0",
"ink-2":"#C9C5B8",
"ink-3":"#8E8B82",
"ink-4":"#5D5C57",
"ink-on-dark": "#F6F4EF",
"ink-on-dark-2": "#B2B9C7",
"ink-on-dark-3": "#7B8FBF",
ok: "#4DC076",
"ok-soft":"#1A2D22",
warn: "#D9A560",
"warn-soft":"#2D2418",
err: "#E07664",
"err-soft":"#2E1715",
},
fontFamily: {
sans: ['"Inter Tight"','Inter','system-ui','sans-serif'],
mono: ['"JetBrains Mono"','"SF Mono"','ui-monospace','monospace'],
},
backgroundImage: {
"hub-grad": "linear-gradient(135deg,#1A2540 0%,#243152 100%)",
"hub-grad-2": "linear-gradient(180deg,#1A2540 0%,#0F1729 100%)",
"accent-grad": "linear-gradient(135deg,#B07A3A 0%,#8A5E27 100%)",
}
}
}
};
</script>
<style>
html { color-scheme: dark; }
html,body { margin:0; padding:0; }
body { background:#0A0D12; font-family:"Inter Tight",system-ui,sans-serif; }
.eyebrow {
font-size: 10.5px; font-weight: 700;
letter-spacing: 0.20em; text-transform: uppercase;
color: #B2B9C7;
}
.eyebrow.muted { color:#8E8B82; letter-spacing:0.16em; font-weight:600; font-size:10px; }
.eyebrow.accent { color:#D9A560; }
.eyebrow.on-dark { color:#7B8FBF; }
.section-eyebrow {
display:inline-flex; align-items:center; gap:10px;
font-size: 10.5px; font-weight: 700;
letter-spacing: 0.22em; text-transform: uppercase;
color:#B2B9C7;
}
.section-eyebrow::after {
content:""; display:block; width:30px; height:1px;
background:#B2B9C7; opacity:.35;
}
.rule { height:1px; background:#2A3142; border:0; margin:0; }
.rule-strong { height:1px; background:#ECE9E0; border:0; margin:0; }
/* Sidebar */
.nav-item {
display:flex; align-items:center; gap:11px;
padding:8px 12px;
border-radius:4px;
font-size:13px; font-weight:500;
color:#C9C5B8;
transition: background .12s, color .12s;
position:relative;
}
.nav-item:hover { background:#1F2A47; color:#ECE9E0; }
.nav-item.active {
background:#1F2A47;
color:#ECE9E0;
font-weight:600;
}
.nav-item.active::before {
content:""; position:absolute; left:-1px; top:6px; bottom:6px;
width:2px; background:#7B8FBF; border-radius:0 2px 2px 0;
}
.nav-item.disabled { color:#5D5C57; cursor:default; }
.nav-item.disabled:hover { background:transparent; color:#5D5C57; }
.nav-item .ico {
width:16px; height:16px; flex-shrink:0;
color: currentColor; opacity:.8;
}
.nav-item.active .ico { opacity:1; }
.nav-section {
font-size:10px; font-weight:700; letter-spacing:0.18em;
text-transform:uppercase; color:#5D5C57;
padding: 0 12px 6px;
}
/* Card / panel headers */
.panel {
background:#181D27; border:1px solid #2A3142; border-radius:6px;
}
.panel-warm { background:#14181F; border:1px solid #2A3142; border-radius:6px; }
.panel-dark { background:#0F1729; border:1px solid #1A2540; border-radius:6px; color:#F6F4EF; }
.panel-head {
display:flex; align-items:center; justify-content:space-between;
padding:14px 20px;
border-bottom:1px solid #2A3142;
}
.panel-dark .panel-head { border-bottom-color: rgba(255,255,255,.08); }
/* Stat cards */
.stat-card {
position:relative;
background:#181D27; border:1px solid #2A3142; border-radius:6px;
padding:18px 20px;
transition: border-color .12s, box-shadow .12s;
}
.stat-card .stat-strip {
position:absolute; left:0; top:0; bottom:0; width:3px;
background:#2C3A5D; border-radius:6px 0 0 6px;
}
.stat-card.is-primary .stat-strip { background:#5A78C2; }
.stat-card.is-ok .stat-strip { background:#4DC076; }
.stat-card.is-warn .stat-strip { background:#D9A560; }
.stat-card.is-muted .stat-strip { background:#5D5C57; }
.stat-label {
font-size:10.5px; font-weight:700;
letter-spacing:0.16em; text-transform:uppercase;
color:#8E8B82;
}
.stat-card.is-ok .stat-label { color:#4DC076; }
.stat-card.is-warn .stat-label { color:#D9A560; }
.stat-card.is-muted .stat-label { color:#8E8B82; }
.stat-num {
font-family:"JetBrains Mono","SF Mono",ui-monospace,monospace;
font-variant-numeric:tabular-nums;
font-size:36px; font-weight:600; color:#ECE9E0;
letter-spacing:-0.5px; line-height:1;
margin-top:14px;
}
/* Hint chips (Datenqualität) */
.hint-card {
display:grid; gap:14px;
grid-template-columns: auto 1fr;
align-items:start;
background:#1F1A12; border:1px solid #2D2418; border-left:3px solid #B07A3A;
border-radius:5px;
padding:16px 18px;
}
.hint-card .hint-ico {
width:36px; height:36px; border-radius:4px;
background:#2D2418; color:#D9A560;
display:flex; align-items:center; justify-content:center;
flex-shrink:0;
}
/* FluxUI-style buttons */
.btn-primary {
display:inline-flex; align-items:center; gap:8px; justify-content:center;
padding:9px 16px;
background:#5A78C2; color:#FFFFFF;
border-radius:4px; font-size:13px; font-weight:600;
transition: background .15s;
}
.btn-primary:hover { background:#6D8AD3; }
.btn-secondary {
display:inline-flex; align-items:center; gap:8px; justify-content:center;
padding:8px 14px;
background:#181D27; color:#ECE9E0;
border:1px solid #2A3142; border-radius:4px;
font-size:12.5px; font-weight:600;
transition: border-color .15s, background .15s;
}
.btn-secondary:hover { border-color:#5A78C2; background:#1F2A47; }
.badge {
display:inline-flex; align-items:center; gap:6px;
padding:3px 9px; border-radius:99px;
font-size:10.5px; font-weight:700;
letter-spacing:0.10em; text-transform:uppercase;
}
.badge.warn { background:#2D2418; color:#D9A560; }
.badge.ok { background:#1A2D22; color:#4DC076; }
.badge.hub { background:#1F2A47; color:#B2B9C7; }
.badge.dot::before {
content:""; width:6px; height:6px; border-radius:99px; background:currentColor;
}
/* Bridge ribbon - sehr subtil */
.bridge-row {
display:inline-flex; align-items:center; gap:6px;
font-family:"JetBrains Mono","SF Mono",ui-monospace,monospace;
font-size:10.5px; letter-spacing:0.10em; text-transform:uppercase;
color:#8E8B82;
}
.dot-pe { width:6px; height:6px; border-radius:99px; background:#4DA37A; }
.dot-bp { width:6px; height:6px; border-radius:99px; background:#E36340; }
</style>
</head>
<body class="bg-bg text-ink font-sans antialiased">
<!-- ============== ARTBOARD ============== -->
<div class="mx-auto bg-bg" style="width:1440px;">
<div class="flex" style="min-height:980px;">
<!-- =============================================
SIDEBAR — schmal, warm, klare Hierarchie
(FluxUI <flux:navlist> + Slot-Header-Styles)
============================================== -->
<aside class="bg-bg-elev border-r border-bg-rule flex flex-col" style="width:260px;">
<!-- Brand-Block -->
<div class="px-5 pt-6 pb-5">
<a href="Hub Landing presseportale.html" class="flex items-baseline gap-2">
<span class="text-[19px] font-bold tracking-[-0.4px] text-hub leading-none">presseportale<span class="text-accent">.com</span></span>
</a>
<div class="eyebrow muted mt-2">Publisher · Hub</div>
<!-- Aktive Firma / Konto-Switcher (FluxUI-Slot: header before nav) -->
<button class="mt-4 w-full grid items-center gap-2.5 px-3 py-2.5 bg-bg-card border border-bg-rule rounded-[4px] hover:border-hub/40 text-left"
style="grid-template-columns:auto 1fr auto;">
<span class="w-7 h-7 rounded-[3px] bg-hub-soft border border-hub-soft-2 flex items-center justify-center text-hub text-[11px] font-bold">TU</span>
<span class="min-w-0">
<span class="block text-[12.5px] font-semibold text-ink leading-tight truncate">Test User</span>
<span class="block text-[10.5px] text-ink-3 leading-tight mt-0.5 truncate">Keine Firma zugeordnet</span>
</span>
<svg width="11" height="11" viewBox="0 0 12 12" fill="none" class="text-ink-3">
<path d="M3 4.5l3 3 3-3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 7.5l3-3 3 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" opacity="0.4"/>
</svg>
</button>
</div>
<nav class="px-3 flex-1">
<div class="nav-section">Mein Bereich</div>
<div class="space-y-0.5 mb-5">
<a class="nav-item active" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M2 7l6-5 6 5v7H2z" stroke="currentColor" stroke-width="1.4"/><path d="M6 14V9h4v5" stroke="currentColor" stroke-width="1.4"/></svg>
Übersicht
</a>
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="2.5" width="9" height="11" stroke="currentColor" stroke-width="1.4"/><path d="M11.5 5h2v8.5H4" stroke="currentColor" stroke-width="1.4"/><path d="M5 5.5h4M5 8h4M5 10.5h2.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
Meine Pressemitteilungen
<span class="badge hub ml-auto" style="font-size:9.5px;padding:1px 6px;letter-spacing:0.08em;">0</span>
</a>
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3.5" width="11" height="10" stroke="currentColor" stroke-width="1.4"/><path d="M2.5 6h11" stroke="currentColor" stroke-width="1.4"/><path d="M6 9h1M9 9h1M6 11.5h1M9 11.5h1" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
Firmen
</a>
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M3 5h10l-1 9H4z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><path d="M6 5V3.5a2 2 0 014 0V5" stroke="currentColor" stroke-width="1.4"/></svg>
Buchungen &amp; Add-ons
</a>
<span class="nav-item disabled">
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M3 13V8M7 13V5M11 13V9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
Statistiken
<span class="ml-auto text-[9.5px] tracking-[0.14em] uppercase font-semibold text-ink-4">bald</span>
</span>
</div>
<div class="nav-section">Finanzen</div>
<div class="space-y-0.5 mb-5">
<span class="nav-item disabled">
<svg class="ico" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="5" stroke="currentColor" stroke-width="1.4"/><path d="M8 5.5v5M6 8h4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
Credits &amp; Tarif
<span class="ml-auto text-[9.5px] tracking-[0.14em] uppercase font-semibold text-ink-4">bald</span>
</span>
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M3 2.5h7l3 3v8H3z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><path d="M10 2.5V5.5h3" stroke="currentColor" stroke-width="1.4"/><path d="M5.5 8h5M5.5 10.5h5M5.5 6h2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
Rechnungen
</a>
<span class="nav-item disabled">
<svg class="ico" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="4.5" width="11" height="7.5" stroke="currentColor" stroke-width="1.4"/><path d="M2.5 7h11" stroke="currentColor" stroke-width="1.4"/></svg>
Zahlungsarten
<span class="ml-auto text-[9.5px] tracking-[0.14em] uppercase font-semibold text-ink-4">bald</span>
</span>
</div>
<div class="nav-section">Konto</div>
<div class="space-y-0.5 mb-5">
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="6" r="2.5" stroke="currentColor" stroke-width="1.4"/><path d="M3 13.5c.7-2.4 2.7-4 5-4s4.3 1.6 5 4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
Profil
</a>
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M8 2l5 2v4c0 3-2 5-5 6-3-1-5-3-5-6V4z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><path d="M6 8l1.5 1.5L10.5 6" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
Sicherheit
</a>
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><circle cx="6" cy="8" r="2.5" stroke="currentColor" stroke-width="1.4"/><path d="M8.5 8h5M11 8v2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
API &amp; Integrationen
</a>
<span class="nav-item disabled">
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M3.5 7a4.5 4.5 0 119 0v3.5l1 1.5H2.5l1-1.5z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><path d="M6.5 13a1.5 1.5 0 003 0" stroke="currentColor" stroke-width="1.4"/></svg>
Benachrichtigungen
<span class="ml-auto text-[9.5px] tracking-[0.14em] uppercase font-semibold text-ink-4">bald</span>
</span>
</div>
</nav>
<!-- Testmodus-Block (war im Original „Zurück zum Admin") -->
<div class="px-4 pb-4">
<div class="text-ink-on-dark rounded-[5px] p-4 relative overflow-hidden" style="background:linear-gradient(135deg,#1A2540 0%,#243152 100%); border:1px solid #2A3142;">
<div class="absolute -top-6 -right-6 w-16 h-16 rounded-full opacity-50" style="background:#2E3D66;"></div>
<div class="absolute -bottom-8 -left-8 w-20 h-20 rounded-full opacity-30" style="background:#2E3D66;"></div>
<div class="relative">
<div class="flex items-center gap-2 mb-2">
<span class="w-1.5 h-1.5 rounded-full animate-pulse" style="background:#D9A560;"></span>
<span class="eyebrow on-dark" style="color:#F4D89C;">Testmodus aktiv</span>
</div>
<div class="text-[12px] leading-[1.5] text-ink-on-dark-2">
Angemeldet als <strong class="text-white font-semibold">Test User</strong>.<br/>
Admin: <strong class="text-white font-semibold">Portal Admin</strong>
</div>
<button class="mt-3 w-full px-3 py-2 text-[12px] font-semibold rounded-[3px] transition-colors flex items-center justify-center gap-1.5" style="background:#F6F4EF;color:#1A2540;">
<svg width="11" height="11" viewBox="0 0 12 12" fill="none">
<path d="M9 3L3 9M3 9H8M3 9V4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
Zurück zum Admin
</button>
</div>
</div>
</div>
<!-- Resources -->
<div class="px-3 pb-5 border-t border-bg-rule pt-4">
<div class="nav-section">Resources</div>
<div class="space-y-0.5">
<a class="nav-item" href="#"><svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M3 11l4 2 6-9-4-1z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>Tailwind CSS</a>
<a class="nav-item" href="#"><svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M8 2l5 2v4c0 3-2 5-5 6-3-1-5-3-5-6V4z" stroke="currentColor" stroke-width="1.3"/></svg>Hero Icons</a>
<a class="nav-item" href="#"><svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M9 2L4 9h3l-1 5 5-7H8z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>Flux UI</a>
<a class="nav-item" href="#"><svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M3 4l5-2 5 2v8l-5 2-5-2z" stroke="currentColor" stroke-width="1.3"/><path d="M3 4l5 2 5-2M8 6v8" stroke="currentColor" stroke-width="1.3"/></svg>Repository</a>
</div>
</div>
</aside>
<!-- =============================================
MAIN — Inhalt
============================================== -->
<main class="flex-1 min-w-0">
<!-- Topbar — sehr schlank; Brücken-Kontext + Kontoinfo rechts -->
<div class="bg-bg-elev border-b border-bg-rule">
<div class="px-10 py-3 flex items-center gap-6">
<!-- Breadcrumb -->
<div class="flex items-center gap-2 text-[12px] text-ink-3 font-medium">
<a href="Hub Landing presseportale.html" class="hover:text-hub">Hub</a>
<span class="text-ink-4">/</span>
<span class="text-ink-2">User Backend</span>
<span class="text-ink-4">/</span>
<span class="text-hub font-semibold">Übersicht</span>
</div>
<span class="flex-1"></span>
<!-- Reichweiten-Kontext (Brücke zur Hub-CI) -->
<span class="bridge-row">
<span class="dot-pe"></span> presseecho
<span class="text-ink-4 mx-1">·</span>
<span class="dot-bp"></span> businessportal24
</span>
<span class="w-px h-5 bg-bg-rule"></span>
<!-- Search (FluxUI input slot) -->
<div class="relative">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" class="absolute left-2.5 top-1/2 -translate-y-1/2 text-ink-3">
<circle cx="7" cy="7" r="4.5" stroke="currentColor" stroke-width="1.3"/>
<path d="M10.5 10.5L13 13" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
</svg>
<input class="pl-8 pr-3 py-1.5 w-[220px] bg-bg-card border border-bg-rule rounded-[4px] text-[12.5px] text-ink placeholder:text-ink-4 focus:outline-none focus:border-hub" placeholder="Suchen…" />
<span class="absolute right-2 top-1/2 -translate-y-1/2 text-[10px] font-mono text-ink-4 border border-bg-rule rounded px-1">⌘K</span>
</div>
<button class="relative w-8 h-8 flex items-center justify-center rounded-[4px] hover:bg-bg-card border border-transparent hover:border-bg-rule">
<svg width="15" height="15" viewBox="0 0 16 16" fill="none" class="text-ink-2">
<path d="M3.5 7a4.5 4.5 0 119 0v3.5l1 1.5H2.5l1-1.5z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/>
<path d="M6.5 13a1.5 1.5 0 003 0" stroke="currentColor" stroke-width="1.3"/>
</svg>
<span class="absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-accent"></span>
</button>
<a href="#" class="btn-primary text-[12px] py-1.5 px-3.5">
<svg width="11" height="11" viewBox="0 0 12 12" fill="none">
<path d="M6 2v8M2 6h8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
</svg>
Neue Mitteilung
</a>
</div>
</div>
<!-- Inhalt -->
<div class="px-10 py-8 space-y-6">
<!-- ============== PAGE HEADER ============== -->
<header class="grid items-end gap-8" style="grid-template-columns:1fr auto;">
<div class="min-w-0">
<div class="flex items-center gap-3 mb-3 flex-nowrap whitespace-nowrap">
<span class="badge hub dot">User Backend</span>
<span class="eyebrow muted">Mein Bereich · A · 01</span>
</div>
<h1 class="text-[34px] font-bold tracking-[-0.7px] text-ink leading-[1.1] m-0">Mein Dashboard</h1>
<p class="text-[13.5px] text-ink-2 leading-[1.55] mt-2 m-0 max-w-[640px]">
Willkommen zurück, <strong class="font-semibold text-ink">Test User</strong>. Hier sehen Sie Status und Reichweite Ihres Kundenkontos für presseecho und businessportal24.
</p>
</div>
<div class="flex items-center gap-3 flex-shrink-0">
<!-- Zeitraum-Filter (FluxUI dropdown-style) -->
<button class="btn-secondary whitespace-nowrap">
<svg width="12" height="12" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3.5" width="11" height="10" stroke="currentColor" stroke-width="1.3"/><path d="M2.5 6h11M5 2v3M11 2v3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
Diese Woche
<svg width="10" height="10" viewBox="0 0 12 12" fill="none"><path d="M3 4.5l3 3 3-3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
<!-- Firma-zuordnen-Status — als Banner-Pille, statt im Karte-Header eingesperrt -->
<span class="inline-flex items-center gap-2 px-3 py-1.5 bg-warn-soft border border-warn/30 text-accent rounded-[4px] text-[12px] font-semibold whitespace-nowrap">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" class="flex-shrink-0"><path d="M8 2l6 11H2z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><path d="M8 7v3M8 11.5v.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
Keine Firma zugeordnet
<a href="#" class="underline underline-offset-[3px] decoration-accent/40 hover:decoration-accent">zuordnen →</a>
</span>
</div>
</header>
<!-- ============== STAT-CARDS — KPI-Reihe ============== -->
<section class="grid gap-4" style="grid-template-columns:repeat(4,1fr);">
<article class="stat-card is-primary">
<span class="stat-strip"></span>
<div class="flex items-baseline justify-between">
<div class="stat-label" style="color:#7B8FBF;">Gesamt</div>
<span class="text-[10.5px] font-mono text-ink-3 tracking-[0.14em] uppercase">2026</span>
</div>
<div class="stat-num">0</div>
<div class="mt-3 flex items-center gap-2 text-[11.5px] text-ink-3">
<span class="flex items-center gap-1">
<svg width="9" height="9" viewBox="0 0 10 10" fill="none"><path d="M5 8V2M2 5l3-3 3 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
0 ggü. Vormonat
</span>
</div>
<!-- Mini Sparkline placeholder -->
<svg class="absolute right-3 bottom-3 opacity-50" width="80" height="22" viewBox="0 0 80 22" fill="none">
<path d="M0 18 L10 17 L20 18 L30 16 L40 18 L50 15 L60 17 L70 14 L80 16" stroke="#1A2540" stroke-width="1.2" fill="none"/>
<circle cx="80" cy="16" r="1.8" fill="#1A2540"/>
</svg>
</article>
<article class="stat-card is-ok">
<span class="stat-strip"></span>
<div class="flex items-baseline justify-between">
<div class="stat-label">Veröffentlicht</div>
<span class="badge ok" style="font-size:9.5px;padding:1px 6px;">live</span>
</div>
<div class="stat-num" style="color:#6BD498;">0</div>
<div class="mt-3 flex items-center gap-2 text-[11.5px] text-ink-3">
auf beiden Portalen
</div>
<svg class="absolute right-3 bottom-3 opacity-50" width="80" height="22" viewBox="0 0 80 22" fill="none">
<path d="M0 19 L10 18 L20 19 L30 17 L40 16 L50 14 L60 13 L70 11 L80 10" stroke="#2E8540" stroke-width="1.2" fill="none"/>
<circle cx="80" cy="10" r="1.8" fill="#2E8540"/>
</svg>
</article>
<article class="stat-card is-warn">
<span class="stat-strip"></span>
<div class="flex items-baseline justify-between">
<div class="stat-label">In Prüfung</div>
<span class="badge warn" style="font-size:9.5px;padding:1px 6px;">Ø 4 h</span>
</div>
<div class="stat-num" style="color:#D9A560;">0</div>
<div class="mt-3 flex items-center gap-2 text-[11.5px] text-ink-3">
redaktionelle Prüfung
</div>
<svg class="absolute right-3 bottom-3 opacity-50" width="80" height="22" viewBox="0 0 80 22" fill="none">
<path d="M0 16 L10 17 L20 14 L30 18 L40 13 L50 17 L60 12 L70 15 L80 11" stroke="#A87A1F" stroke-width="1.2" fill="none"/>
<circle cx="80" cy="11" r="1.8" fill="#A87A1F"/>
</svg>
</article>
<article class="stat-card is-muted">
<span class="stat-strip"></span>
<div class="flex items-baseline justify-between">
<div class="stat-label">Entwürfe</div>
<span class="text-[10.5px] font-mono text-ink-3 tracking-[0.14em] uppercase">privat</span>
</div>
<div class="stat-num" style="color:#8E8B82;">0</div>
<div class="mt-3 flex items-center gap-2 text-[11.5px] text-ink-3">
gespeichert, nicht eingereicht
</div>
<svg class="absolute right-3 bottom-3 opacity-50" width="80" height="22" viewBox="0 0 80 22" fill="none">
<path d="M0 12 L10 12 L20 12 L30 12 L40 12 L50 12 L60 12 L70 12 L80 12" stroke="#8A918D" stroke-width="1.2" stroke-dasharray="2 2" fill="none"/>
</svg>
</article>
</section>
<!-- ============== ZWEISPALTEN-GRID ============== -->
<section class="grid gap-6" style="grid-template-columns:2fr 1fr;">
<!-- ============== LINKS: Pressemitteilungen-Liste / Empty ============== -->
<article class="panel">
<div class="panel-head">
<div class="flex items-center gap-3">
<span class="section-eyebrow">Meine letzten Pressemitteilungen</span>
</div>
<div class="flex items-center gap-3">
<span class="text-[11.5px] text-ink-3">0 von 0</span>
<a href="#" class="text-[12px] font-semibold text-hub hover:underline underline-offset-[3px] decoration-hub/30">Alle anzeigen →</a>
</div>
</div>
<!-- Empty State — sauber, ohne überlappende Skelett-Zeilen -->
<div class="px-10 py-14 flex flex-col items-center text-center">
<!-- Dezentes Tabellen-Icon -->
<div class="w-16 h-16 rounded-[6px] bg-hub-soft border border-hub-soft-2 flex items-center justify-center text-hub mb-5 relative">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none">
<rect x="3" y="4" width="14" height="16" stroke="currentColor" stroke-width="1.5"/>
<path d="M17 7h4v13H6" stroke="currentColor" stroke-width="1.5"/>
<path d="M6 8h8M6 11h8M6 14h5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
</svg>
<span class="absolute -top-1.5 -right-1.5 w-5 h-5 rounded-full bg-accent text-white text-[10px] font-bold flex items-center justify-center font-mono">0</span>
</div>
<div class="text-[16px] font-semibold text-ink m-0">Noch keine Pressemitteilungen</div>
<p class="text-[13px] text-ink-3 leading-[1.55] mt-2 m-0 max-w-[460px]">
Starten Sie mit einer ersten Mitteilung für die aktive Firma oder Ihr Kundenkonto. Veröffentlichung erfolgt nach redaktioneller Prüfung auf beiden Portalen.
</p>
<div class="mt-6 flex items-center gap-2.5">
<a href="#" class="btn-primary">
<svg width="11" height="11" viewBox="0 0 12 12" fill="none"><path d="M6 2v8M2 6h8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
Erste Pressemitteilung erstellen
</a>
<a href="#" class="btn-secondary">Vorlage öffnen</a>
</div>
<!-- Vorgeschlagene Schritte -->
<div class="mt-9 grid gap-3 w-full max-w-[560px]" style="grid-template-columns:repeat(3,1fr);">
<div class="text-left px-3 py-2.5 bg-bg-elev border border-bg-rule rounded-[3px]">
<div class="font-mono text-[9.5px] tracking-[0.16em] text-accent font-bold mb-1">01</div>
<div class="text-[11.5px] font-semibold text-ink leading-tight">Firma zuordnen</div>
</div>
<div class="text-left px-3 py-2.5 bg-bg-elev border border-bg-rule rounded-[3px]">
<div class="font-mono text-[9.5px] tracking-[0.16em] text-accent font-bold mb-1">02</div>
<div class="text-[11.5px] font-semibold text-ink leading-tight">Mitteilung verfassen</div>
</div>
<div class="text-left px-3 py-2.5 bg-bg-elev border border-bg-rule rounded-[3px]">
<div class="font-mono text-[9.5px] tracking-[0.16em] text-accent font-bold mb-1">03</div>
<div class="text-[11.5px] font-semibold text-ink leading-tight">Zur Prüfung senden</div>
</div>
</div>
</div>
<div class="px-5 py-3 bg-bg-elev border-t border-bg-rule flex items-center gap-2.5 text-[11.5px] text-ink-3">
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" class="text-hub flex-shrink-0">
<path d="M8 1.5l5.5 2.5v4c0 3.3-2.2 5.8-5.5 6.5C4.7 13.8 2.5 11.3 2.5 8V4z" stroke="currentColor" stroke-width="1.3" fill="none"/>
<path d="M5.5 8l2 2 3.5-4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Tipp: Geprüfte Mitteilungen erscheinen i. d. R. binnen <strong class="text-ink-2 font-semibold">4 Stunden</strong> werktags auf beiden Portalen.
</div>
</article>
<!-- ============== RECHTS: Datenqualität ============== -->
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">Datenqualität</span>
<span class="badge warn dot">2 offen</span>
</div>
<div class="px-5 py-5">
<p class="text-[12px] text-ink-3 leading-[1.55] m-0 mb-4">
Diese Hinweise helfen, Ihr User Backend vollständig und sauber zu halten.
</p>
<div class="space-y-3">
<div class="hint-card">
<span class="hint-ico">
<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="6" r="2.5" stroke="currentColor" stroke-width="1.4"/><path d="M3 13.5c.7-2.4 2.7-4 5-4s4.3 1.6 5 4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
</span>
<div class="min-w-0">
<div class="flex items-baseline justify-between gap-3">
<div class="text-[13px] font-semibold text-ink leading-tight">Profil unvollständig</div>
<span class="font-mono text-[10px] text-ink-3 tracking-[0.10em] uppercase whitespace-nowrap flex-shrink-0">60 %</span>
</div>
<div class="mt-2 h-1 w-full rounded-full bg-bg-rule-2 overflow-hidden">
<div class="h-full rounded-full bg-accent" style="width:60%;"></div>
</div>
<p class="text-[11.5px] text-ink-3 leading-[1.5] mt-2 m-0">
Vorname, Telefon und Pressekontakt fehlen für saubere Kundenakte.
</p>
<a href="#" class="inline-flex items-center gap-1 text-[11.5px] font-semibold text-accent mt-2 hover:underline underline-offset-[3px] decoration-accent/40">Profil öffnen →</a>
</div>
</div>
<div class="hint-card">
<span class="hint-ico">
<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3.5" width="11" height="10" stroke="currentColor" stroke-width="1.4"/><path d="M2.5 6h11" stroke="currentColor" stroke-width="1.4"/></svg>
</span>
<div class="min-w-0">
<div class="flex items-baseline justify-between gap-3">
<div class="text-[13px] font-semibold text-ink leading-tight">Rechnungsadresse fehlt</div>
<span class="font-mono text-[10px] text-ink-3 tracking-[0.10em] uppercase whitespace-nowrap flex-shrink-0">0 %</span>
</div>
<div class="mt-2 h-1 w-full rounded-full bg-bg-rule-2 overflow-hidden">
<div class="h-full rounded-full bg-accent" style="width:5%;"></div>
</div>
<p class="text-[11.5px] text-ink-3 leading-[1.5] mt-2 m-0">
Hinterlegen Sie eine Rechnungsadresse, damit spätere Buchungen sauber abgerechnet werden können.
</p>
<a href="#" class="inline-flex items-center gap-1 text-[11.5px] font-semibold text-accent mt-2 hover:underline underline-offset-[3px] decoration-accent/40">Rechnungsadresse ergänzen →</a>
</div>
</div>
</div>
</div>
</article>
</section>
<!-- ============== UNTERER GRID: FIRMEN + REICHWEITE / AKTIVITÄT ============== -->
<section class="grid gap-6" style="grid-template-columns:2fr 1fr;">
<!-- Firmen Card -->
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">Meine Firmen</span>
<div class="flex items-center gap-3">
<span class="badge hub" style="font-size:9.5px;padding:1px 6px;">0 zugeordnet</span>
<a href="#" class="text-[12px] font-semibold text-hub hover:underline underline-offset-[3px] decoration-hub/30">Profil &amp; Firma verwalten →</a>
</div>
</div>
<div class="p-6">
<div class="grid gap-3" style="grid-template-columns:1fr 1fr;">
<!-- Empty-Slot Firma 1 -->
<div class="relative border border-dashed border-bg-rule rounded-[5px] p-5 hover:border-hub/50 hover:bg-bg-elev transition-colors">
<div class="flex items-center gap-3 mb-3">
<span class="w-10 h-10 rounded-[4px] border border-bg-rule bg-bg-elev flex items-center justify-center text-ink-4">
<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3.5" width="11" height="10" stroke="currentColor" stroke-width="1.3"/><path d="M2.5 6h11M6 9h1M9 9h1M6 11.5h1M9 11.5h1" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</span>
<div>
<div class="text-[13.5px] font-semibold text-ink-2">Firma hinzufügen</div>
<div class="text-[11px] text-ink-3 mt-0.5">Slot frei · 1 von 3</div>
</div>
</div>
<p class="text-[11.5px] text-ink-3 leading-[1.5] m-0">
Pressestellen, für die Sie Mitteilungen erstellen — mit eigenem Logo, Kontaktperson und Themen-Tags.
</p>
<a href="#" class="absolute top-3 right-3 inline-flex items-center justify-center w-7 h-7 rounded-[3px] bg-bg-card border border-bg-rule text-hub hover:border-hub">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M6 2v8M2 6h8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
</a>
</div>
<!-- Hinweis -->
<div class="bg-bg-elev border border-bg-rule rounded-[5px] p-5">
<div class="eyebrow muted mb-2">Hinweis</div>
<div class="text-[13px] text-ink-2 leading-[1.55] m-0">
Keine Firmen zugeordnet. Wenn hier eine Firma fehlen sollte, prüfen Sie bitte Ihr Profil oder wenden Sie sich an den Support.
</div>
<div class="mt-3 flex items-center gap-2">
<a href="#" class="btn-secondary" style="padding:6px 10px;font-size:11.5px;">Profil prüfen</a>
<a href="/cdn-cgi/l/email-protection#e5969095958a9791a5959780969680958a9791848980cb868a88" class="text-[11.5px] font-semibold text-ink-3 hover:text-hub">Support kontaktieren</a>
</div>
</div>
</div>
</div>
</article>
<!-- Aktivität / Bridge -->
<article class="panel-dark">
<div class="panel-head">
<span class="eyebrow on-dark">Brand-Bridge</span>
<span class="font-mono text-[10px] text-ink-on-dark-3 tracking-[0.14em] uppercase">A · B</span>
</div>
<div class="px-5 py-5">
<div class="text-[12.5px] leading-[1.55] text-ink-on-dark-2 m-0 mb-4">
Ein Konto, beide Portale — Veröffentlichungen werden parallel auf presseecho und businessportal24 ausgespielt.
</div>
<div class="grid gap-3" style="grid-template-columns:1fr 1fr;">
<div class="rounded-[4px] px-3.5 py-3 border border-white/5" style="background:#1A2540;">
<div class="flex items-center gap-2 mb-1.5">
<span class="dot-pe"></span>
<span class="text-[11px] font-bold tracking-[0.14em] uppercase text-white/85">presseecho</span>
</div>
<div class="font-mono text-[15px] font-semibold text-white" style="font-variant-numeric:tabular-nums;">verbunden</div>
<div class="text-[10.5px] text-ink-on-dark-3 mt-0.5">Archiv · Branchen-Tiefe</div>
</div>
<div class="rounded-[4px] px-3.5 py-3 border border-white/5" style="background:#1A2540;">
<div class="flex items-center gap-2 mb-1.5">
<span class="dot-bp"></span>
<span class="text-[11px] font-bold tracking-[0.14em] uppercase text-white/85">businessportal24</span>
</div>
<div class="font-mono text-[15px] font-semibold text-white" style="font-variant-numeric:tabular-nums;">verbunden</div>
<div class="text-[10.5px] text-ink-on-dark-3 mt-0.5">Wirtschaft · Live</div>
</div>
</div>
<hr class="mt-5 mb-4" style="border:0;height:1px;background:rgba(255,255,255,.10);" />
<div class="space-y-2 text-[11.5px] text-ink-on-dark-2">
<div class="flex items-center justify-between">
<span>API-Status</span>
<span class="flex items-center gap-1.5 text-white"><span class="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse"></span>operational</span>
</div>
<div class="flex items-center justify-between">
<span>Letzte Synchronisation</span>
<span class="font-mono text-white">vor 2 min</span>
</div>
<div class="flex items-center justify-between">
<span>Tarif</span>
<span class="font-mono text-white">Starter</span>
</div>
</div>
<a href="Hub Landing presseportale.html#tarife" class="mt-5 inline-flex items-center gap-1.5 text-[11.5px] font-semibold text-white hover:underline underline-offset-[3px] decoration-white/30">
Tarife &amp; Add-ons ansehen →
</a>
</div>
</article>
</section>
<!-- ============== FUSSZEILE ============== -->
<footer class="flex items-center justify-between pt-4 pb-2 text-[11px] text-ink-3 border-t border-bg-rule">
<span>© 2026 presseportale.com · Publisher-Hub</span>
<span class="flex items-center gap-5">
<a href="#" class="hover:text-hub">Tastenkürzel</a>
<a href="#" class="hover:text-hub">Changelog</a>
<a href="#" class="hover:text-hub">Statusseite</a>
<a href="/cdn-cgi/l/email-protection#21525451514e535561515344525244514e5355404d440f424e4c" class="hover:text-hub">Support</a>
</span>
</footer>
</div>
</main>
</div>
</div>
<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script></body>
</html>

View file

@ -0,0 +1,764 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>presseportale.com — Mein Dashboard</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
<script src="https://cdn.tailwindcss.com?plugins=forms"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
bg: "#F6F4EF",
"bg-elev": "#FBFAF6",
"bg-rule": "#E2DDD0",
"bg-rule-2": "#EDE7D7",
"bg-card": "#FFFFFF",
"bg-card-warm": "#EFEADC",
"hub": "#1A2540",
"hub-2": "#243152",
"hub-3": "#2E3D66",
"hub-soft": "#E5E9F1",
"hub-soft-2":"#CFD6E4",
"hub-line": "#7B8FBF",
"accent": "#B07A3A",
"accent-deep": "#8A5E27",
"accent-soft": "#F1E6D3",
ink: "#1A1F1C",
"ink-2":"#3A413D",
"ink-3":"#5A6360",
"ink-4":"#8A918D",
"ink-on-dark": "#F6F4EF",
"ink-on-dark-2": "#B2B9C7",
"ink-on-dark-3": "#7B8FBF",
ok: "#2E8540",
"ok-soft":"#E2F1E5",
warn: "#A87A1F",
"warn-soft":"#F6EAC8",
err: "#A8331F",
"err-soft":"#F4DAD2",
},
fontFamily: {
sans: ['"Inter Tight"','Inter','system-ui','sans-serif'],
mono: ['"JetBrains Mono"','"SF Mono"','ui-monospace','monospace'],
},
backgroundImage: {
"hub-grad": "linear-gradient(135deg,#1A2540 0%,#243152 100%)",
"hub-grad-2": "linear-gradient(180deg,#1A2540 0%,#0F1729 100%)",
"accent-grad": "linear-gradient(135deg,#B07A3A 0%,#8A5E27 100%)",
}
}
}
};
</script>
<style>
html,body { margin:0; padding:0; }
body { background:#E8E4DA; font-family:"Inter Tight",system-ui,sans-serif; }
.eyebrow {
font-size: 10.5px; font-weight: 700;
letter-spacing: 0.20em; text-transform: uppercase;
color: #1A2540;
}
.eyebrow.muted { color:#5A6360; letter-spacing:0.16em; font-weight:600; font-size:10px; }
.eyebrow.accent { color:#8A5E27; }
.eyebrow.on-dark { color:#7B8FBF; }
.section-eyebrow {
display:inline-flex; align-items:center; gap:10px;
font-size: 10.5px; font-weight: 700;
letter-spacing: 0.22em; text-transform: uppercase;
color:#1A2540;
}
.section-eyebrow::after {
content:""; display:block; width:30px; height:1px;
background:#1A2540; opacity:.45;
}
.rule { height:1px; background:#E2DDD0; border:0; margin:0; }
.rule-strong { height:1px; background:#1A1F1C; border:0; margin:0; }
/* Sidebar */
.nav-item {
display:flex; align-items:center; gap:11px;
padding:8px 12px;
border-radius:4px;
font-size:13px; font-weight:500;
color:#3A413D;
transition: background .12s, color .12s;
position:relative;
}
.nav-item:hover { background:#F6F4EF; color:#1A2540; }
.nav-item.active {
background:#E5E9F1;
color:#1A2540;
font-weight:600;
}
.nav-item.active::before {
content:""; position:absolute; left:-1px; top:6px; bottom:6px;
width:2px; background:#1A2540; border-radius:0 2px 2px 0;
}
.nav-item.disabled { color:#8A918D; cursor:default; }
.nav-item.disabled:hover { background:transparent; color:#8A918D; }
.nav-item .ico {
width:16px; height:16px; flex-shrink:0;
color: currentColor; opacity:.8;
}
.nav-item.active .ico { opacity:1; }
.nav-section {
font-size:10px; font-weight:700; letter-spacing:0.18em;
text-transform:uppercase; color:#8A918D;
padding: 0 12px 6px;
}
/* Card / panel headers */
.panel {
background:#FFFFFF; border:1px solid #E2DDD0; border-radius:6px;
}
.panel-warm { background:#FBFAF6; border:1px solid #E2DDD0; border-radius:6px; }
.panel-dark { background:#1A2540; border:1px solid #0F1729; border-radius:6px; color:#F6F4EF; }
.panel-head {
display:flex; align-items:center; justify-content:space-between;
padding:14px 20px;
border-bottom:1px solid #E2DDD0;
}
.panel-dark .panel-head { border-bottom-color: rgba(255,255,255,.10); }
/* Stat cards */
.stat-card {
position:relative;
background:#FFFFFF; border:1px solid #E2DDD0; border-radius:6px;
padding:18px 20px;
transition: border-color .12s, box-shadow .12s;
}
.stat-card .stat-strip {
position:absolute; left:0; top:0; bottom:0; width:3px;
background:#CFD6E4; border-radius:6px 0 0 6px;
}
.stat-card.is-primary .stat-strip { background:#1A2540; }
.stat-card.is-ok .stat-strip { background:#2E8540; }
.stat-card.is-warn .stat-strip { background:#A87A1F; }
.stat-card.is-muted .stat-strip { background:#8A918D; }
.stat-label {
font-size:10.5px; font-weight:700;
letter-spacing:0.16em; text-transform:uppercase;
color:#5A6360;
}
.stat-card.is-ok .stat-label { color:#2E8540; }
.stat-card.is-warn .stat-label { color:#A87A1F; }
.stat-card.is-muted .stat-label { color:#8A918D; }
.stat-num {
font-family:"JetBrains Mono","SF Mono",ui-monospace,monospace;
font-variant-numeric:tabular-nums;
font-size:36px; font-weight:600; color:#1A1F1C;
letter-spacing:-0.5px; line-height:1;
margin-top:14px;
}
/* Hint chips (Datenqualität) */
.hint-card {
display:grid; gap:14px;
grid-template-columns: auto 1fr;
align-items:start;
background:#FBFAF6; border:1px solid #E2DDD0; border-left:3px solid #B07A3A;
border-radius:5px;
padding:16px 18px;
}
.hint-card .hint-ico {
width:36px; height:36px; border-radius:4px;
background:#F1E6D3; color:#8A5E27;
display:flex; align-items:center; justify-content:center;
flex-shrink:0;
}
/* FluxUI-style standard button (the "blue" one in screenshots) — kept, but harmonised */
.btn-primary {
display:inline-flex; align-items:center; gap:8px; justify-content:center;
padding:9px 16px;
background:#1A2540; color:#FFFFFF;
border-radius:4px; font-size:13px; font-weight:600;
transition: background .15s;
}
.btn-primary:hover { background:#243152; }
.btn-secondary {
display:inline-flex; align-items:center; gap:8px; justify-content:center;
padding:8px 14px;
background:#FFFFFF; color:#1A2540;
border:1px solid #CFD6E4; border-radius:4px;
font-size:12.5px; font-weight:600;
transition: border-color .15s, background .15s;
}
.btn-secondary:hover { border-color:#1A2540; background:#F6F4EF; }
.badge {
display:inline-flex; align-items:center; gap:6px;
padding:3px 9px; border-radius:99px;
font-size:10.5px; font-weight:700;
letter-spacing:0.10em; text-transform:uppercase;
}
.badge.warn { background:#F6EAC8; color:#8A5E27; }
.badge.ok { background:#E2F1E5; color:#1F5E2E; }
.badge.hub { background:#E5E9F1; color:#1A2540; }
.badge.dot::before {
content:""; width:6px; height:6px; border-radius:99px; background:currentColor;
}
/* Bridge ribbon - sehr subtil */
.bridge-row {
display:inline-flex; align-items:center; gap:6px;
font-family:"JetBrains Mono","SF Mono",ui-monospace,monospace;
font-size:10.5px; letter-spacing:0.10em; text-transform:uppercase;
color:#5A6360;
}
.dot-pe { width:6px; height:6px; border-radius:99px; background:#1F4D3A; }
.dot-bp { width:6px; height:6px; border-radius:99px; background:#C84A1E; }
</style>
</head>
<body class="bg-bg text-ink font-sans antialiased">
<!-- ============== ARTBOARD ============== -->
<div class="mx-auto bg-bg" style="width:1440px;">
<div class="flex" style="min-height:980px;">
<!-- =============================================
SIDEBAR — schmal, warm, klare Hierarchie
(FluxUI <flux:navlist> + Slot-Header-Styles)
============================================== -->
<aside class="bg-bg-elev border-r border-bg-rule flex flex-col" style="width:260px;">
<!-- Brand-Block -->
<div class="px-5 pt-6 pb-5">
<a href="Hub Landing presseportale.html" class="flex items-baseline gap-2">
<span class="text-[19px] font-bold tracking-[-0.4px] text-hub leading-none">presseportale<span class="text-accent">.com</span></span>
</a>
<div class="eyebrow muted mt-2">Publisher · Hub</div>
<!-- Aktive Firma / Konto-Switcher (FluxUI-Slot: header before nav) -->
<button class="mt-4 w-full grid items-center gap-2.5 px-3 py-2.5 bg-white border border-bg-rule rounded-[4px] hover:border-hub/40 text-left"
style="grid-template-columns:auto 1fr auto;">
<span class="w-7 h-7 rounded-[3px] bg-hub-soft border border-hub-soft-2 flex items-center justify-center text-hub text-[11px] font-bold">TU</span>
<span class="min-w-0">
<span class="block text-[12.5px] font-semibold text-ink leading-tight truncate">Test User</span>
<span class="block text-[10.5px] text-ink-3 leading-tight mt-0.5 truncate">Keine Firma zugeordnet</span>
</span>
<svg width="11" height="11" viewBox="0 0 12 12" fill="none" class="text-ink-3">
<path d="M3 4.5l3 3 3-3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 7.5l3-3 3 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" opacity="0.4"/>
</svg>
</button>
</div>
<nav class="px-3 flex-1">
<div class="nav-section">Mein Bereich</div>
<div class="space-y-0.5 mb-5">
<a class="nav-item active" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M2 7l6-5 6 5v7H2z" stroke="currentColor" stroke-width="1.4"/><path d="M6 14V9h4v5" stroke="currentColor" stroke-width="1.4"/></svg>
Übersicht
</a>
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="2.5" width="9" height="11" stroke="currentColor" stroke-width="1.4"/><path d="M11.5 5h2v8.5H4" stroke="currentColor" stroke-width="1.4"/><path d="M5 5.5h4M5 8h4M5 10.5h2.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
Meine Pressemitteilungen
<span class="badge hub ml-auto" style="font-size:9.5px;padding:1px 6px;letter-spacing:0.08em;">0</span>
</a>
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3.5" width="11" height="10" stroke="currentColor" stroke-width="1.4"/><path d="M2.5 6h11" stroke="currentColor" stroke-width="1.4"/><path d="M6 9h1M9 9h1M6 11.5h1M9 11.5h1" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
Firmen
</a>
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M3 5h10l-1 9H4z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><path d="M6 5V3.5a2 2 0 014 0V5" stroke="currentColor" stroke-width="1.4"/></svg>
Buchungen &amp; Add-ons
</a>
<span class="nav-item disabled">
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M3 13V8M7 13V5M11 13V9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
Statistiken
<span class="ml-auto text-[9.5px] tracking-[0.14em] uppercase font-semibold text-ink-4">bald</span>
</span>
</div>
<div class="nav-section">Finanzen</div>
<div class="space-y-0.5 mb-5">
<span class="nav-item disabled">
<svg class="ico" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="5" stroke="currentColor" stroke-width="1.4"/><path d="M8 5.5v5M6 8h4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
Credits &amp; Tarif
<span class="ml-auto text-[9.5px] tracking-[0.14em] uppercase font-semibold text-ink-4">bald</span>
</span>
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M3 2.5h7l3 3v8H3z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><path d="M10 2.5V5.5h3" stroke="currentColor" stroke-width="1.4"/><path d="M5.5 8h5M5.5 10.5h5M5.5 6h2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
Rechnungen
</a>
<span class="nav-item disabled">
<svg class="ico" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="4.5" width="11" height="7.5" stroke="currentColor" stroke-width="1.4"/><path d="M2.5 7h11" stroke="currentColor" stroke-width="1.4"/></svg>
Zahlungsarten
<span class="ml-auto text-[9.5px] tracking-[0.14em] uppercase font-semibold text-ink-4">bald</span>
</span>
</div>
<div class="nav-section">Konto</div>
<div class="space-y-0.5 mb-5">
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="6" r="2.5" stroke="currentColor" stroke-width="1.4"/><path d="M3 13.5c.7-2.4 2.7-4 5-4s4.3 1.6 5 4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
Profil
</a>
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M8 2l5 2v4c0 3-2 5-5 6-3-1-5-3-5-6V4z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><path d="M6 8l1.5 1.5L10.5 6" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
Sicherheit
</a>
<a class="nav-item" href="#">
<svg class="ico" viewBox="0 0 16 16" fill="none"><circle cx="6" cy="8" r="2.5" stroke="currentColor" stroke-width="1.4"/><path d="M8.5 8h5M11 8v2.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
API &amp; Integrationen
</a>
<span class="nav-item disabled">
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M3.5 7a4.5 4.5 0 119 0v3.5l1 1.5H2.5l1-1.5z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><path d="M6.5 13a1.5 1.5 0 003 0" stroke="currentColor" stroke-width="1.4"/></svg>
Benachrichtigungen
<span class="ml-auto text-[9.5px] tracking-[0.14em] uppercase font-semibold text-ink-4">bald</span>
</span>
</div>
</nav>
<!-- Testmodus-Block (war im Original „Zurück zum Admin") -->
<div class="px-4 pb-4">
<div class="bg-hub text-ink-on-dark rounded-[5px] p-4 relative overflow-hidden">
<div class="absolute -top-6 -right-6 w-16 h-16 rounded-full bg-hub-3 opacity-50"></div>
<div class="absolute -bottom-8 -left-8 w-20 h-20 rounded-full bg-hub-3 opacity-30"></div>
<div class="relative">
<div class="flex items-center gap-2 mb-2">
<span class="w-1.5 h-1.5 rounded-full bg-accent animate-pulse"></span>
<span class="eyebrow on-dark" style="color:#F4D89C;">Testmodus aktiv</span>
</div>
<div class="text-[12px] leading-[1.5] text-ink-on-dark-2">
Angemeldet als <strong class="text-white font-semibold">Test User</strong>.<br/>
Admin: <strong class="text-white font-semibold">Portal Admin</strong>
</div>
<button class="mt-3 w-full px-3 py-2 bg-white text-hub text-[12px] font-semibold rounded-[3px] hover:bg-bg transition-colors flex items-center justify-center gap-1.5">
<svg width="11" height="11" viewBox="0 0 12 12" fill="none">
<path d="M9 3L3 9M3 9H8M3 9V4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
Zurück zum Admin
</button>
</div>
</div>
</div>
<!-- Resources -->
<div class="px-3 pb-5 border-t border-bg-rule pt-4">
<div class="nav-section">Resources</div>
<div class="space-y-0.5">
<a class="nav-item" href="#"><svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M3 11l4 2 6-9-4-1z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>Tailwind CSS</a>
<a class="nav-item" href="#"><svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M8 2l5 2v4c0 3-2 5-5 6-3-1-5-3-5-6V4z" stroke="currentColor" stroke-width="1.3"/></svg>Hero Icons</a>
<a class="nav-item" href="#"><svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M9 2L4 9h3l-1 5 5-7H8z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/></svg>Flux UI</a>
<a class="nav-item" href="#"><svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M3 4l5-2 5 2v8l-5 2-5-2z" stroke="currentColor" stroke-width="1.3"/><path d="M3 4l5 2 5-2M8 6v8" stroke="currentColor" stroke-width="1.3"/></svg>Repository</a>
</div>
</div>
</aside>
<!-- =============================================
MAIN — Inhalt
============================================== -->
<main class="flex-1 min-w-0">
<!-- Topbar — sehr schlank; Brücken-Kontext + Kontoinfo rechts -->
<div class="bg-bg-elev border-b border-bg-rule">
<div class="px-10 py-3 flex items-center gap-6">
<!-- Breadcrumb -->
<div class="flex items-center gap-2 text-[12px] text-ink-3 font-medium">
<a href="Hub Landing presseportale.html" class="hover:text-hub">Hub</a>
<span class="text-ink-4">/</span>
<span class="text-ink-2">User Backend</span>
<span class="text-ink-4">/</span>
<span class="text-hub font-semibold">Übersicht</span>
</div>
<span class="flex-1"></span>
<!-- Reichweiten-Kontext (Brücke zur Hub-CI) -->
<span class="bridge-row">
<span class="dot-pe"></span> presseecho
<span class="text-ink-4 mx-1">·</span>
<span class="dot-bp"></span> businessportal24
</span>
<span class="w-px h-5 bg-bg-rule"></span>
<!-- Search (FluxUI input slot) -->
<div class="relative">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" class="absolute left-2.5 top-1/2 -translate-y-1/2 text-ink-3">
<circle cx="7" cy="7" r="4.5" stroke="currentColor" stroke-width="1.3"/>
<path d="M10.5 10.5L13 13" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
</svg>
<input class="pl-8 pr-3 py-1.5 w-[220px] bg-white border border-bg-rule rounded-[4px] text-[12.5px] placeholder:text-ink-4 focus:outline-none focus:border-hub" placeholder="Suchen…" />
<span class="absolute right-2 top-1/2 -translate-y-1/2 text-[10px] font-mono text-ink-4 border border-bg-rule rounded px-1">⌘K</span>
</div>
<button class="relative w-8 h-8 flex items-center justify-center rounded-[4px] hover:bg-bg border border-transparent hover:border-bg-rule">
<svg width="15" height="15" viewBox="0 0 16 16" fill="none" class="text-ink-2">
<path d="M3.5 7a4.5 4.5 0 119 0v3.5l1 1.5H2.5l1-1.5z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/>
<path d="M6.5 13a1.5 1.5 0 003 0" stroke="currentColor" stroke-width="1.3"/>
</svg>
<span class="absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-accent"></span>
</button>
<a href="#" class="btn-primary text-[12px] py-1.5 px-3.5">
<svg width="11" height="11" viewBox="0 0 12 12" fill="none">
<path d="M6 2v8M2 6h8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
</svg>
Neue Mitteilung
</a>
</div>
</div>
<!-- Inhalt -->
<div class="px-10 py-8 space-y-6">
<!-- ============== PAGE HEADER ============== -->
<header class="grid items-end gap-8" style="grid-template-columns:1fr auto;">
<div class="min-w-0">
<div class="flex items-center gap-3 mb-3 flex-nowrap whitespace-nowrap">
<span class="badge hub dot">User Backend</span>
<span class="eyebrow muted">Mein Bereich · A · 01</span>
</div>
<h1 class="text-[34px] font-bold tracking-[-0.7px] text-ink leading-[1.1] m-0">Mein Dashboard</h1>
<p class="text-[13.5px] text-ink-2 leading-[1.55] mt-2 m-0 max-w-[640px]">
Willkommen zurück, <strong class="font-semibold text-ink">Test User</strong>. Hier sehen Sie Status und Reichweite Ihres Kundenkontos für presseecho und businessportal24.
</p>
</div>
<div class="flex items-center gap-3 flex-shrink-0">
<!-- Zeitraum-Filter (FluxUI dropdown-style) -->
<button class="btn-secondary whitespace-nowrap">
<svg width="12" height="12" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3.5" width="11" height="10" stroke="currentColor" stroke-width="1.3"/><path d="M2.5 6h11M5 2v3M11 2v3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
Diese Woche
<svg width="10" height="10" viewBox="0 0 12 12" fill="none"><path d="M3 4.5l3 3 3-3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
<!-- Firma-zuordnen-Status — als Banner-Pille, statt im Karte-Header eingesperrt -->
<span class="inline-flex items-center gap-2 px-3 py-1.5 bg-warn-soft border border-warn/30 text-accent-deep rounded-[4px] text-[12px] font-semibold whitespace-nowrap">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" class="flex-shrink-0"><path d="M8 2l6 11H2z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/><path d="M8 7v3M8 11.5v.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
Keine Firma zugeordnet
<a href="#" class="underline underline-offset-[3px] decoration-accent-deep/40 hover:decoration-accent-deep">zuordnen →</a>
</span>
</div>
</header>
<!-- ============== STAT-CARDS — KPI-Reihe ============== -->
<section class="grid gap-4" style="grid-template-columns:repeat(4,1fr);">
<article class="stat-card is-primary">
<span class="stat-strip"></span>
<div class="flex items-baseline justify-between">
<div class="stat-label" style="color:#1A2540;">Gesamt</div>
<span class="text-[10.5px] font-mono text-ink-3 tracking-[0.14em] uppercase">2026</span>
</div>
<div class="stat-num">0</div>
<div class="mt-3 flex items-center gap-2 text-[11.5px] text-ink-3">
<span class="flex items-center gap-1">
<svg width="9" height="9" viewBox="0 0 10 10" fill="none"><path d="M5 8V2M2 5l3-3 3 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
0 ggü. Vormonat
</span>
</div>
<!-- Mini Sparkline placeholder -->
<svg class="absolute right-3 bottom-3 opacity-50" width="80" height="22" viewBox="0 0 80 22" fill="none">
<path d="M0 18 L10 17 L20 18 L30 16 L40 18 L50 15 L60 17 L70 14 L80 16" stroke="#1A2540" stroke-width="1.2" fill="none"/>
<circle cx="80" cy="16" r="1.8" fill="#1A2540"/>
</svg>
</article>
<article class="stat-card is-ok">
<span class="stat-strip"></span>
<div class="flex items-baseline justify-between">
<div class="stat-label">Veröffentlicht</div>
<span class="badge ok" style="font-size:9.5px;padding:1px 6px;">live</span>
</div>
<div class="stat-num" style="color:#1F5E2E;">0</div>
<div class="mt-3 flex items-center gap-2 text-[11.5px] text-ink-3">
auf beiden Portalen
</div>
<svg class="absolute right-3 bottom-3 opacity-50" width="80" height="22" viewBox="0 0 80 22" fill="none">
<path d="M0 19 L10 18 L20 19 L30 17 L40 16 L50 14 L60 13 L70 11 L80 10" stroke="#2E8540" stroke-width="1.2" fill="none"/>
<circle cx="80" cy="10" r="1.8" fill="#2E8540"/>
</svg>
</article>
<article class="stat-card is-warn">
<span class="stat-strip"></span>
<div class="flex items-baseline justify-between">
<div class="stat-label">In Prüfung</div>
<span class="badge warn" style="font-size:9.5px;padding:1px 6px;">Ø 4 h</span>
</div>
<div class="stat-num" style="color:#8A5E27;">0</div>
<div class="mt-3 flex items-center gap-2 text-[11.5px] text-ink-3">
redaktionelle Prüfung
</div>
<svg class="absolute right-3 bottom-3 opacity-50" width="80" height="22" viewBox="0 0 80 22" fill="none">
<path d="M0 16 L10 17 L20 14 L30 18 L40 13 L50 17 L60 12 L70 15 L80 11" stroke="#A87A1F" stroke-width="1.2" fill="none"/>
<circle cx="80" cy="11" r="1.8" fill="#A87A1F"/>
</svg>
</article>
<article class="stat-card is-muted">
<span class="stat-strip"></span>
<div class="flex items-baseline justify-between">
<div class="stat-label">Entwürfe</div>
<span class="text-[10.5px] font-mono text-ink-3 tracking-[0.14em] uppercase">privat</span>
</div>
<div class="stat-num" style="color:#5A6360;">0</div>
<div class="mt-3 flex items-center gap-2 text-[11.5px] text-ink-3">
gespeichert, nicht eingereicht
</div>
<svg class="absolute right-3 bottom-3 opacity-50" width="80" height="22" viewBox="0 0 80 22" fill="none">
<path d="M0 12 L10 12 L20 12 L30 12 L40 12 L50 12 L60 12 L70 12 L80 12" stroke="#8A918D" stroke-width="1.2" stroke-dasharray="2 2" fill="none"/>
</svg>
</article>
</section>
<!-- ============== ZWEISPALTEN-GRID ============== -->
<section class="grid gap-6" style="grid-template-columns:2fr 1fr;">
<!-- ============== LINKS: Pressemitteilungen-Liste / Empty ============== -->
<article class="panel">
<div class="panel-head">
<div class="flex items-center gap-3">
<span class="section-eyebrow">Meine letzten Pressemitteilungen</span>
</div>
<div class="flex items-center gap-3">
<span class="text-[11.5px] text-ink-3">0 von 0</span>
<a href="#" class="text-[12px] font-semibold text-hub hover:underline underline-offset-[3px] decoration-hub/30">Alle anzeigen →</a>
</div>
</div>
<!-- Empty State — sauber, ohne überlappende Skelett-Zeilen -->
<div class="px-10 py-14 flex flex-col items-center text-center">
<!-- Dezentes Tabellen-Icon -->
<div class="w-16 h-16 rounded-[6px] bg-hub-soft border border-hub-soft-2 flex items-center justify-center text-hub mb-5 relative">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none">
<rect x="3" y="4" width="14" height="16" stroke="currentColor" stroke-width="1.5"/>
<path d="M17 7h4v13H6" stroke="currentColor" stroke-width="1.5"/>
<path d="M6 8h8M6 11h8M6 14h5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
</svg>
<span class="absolute -top-1.5 -right-1.5 w-5 h-5 rounded-full bg-accent text-white text-[10px] font-bold flex items-center justify-center font-mono">0</span>
</div>
<div class="text-[16px] font-semibold text-ink m-0">Noch keine Pressemitteilungen</div>
<p class="text-[13px] text-ink-3 leading-[1.55] mt-2 m-0 max-w-[460px]">
Starten Sie mit einer ersten Mitteilung für die aktive Firma oder Ihr Kundenkonto. Veröffentlichung erfolgt nach redaktioneller Prüfung auf beiden Portalen.
</p>
<div class="mt-6 flex items-center gap-2.5">
<a href="#" class="btn-primary">
<svg width="11" height="11" viewBox="0 0 12 12" fill="none"><path d="M6 2v8M2 6h8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
Erste Pressemitteilung erstellen
</a>
<a href="#" class="btn-secondary">Vorlage öffnen</a>
</div>
<!-- Vorgeschlagene Schritte -->
<div class="mt-9 grid gap-3 w-full max-w-[560px]" style="grid-template-columns:repeat(3,1fr);">
<div class="text-left px-3 py-2.5 bg-bg-elev border border-bg-rule rounded-[3px]">
<div class="font-mono text-[9.5px] tracking-[0.16em] text-accent font-bold mb-1">01</div>
<div class="text-[11.5px] font-semibold text-ink leading-tight">Firma zuordnen</div>
</div>
<div class="text-left px-3 py-2.5 bg-bg-elev border border-bg-rule rounded-[3px]">
<div class="font-mono text-[9.5px] tracking-[0.16em] text-accent font-bold mb-1">02</div>
<div class="text-[11.5px] font-semibold text-ink leading-tight">Mitteilung verfassen</div>
</div>
<div class="text-left px-3 py-2.5 bg-bg-elev border border-bg-rule rounded-[3px]">
<div class="font-mono text-[9.5px] tracking-[0.16em] text-accent font-bold mb-1">03</div>
<div class="text-[11.5px] font-semibold text-ink leading-tight">Zur Prüfung senden</div>
</div>
</div>
</div>
<div class="px-5 py-3 bg-bg-elev border-t border-bg-rule flex items-center gap-2.5 text-[11.5px] text-ink-3">
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" class="text-hub flex-shrink-0">
<path d="M8 1.5l5.5 2.5v4c0 3.3-2.2 5.8-5.5 6.5C4.7 13.8 2.5 11.3 2.5 8V4z" stroke="currentColor" stroke-width="1.3" fill="none"/>
<path d="M5.5 8l2 2 3.5-4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Tipp: Geprüfte Mitteilungen erscheinen i. d. R. binnen <strong class="text-ink-2 font-semibold">4 Stunden</strong> werktags auf beiden Portalen.
</div>
</article>
<!-- ============== RECHTS: Datenqualität ============== -->
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">Datenqualität</span>
<span class="badge warn dot">2 offen</span>
</div>
<div class="px-5 py-5">
<p class="text-[12px] text-ink-3 leading-[1.55] m-0 mb-4">
Diese Hinweise helfen, Ihr User Backend vollständig und sauber zu halten.
</p>
<div class="space-y-3">
<div class="hint-card">
<span class="hint-ico">
<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="6" r="2.5" stroke="currentColor" stroke-width="1.4"/><path d="M3 13.5c.7-2.4 2.7-4 5-4s4.3 1.6 5 4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
</span>
<div class="min-w-0">
<div class="flex items-baseline justify-between gap-3">
<div class="text-[13px] font-semibold text-ink leading-tight">Profil unvollständig</div>
<span class="font-mono text-[10px] text-ink-3 tracking-[0.10em] uppercase whitespace-nowrap flex-shrink-0">60 %</span>
</div>
<div class="mt-2 h-1 w-full rounded-full bg-bg-rule-2 overflow-hidden">
<div class="h-full rounded-full bg-accent" style="width:60%;"></div>
</div>
<p class="text-[11.5px] text-ink-3 leading-[1.5] mt-2 m-0">
Vorname, Telefon und Pressekontakt fehlen für saubere Kundenakte.
</p>
<a href="#" class="inline-flex items-center gap-1 text-[11.5px] font-semibold text-accent-deep mt-2 hover:underline underline-offset-[3px] decoration-accent-deep/40">Profil öffnen →</a>
</div>
</div>
<div class="hint-card">
<span class="hint-ico">
<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3.5" width="11" height="10" stroke="currentColor" stroke-width="1.4"/><path d="M2.5 6h11" stroke="currentColor" stroke-width="1.4"/></svg>
</span>
<div class="min-w-0">
<div class="flex items-baseline justify-between gap-3">
<div class="text-[13px] font-semibold text-ink leading-tight">Rechnungsadresse fehlt</div>
<span class="font-mono text-[10px] text-ink-3 tracking-[0.10em] uppercase whitespace-nowrap flex-shrink-0">0 %</span>
</div>
<div class="mt-2 h-1 w-full rounded-full bg-bg-rule-2 overflow-hidden">
<div class="h-full rounded-full bg-accent" style="width:5%;"></div>
</div>
<p class="text-[11.5px] text-ink-3 leading-[1.5] mt-2 m-0">
Hinterlegen Sie eine Rechnungsadresse, damit spätere Buchungen sauber abgerechnet werden können.
</p>
<a href="#" class="inline-flex items-center gap-1 text-[11.5px] font-semibold text-accent-deep mt-2 hover:underline underline-offset-[3px] decoration-accent-deep/40">Rechnungsadresse ergänzen →</a>
</div>
</div>
</div>
</div>
</article>
</section>
<!-- ============== UNTERER GRID: FIRMEN + REICHWEITE / AKTIVITÄT ============== -->
<section class="grid gap-6" style="grid-template-columns:2fr 1fr;">
<!-- Firmen Card -->
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">Meine Firmen</span>
<div class="flex items-center gap-3">
<span class="badge hub" style="font-size:9.5px;padding:1px 6px;">0 zugeordnet</span>
<a href="#" class="text-[12px] font-semibold text-hub hover:underline underline-offset-[3px] decoration-hub/30">Profil &amp; Firma verwalten →</a>
</div>
</div>
<div class="p-6">
<div class="grid gap-3" style="grid-template-columns:1fr 1fr;">
<!-- Empty-Slot Firma 1 -->
<div class="relative border border-dashed border-bg-rule rounded-[5px] p-5 hover:border-hub/50 hover:bg-bg-elev transition-colors">
<div class="flex items-center gap-3 mb-3">
<span class="w-10 h-10 rounded-[4px] border border-bg-rule bg-bg-elev flex items-center justify-center text-ink-4">
<svg width="18" height="18" viewBox="0 0 16 16" fill="none"><rect x="2.5" y="3.5" width="11" height="10" stroke="currentColor" stroke-width="1.3"/><path d="M2.5 6h11M6 9h1M9 9h1M6 11.5h1M9 11.5h1" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>
</span>
<div>
<div class="text-[13.5px] font-semibold text-ink-2">Firma hinzufügen</div>
<div class="text-[11px] text-ink-3 mt-0.5">Slot frei · 1 von 3</div>
</div>
</div>
<p class="text-[11.5px] text-ink-3 leading-[1.5] m-0">
Pressestellen, für die Sie Mitteilungen erstellen — mit eigenem Logo, Kontaktperson und Themen-Tags.
</p>
<a href="#" class="absolute top-3 right-3 inline-flex items-center justify-center w-7 h-7 rounded-[3px] bg-white border border-bg-rule text-hub hover:border-hub">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M6 2v8M2 6h8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
</a>
</div>
<!-- Hinweis -->
<div class="bg-bg-elev border border-bg-rule rounded-[5px] p-5">
<div class="eyebrow muted mb-2">Hinweis</div>
<div class="text-[13px] text-ink-2 leading-[1.55] m-0">
Keine Firmen zugeordnet. Wenn hier eine Firma fehlen sollte, prüfen Sie bitte Ihr Profil oder wenden Sie sich an den Support.
</div>
<div class="mt-3 flex items-center gap-2">
<a href="#" class="btn-secondary" style="padding:6px 10px;font-size:11.5px;">Profil prüfen</a>
<a href="/cdn-cgi/l/email-protection#d1a2a4a1a1bea3a591a1a3b4a2a2b4a1bea3a5b0bdb4ffb2bebc" class="text-[11.5px] font-semibold text-ink-3 hover:text-hub">Support kontaktieren</a>
</div>
</div>
</div>
</div>
</article>
<!-- Aktivität / Bridge -->
<article class="panel-dark">
<div class="panel-head">
<span class="eyebrow on-dark">Brand-Bridge</span>
<span class="font-mono text-[10px] text-ink-on-dark-3 tracking-[0.14em] uppercase">A · B</span>
</div>
<div class="px-5 py-5">
<div class="text-[12.5px] leading-[1.55] text-ink-on-dark-2 m-0 mb-4">
Ein Konto, beide Portale — Veröffentlichungen werden parallel auf presseecho und businessportal24 ausgespielt.
</div>
<div class="grid gap-3" style="grid-template-columns:1fr 1fr;">
<div class="bg-hub-2 rounded-[4px] px-3.5 py-3 border border-white/5">
<div class="flex items-center gap-2 mb-1.5">
<span class="dot-pe"></span>
<span class="text-[11px] font-bold tracking-[0.14em] uppercase text-white/85">presseecho</span>
</div>
<div class="font-mono text-[15px] font-semibold text-white" style="font-variant-numeric:tabular-nums;">verbunden</div>
<div class="text-[10.5px] text-ink-on-dark-3 mt-0.5">Archiv · Branchen-Tiefe</div>
</div>
<div class="bg-hub-2 rounded-[4px] px-3.5 py-3 border border-white/5">
<div class="flex items-center gap-2 mb-1.5">
<span class="dot-bp"></span>
<span class="text-[11px] font-bold tracking-[0.14em] uppercase text-white/85">businessportal24</span>
</div>
<div class="font-mono text-[15px] font-semibold text-white" style="font-variant-numeric:tabular-nums;">verbunden</div>
<div class="text-[10.5px] text-ink-on-dark-3 mt-0.5">Wirtschaft · Live</div>
</div>
</div>
<hr class="mt-5 mb-4" style="border:0;height:1px;background:rgba(255,255,255,.10);" />
<div class="space-y-2 text-[11.5px] text-ink-on-dark-2">
<div class="flex items-center justify-between">
<span>API-Status</span>
<span class="flex items-center gap-1.5 text-white"><span class="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse"></span>operational</span>
</div>
<div class="flex items-center justify-between">
<span>Letzte Synchronisation</span>
<span class="font-mono text-white">vor 2 min</span>
</div>
<div class="flex items-center justify-between">
<span>Tarif</span>
<span class="font-mono text-white">Starter</span>
</div>
</div>
<a href="Hub Landing presseportale.html#tarife" class="mt-5 inline-flex items-center gap-1.5 text-[11.5px] font-semibold text-white hover:underline underline-offset-[3px] decoration-white/30">
Tarife &amp; Add-ons ansehen →
</a>
</div>
</article>
</section>
<!-- ============== FUSSZEILE ============== -->
<footer class="flex items-center justify-between pt-4 pb-2 text-[11px] text-ink-3 border-t border-bg-rule">
<span>© 2026 presseportale.com · Publisher-Hub</span>
<span class="flex items-center gap-5">
<a href="#" class="hover:text-hub">Tastenkürzel</a>
<a href="#" class="hover:text-hub">Changelog</a>
<a href="#" class="hover:text-hub">Statusseite</a>
<a href="/cdn-cgi/l/email-protection#04777174746b767044747661777761746b76706568612a676b69" class="hover:text-hub">Support</a>
</span>
</footer>
</div>
</main>
</div>
</div>
<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script></body>
</html>

View file

@ -165,7 +165,7 @@
<span class="absolute left-0 top-0 bottom-0 w-[3px] bg-brand"></span>
<div>
<div class="eyebrow mb-2.5 text-[10.5px]" style="color:#FF8B6F;">Einreichen im Publisher-Bereich</div>
<h3 class="font-serif m-0 text-[23px] font-semibold leading-[1.25] tracking-[-0.3px] text-ink-on-dark">Die Einreichung läuft über presseportale.com.</h3>
<h3 class="font-serif m-0 text-[23px] font-semibold leading-[1.25] tracking-[-0.3px] text-ink-on-dark">Die Einreichung läuft über pressekonto.de.</h3>
<p class="mt-2.5 mb-0 text-[13.5px] leading-[1.55] text-ink-on-dark-2 max-w-[540px]">Dort verwalten Sie Mitteilungen, Credits und Newsroom — einmaliges Konto, beide Portale nutzbar (businessportal24 &amp; presseecho.de).</p>
</div>
<div class="flex flex-col items-end gap-2.5">
@ -342,7 +342,7 @@
<p class="mt-4.5 mb-0 text-[12px] text-ink-3" style="margin-top:18px;">
Die Veröffentlichung erfolgt über den zentralen Publisher-Bereich auf
<a href="#" class="text-brand font-medium border-b border-brand">presseportale.com</a>.
<a href="#" class="text-brand font-medium border-b border-brand">pressekonto.de</a>.
Cross-Publishing nach presseecho.de ist optional verfügbar.
</p>
</div>
@ -583,7 +583,7 @@
Zum Publisher-Bereich
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" class="ml-0.5"><path d="M4 8L8.5 3.5M8.5 3.5H5M8.5 3.5V7" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
<span class="text-[12px] text-ink-on-dark-2">Einreichung läuft über presseportale.com · Login per Magic-Link</span>
<span class="text-[12px] text-ink-on-dark-2">Einreichung läuft über pressekonto.de · Login per Magic-Link</span>
<a href="#" class="mt-1.5 text-[13px] text-ink-on-dark border-b pb-0.5" style="border-color:rgba(255,255,255,0.35);">Oder zuerst Beispiele ansehen →</a>
</div>
</div>
@ -597,7 +597,7 @@
<div class="grid pb-7" style="grid-template-columns:1.5fr 1fr 1fr 1fr;gap:56px;border-bottom:1px solid rgba(255,255,255,0.08);">
<div>
<div class="font-serif text-[22px] font-semibold" style="letter-spacing:-0.4px;">businessportal<span class="text-brand">24</span></div>
<p class="mt-3 text-[12.5px] text-ink-on-dark-2 leading-[1.6] max-w-[380px]">businessportal24 ist ein Service der Presseportale-Gruppe. Plattform für Pressemitteilungen mittelständischer Unternehmen, Selbstständiger und PR-Agenturen im deutschsprachigen Raum.</p>
<p class="mt-3 text-[12.5px] text-ink-on-dark-2 leading-[1.6] max-w-[380px]">businessportal24 ist ein Service der Pressekonto-Gruppe. Plattform für Pressemitteilungen mittelständischer Unternehmen, Selbstständiger und PR-Agenturen im deutschsprachigen Raum.</p>
</div>
<div>
<div class="eyebrow mb-3.5 text-[10px]" style="color:rgba(255,255,255,0.5);">Einreichen</div>
@ -628,7 +628,7 @@
</div>
</div>
<div class="mt-5.5 flex justify-between items-center flex-wrap gap-4 text-[12px] text-ink-on-dark-2" style="margin-top:22px;">
<div>© 2026 Presseportale-Gruppe · Alle Rechte vorbehalten</div>
<div>© 2026 Pressekonto-Gruppe · Alle Rechte vorbehalten</div>
<div class="flex items-center gap-4">
<span class="text-ink-on-dark-2">Für fachlich-spezifische Themen:</span>
<a href="#" class="text-ink-on-dark border-b pb-px" style="border-color:rgba(255,255,255,0.3);">presseecho.de →</a>

View file

@ -24,7 +24,7 @@ Diese Frontends sollen künftig **gegen das neue Backend arbeiten** via dire
### 1.3 Neues Backend (Aufgabe dieses Projekts)
- Läuft unter `presseportale.test` / `presseportale.com`
- Läuft unter `pressekonto.test` / `pressekonto.de`
- Stack: **Laravel 12, PHP 8.4, Livewire 4, Volt, Flux UI 2, MySQL 8, Tailwind 4**
- **Vorarbeiten bereits vorhanden**: Admin-UI-Gerüst, Routes, Auth-Stack
- **Noch zu tun**: Eloquent-Models, Migrations, Services, Daten-Migration, API, Payment (Stripe), Cron-Jobs
@ -58,7 +58,7 @@ Diese Frontends sollen künftig **gegen das neue Backend arbeiten** via dire
| Kriterium | Messbar an |
|---|---|
| Backend unter `presseportale.test` erreichbar | HTTP 200 auf Login-Seite |
| Backend unter `pressekonto.test` erreichbar | HTTP 200 auf Login-Seite |
| Admin-Login (Fortify + 2FA) funktioniert | Feature-Test `AuthTest` |
| Customer-Portal via Magic-Link erreichbar | Feature-Test `MagicLinkLoginTest` |
| Pressemitteilungen CRUD (Admin + Customer) | Livewire-Tests `PressReleaseIndex/Create/Edit/Show` |

View file

@ -4,7 +4,7 @@
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Presseportale Backend (presseportale.test) │
│ Pressekonto Backend (pressekonto.test) │
│ Laravel 12 · PHP 8.4 · Livewire 4 · Volt · Flux UI 2 │
│ │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ ┌─────────┐ │
@ -268,7 +268,7 @@ app/
## 6. Multi-Domain & Portal-Scope
- Pro Domain ein Frontend, aber alle Admin-Screens auf `presseportale.test`.
- Pro Domain ein Frontend, aber alle Admin-Screens auf `pressekonto.test`.
- Middleware `SetCurrentPortal`:
- Web (presseecho.test / businessportal24.test) → `app()->instance('current_portal', Portal::Presseecho|Businessportal24)`
- Admin → Portal-Auswahl via Dropdown, gespeichert in Session (`current_portal`)

View file

@ -224,7 +224,7 @@ Die Views existieren bereits als Blade/Livewire-Stubs (`resources/views/admin/*`
| 11.3 | Produktiv-Import-**Rehearsal** gegen aktuellen Legacy-Snapshot | ⬜ |
| 11.4 | **Go-Live-Mailing**: Passwort-Reset + Sicherheitshinweis an alle User | 🔄 Command/Mailable fertig; Versand und finale Texte offen |
| 11.5 | **API-Kunden-Kommunikation** (Token-Migration) | 🔄 Kundenreport fertig; Freigabeliste und operative Mailtexte offen |
| 11.6 | DNS-Cutover-Plan: `presseportale.com` auf neuen Server | ⬜ |
| 11.6 | DNS-Cutover-Plan: `pressekonto.de` auf neuen Server | ⬜ |
| 11.7 | Read-Only-Modus auf Legacy-Systemen während Final-Import | ⬜ |
| 11.8 | Abschalten der alten Symfony-Server (nach Review-Periode) | ⬜ |

View file

@ -175,8 +175,8 @@ Content-Type: application/json
{
"message": "Legacy API keys are no longer supported.",
"migration_url": "https://presseportale.com/account/tokens",
"docs_url": "https://presseportale.com/docs/api/v1"
"migration_url": "https://pressekonto.de/account/tokens",
"docs_url": "https://pressekonto.de/docs/api/v1"
}
```

View file

@ -154,7 +154,7 @@ php artisan test --compact tests/Feature/LegacyInvoiceArchiveCommandTest.php tes
## 2026-05-04 Architektur-Entscheidung: ein gemeinsames Admin-Panel mit rollenbasierter Sichtbarkeit
Der Auftraggeber hat festgelegt: **es gibt nur ein gemeinsames Admin-Panel** unter dem Presseportale-Backend. Admins, Editoren und Customer arbeiten im selben UI Sichtbarkeit von Menüpunkten und Aktionen entscheidet die Rolle/Permission.
Der Auftraggeber hat festgelegt: **es gibt nur ein gemeinsames Admin-Panel** unter dem Pressekonto-Backend. Admins, Editoren und Customer arbeiten im selben UI Sichtbarkeit von Menüpunkten und Aktionen entscheidet die Rolle/Permission.
- Aktuell laufen Admin-/Editor-Funktionen unter `/admin/*` und Customer-Funktionen unter `/customer/*`. Diese Trennung wird **vor dem Pressemitteilungs-Veröffentlichungs-Block** in eine **gemeinsame Panel-Architektur** überführt.
- Sidebar wird rollenbasiert gefiltert (Admin/Editor sehen alle PMs, Customer nur seine etc.). Routen-Konsolidierung Customer → Admin-Panel.
@ -318,7 +318,7 @@ Status in `03-MIGRATION-PLAN.md` ist auf `⏸️ Vertagt 2026-05-04` gesetzt.
### Was wurde gemacht
#### Paket A Test-Suite stabilisiert
- `phpunit.xml`: `APP_URL=https://presseportale.test` ergänzt, damit `route('login')` etc. nicht mehr auf eine fremde Domain ohne Auth-Routen zeigen.
- `phpunit.xml`: `APP_URL=https://pressekonto.test` ergänzt, damit `route('login')` etc. nicht mehr auf eine fremde Domain ohne Auth-Routen zeigen.
- `tests/Feature/Auth/EmailVerificationTest.php`: Tests werden jetzt sauber per `markTestSkipped()` übersprungen, solange `Features::emailVerification()` in `config/fortify.php` deaktiviert ist. Sobald das Feature aktiviert wird, laufen die Tests automatisch wieder.
- `tests/Feature/ExampleTest.php`: testet jetzt den Health-Endpoint `/up` statt der von Vite/Theme-Layout abhängigen Startseite.
- Leerer `tests/Feature/Feature/` / `tests/Feature/Feature/Billing/`-Doppelpfad entfernt.

View file

@ -166,7 +166,7 @@ Hängt komplett an §2 (Stripe-Account + Produktliste).
| 11.3 | Produktiv-Import-**Rehearsal** gegen aktuellen Legacy-Snapshot | 🔴 | ⬜ |
| 11.4 | Go-Live-Mailing (Passwort-Reset + Sicherheitshinweis) versenden | 🔴 | 🔄 (Code fertig, Texte/Versand offen) |
| 11.5 | API-Kunden-Kommunikation (Token-Migration) versenden | 🔴 | 🔄 (Report fertig, Texte/Versand offen) |
| 11.6 | DNS-Cutover-Plan: `presseportale.com` auf neuen Server | 🔴 | ⬜ |
| 11.6 | DNS-Cutover-Plan: `pressekonto.de` auf neuen Server | 🔴 | ⬜ |
| 11.7 | Read-Only-Modus auf Legacy-Systemen während Final-Import | 🔴 | ⬜ |
| 11.8 | Abschalten der alten Symfony-Server (nach Review-Periode) | 🟡 | ⬜ |

View file

@ -8,6 +8,8 @@ Stand: 2026-05-04. Dieses Kurz-Runbook spiegelt den aktuell implementierten Comm
php artisan legacy:import --source=all --dry-run
php artisan legacy:archive-invoices --dry-run
php artisan legacy:verify --no-report
php artisan legacy:migrate-media --portal=all --type=all --base-path=dev/migration --dry-run
```
Hinweis: `legacy:archive-invoices` importiert die Legacy-Rechnungen vollständig in `legacy_invoices`, inkl. Status/User-Zuordnung, `raw_snapshot`, `pdf_payload` und Report. Die PDF-Erzeugung erfolgt im Customer-Bereich bei Abruf aus diesen Archivdaten.
@ -38,6 +40,7 @@ php artisan legacy:import --source=all --force
php artisan legacy:archive-invoices
php artisan legacy:fix-timestamps
php artisan legacy:verify
php artisan legacy:migrate-media --portal=all --type=all --base-path=dev/migration
```
## Noch nicht im Runbook finalisiert

View file

@ -1,6 +1,6 @@
# Migration 2026 Presseportale Backend
# Migration 2026 Pressekonto Backend
> **Migration des Backends** der beiden Legacy-Portale `presseecho` und `businessportal24` (Symfony 1.4, PHP 5.6) in ein **gemeinsames, modernes Laravel 12 Backend** unter der Domain `presseportale.test` / `presseportale.com`.
> **Migration des Backends** der beiden Legacy-Portale `presseecho` und `businessportal24` (Symfony 1.4, PHP 5.6) in ein **gemeinsames, modernes Laravel 12 Backend** unter der Domain `pressekonto.test` / `pressekonto.de`.
**Start:** 23.04.2026
**Lead-Technologie:** Laravel 12 · PHP 8.4 · Livewire 4 · Volt · Flux UI 2 · Tailwind CSS 4 · MySQL 8
@ -43,7 +43,7 @@ Historisch existieren zwei technisch identische Symfony-1.4-Installationen (`pre
- **DB-Dumps** (2026-04-23): `businessportal24` 42 Tabellen / 578 MB, `presseecho` 43 Tabellen / 369 MB 41 Tabellen sind strukturell identisch, 3 abweichend (`press_release_image_old` nur BP24, `category_pe_data` + `press_release_pe_data` nur PE)
Aktueller Implementierungsstand (Code-Abgleich 2026-04-29):
- ✅ **Domain-basiertes Theme-System** (`presseportale` / `presseecho` / `businessportal24`)
- ✅ **Domain-basiertes Theme-System** (`pressekonto` / `presseecho` / `businessportal24`)
- ✅ **Admin-UI** mit echten Daten für Dashboard, Users/Roles, Companies, Contacts, Categories-Index und PressRelease-CRUD/-Workflow
- ✅ **Auth-Stack**: Fortify, Sanctum, Spatie/Permission
- ✅ **Admin-Schutz und Portal-Scoping**: `EnsureUserIsAdmin`, `SetCurrentPortal`, `PortalScope`, Sidebar-Portal-Switcher