--- quick_id: 260907-fgv slug: tessera-logo-und-favicon-fest-einbauen-b date: 2026-09-07 status: planned relates_to: 01-foundation-portal-shell phase: quick-260907-fgv plan: 01 type: execute wave: 1 depends_on: [] files_modified: - apps/web/src/app/icon.svg - apps/web/src/app/apple-icon.png - apps/web/src/components/brand/brand.ts - apps/web/src/components/brand/tessera-logo.tsx - apps/web/src/components/brand/tessera-logo.test.tsx - apps/web/src/messages/de.json - apps/web/src/messages/en.json - apps/web/src/components/layout/header.tsx - apps/web/src/components/layout/sidebar.tsx - apps/web/src/components/layout/sidebar.test.tsx - apps/web/src/app/(auth)/login/page.tsx - apps/web/src/app/(auth)/reset-password/page.tsx - apps/web/src/app/(auth)/reset-password/[token]/page.tsx - apps/web/src/components/settings/account-settings-form.tsx autonomous: false requirements: [QUICK-260907-FGV] estimate: tokens: 55000 raw_tokens: 55000 tasks: 3 confidence: low must_haves: truths: - "Der Browser-Tab zeigt die Bildmarke statt des Standard-Globus — in hellem und in dunklem Erscheinungsbild." - "Kopfleiste, Seitenleisten-Schublade, Anmeldeseite (breite und schmale Ansicht) und beide Passwort-Seiten zeigen dieselbe Bildmarke aus derselben Komponente." - "Der Schriftzug bleibt in beiden Erscheinungsbildern lesbar, weil er echter HTML-Text in der Vordergrundfarbe der Oberflaeche ist und kein eingebetteter Text mit fest eingebackener Farbe." - "Die dunkle Platte der Bildmarke hebt sich im dunklen Erscheinungsbild vom Hintergrund ab, weil sie dort eine helle Haarlinie als Kontur bekommt; im hellen Erscheinungsbild ist die Kontur unsichtbar." - "Auf dem gelben Anmelde-Panel bleibt die Bildmarke ohne Kontur, weil dieses Panel unabhaengig vom Erscheinungsbild immer eine helle Flaeche ist." - "Der Gelbwert der Marke ist im gesamten Web-Quellcode genau einmal notiert; Anmelde-Panel und Bildmarke lesen ihn aus dieser einen Konstante." - "Das gelieferte Doppel favicon.svg / tessera-logo.svg wird nur einmal in das Repository uebernommen — der Byte-Vergleich hat beide als identisch nachgewiesen." - "Es entsteht keine Hochlade-Maske, kein Einstellungsfeld, keine Datenbankspalte und keine Mandantenabfrage fuer das Logo." artifacts: - apps/web/src/app/icon.svg - apps/web/src/app/apple-icon.png - apps/web/src/components/brand/brand.ts - apps/web/src/components/brand/tessera-logo.tsx - apps/web/src/components/brand/tessera-logo.test.tsx key_links: - "BRAND_YELLOW in brand.ts -> Signalkachel der Bildmarke UND Hintergrund des Anmelde-Panels UND Vorgabewert der persoenlichen Akzentfarbe: eine Konstante, drei Nutzer" - "TesseraLogo -> header.tsx, sidebar.tsx, login/page.tsx (zweimal), reset-password/page.tsx, reset-password/[token]/page.tsx: die eine Stelle, an der eine spaetere Mandanten-Marke ansetzt" - "common.brand.logoAlt in de.json UND en.json -> aria-label der Bildmarke; fehlt der Schluessel in einer Sprache, liest ein Screenreader den Schluesselnamen vor" - "apps/web/src/app/icon.svg -> dateibasierte Metadaten von Next.js: die Datei MUSS unter src/app/ liegen, in public/ waere sie wirkungslos, weil layout.tsx kein icons-Feld setzt" --- Das gelieferte Tessera-Logo und das Favicon fest in die Oberflaeche einbauen — und zwar so, dass die spaetere Wunschfunktion "Administrator gestaltet das Erscheinungsbild je Mandant" genau eine Datei anfassen muss. Purpose: Die Plattform traegt bis heute an jeder Marken-Stelle nur das Wort "Tessera" als Text, dazu auf der Anmeldeseite ein handgezeichnetes Vier-Quadrate-Platzhalter-Icon. Das gelieferte Logo ersetzt beides. Output: Eine zentrale Komponente `TesseraLogo`, eine Farbkonstanten-Datei, zwei Icon-Dateien fuer den Browser-Tab, sechs umgestellte Verwendungsstellen. ## Die gelieferten Dateien und was mit ihnen geschieht Der Ordner `user-files/logo/` ist ueber `.gitignore:32` vom Repository ausgeschlossen. Nichts darf von dort aus referenziert werden — was gebraucht wird, wird kopiert. | Datei | Verwendung | |---|---| | `favicon.svg` | wird nach `apps/web/src/app/icon.svg` kopiert (Browser-Tab) | | `tessera-logo.svg` | wird NICHT kopiert — `diff` weist sie als byteweise identisch zu `favicon.svg` aus | | `apple-touch-icon.png` | wird nach `apps/web/src/app/apple-icon.png` kopiert | | `tessera-logo-schriftzug.svg` | wird NICHT kopiert — Begruendung unten | | `favicon-16.png`, `favicon-32.png` | werden NICHT kopiert — jeder von Tessera unterstuetzte Browser zeigt SVG-Favicons | | `tessera-logo-512.png` | wird NICHT kopiert — es gibt keine Web-App-Manifest-Datei, die sie braeuchte | ## Vier Entscheidungen, die vorab getroffen wurden **1. Der Schriftzug wird nicht als Bild ausgeliefert, sondern als echter Text neben der Bildmarke gesetzt.** Zwei unabhaengige Gruende. Erstens die Farbe: in `tessera-logo-schriftzug.svg` steht die Textfarbe zweimal fest eingebacken als dunkler Ton; auf dem dunklen Erscheinungsbild der Anwendung waere der Schriftzug unsichtbar. Zweitens die Schrift: die Datei verlangt die Schriftart Archivo in Schnitt 900 — die Anwendung laedt ueber `--font-sans` in `globals.css:33` ausschliesslich Inter, Archivo ist nirgends eingebunden. Der Browser wuerde also ohnehin eine andere Schrift einsetzen, mit anderen Buchstabenbreiten, in einem starr auf 420x100 festgelegten Zeichenbereich — der Schriftzug wuerde je nach Rechner ueberlaufen oder verloren in der Flaeche stehen. Beides faellt weg, wenn "Tessera" normaler HTML-Text in der Hausschrift ist: er erbt die Vordergrundfarbe, er bricht nie, und er kommt aus `common.appName`, ist also uebersetzbar. Die Alternative — je eine helle und eine dunkle Variante der Bilddatei ausliefern — waere zwei Dateien, ein Theme-Hook und ein Umschaltflackern beim Seitenaufbau; sie loest das Schriftproblem ausserdem gar nicht. **2. Die dunkle Platte der Bildmarke bekommt im dunklen Erscheinungsbild eine Kontur.** Nachgerechnet, nicht vermutet: die Platte der Marke ist ein sehr dunkles Grau, der dunkle Anwendungshintergrund `oklch(0.23 0.01 260)` entspricht `#1a1d22`, die dunkle Seitenleiste `oklch(0.20 0.01 260)` entspricht `#13161b`. Platte und Hintergrund sind damit praktisch derselbe Ton — die Platte verschwindet, die fuenf Kacheln wuerden frei schweben. Die Marke selbst bleibt trotzdem unangetastet: die Platte erhaelt lediglich ueber eine `dark:`-Utility-Klasse eine halbtransparente helle Haarlinie als Kontur. Im hellen Erscheinungsbild ist die Kontur durchsichtig, dort aendert sich also nichts. Das ist dieselbe Behandlung, die die Anwendung ohnehin fuer dunkle Flaechen auf dunklem Grund verwendet. Auf dem gelben Anmelde-Panel wird die Kontur per Eigenschaft abgeschaltet, weil dieses Panel unabhaengig vom gewaehlten Erscheinungsbild gelb bleibt und die Platte dort von sich aus maximalen Kontrast hat. Fuer die Icon-Datei im Browser-Tab bleibt die Platte ohne Kontur: die Tab-Leiste ist keine Flaeche dieser Anwendung, und die Wiedererkennung traegt dort ohnehin die helle Signalkachel. **3. Der Gelbwert steht kuenftig genau einmal im Quellcode.** Er kommt heute dreimal vor: als Hintergrund-Utility des Anmelde-Panels, im Kopfkommentar derselben Datei und als Vorgabewert der persoenlichen Akzentfarbe in `account-settings-form.tsx:14`. Kuenftig steht er nur noch in `apps/web/src/components/brand/brand.ts`; die drei Stellen lesen ihn von dort. Bewusst KEINE neue CSS-Variable in `globals.css`: die Auswahl der Akzentfarbe laeuft ueber ein ``, das zwingend einen echten Farbwert braucht und mit einer CSS-Variablen nichts anfangen kann. Eine TypeScript-Konstante bedient alle drei Stellen, eine CSS-Variable koennte es nicht. Der vorhandene Token `--primary` bleibt unberuehrt: er ist ein rechnerisch leicht abweichender Ton (`#fbe405` gegen den Gelbwert der Marke) und steuert Schaltflaechen, Fokusringe und Aktivmarkierungen — ihn auf den Markenwert zu ziehen waere eine Umfaerbung der gesamten Anwendung und ist nicht Teil dieser Aufgabe. **4. Der Browser-Tab wird ueber dateibasierte Metadaten verdrahtet, nicht ueber `metadata.icons`.** Next.js liegt hier in Version 15.5.19 mit App Router vor; eine Datei `src/app/icon.svg` erzeugt den ``-Eintrag von selbst. Damit bleibt `layout.tsx` unberuehrt — weniger Code, weniger Fehlerquellen. Das leere Verzeichnis `apps/web/public/` bleibt leer; dort abgelegte Icons wuerden ohne zusaetzlichen Eintrag in `layout.tsx` nicht gefunden. ## Zwei sichtbare Aenderungen, die ueber "Logo einbauen" hinausgehen und deshalb ausdruecklich zur Abnahme gestellt werden - **Der Schriftzug ist kuenftig nicht mehr gelb.** Kopfleiste und Seitenleiste setzen "Tessera" heute in der Primaerfarbe, also Gelb auf weissem Grund — schwer lesbar. Das gelieferte Logo setzt den Schriftzug dunkel. Die Komponente folgt dem Logo und nutzt die Vordergrundfarbe der Oberflaeche. - **Auf dem gelben Anmelde-Panel steht die Bildmarke kuenftig OBEN.** Heute steht dort Schriftzug, Zeile darunter, Platzhalter-Icon ganz unten. Das gelieferte Logo ist als Bildmarke-zuerst aufgebaut; die Komponente uebernimmt diese Reihenfolge. Der bestehende Werbesatz bleibt unveraendert darunter stehen. Beides steht im Abnahmeschritt und kann dort zurueckgewiesen werden. ## Was ausdruecklich NICHT passiert Keine Hochlade-Maske, keine Einstellungsseite, kein Datenbankfeld, keine Mandantenabfrage fuer das Logo. Der Wunsch "ein Administrator soll das Erscheinungsbild je Mandant anpassen koennen" wird als eigener Backlog-Punkt festgehalten und vom Orchestrator eingereicht — nicht in diesem Plan gebaut. @~/.claude/gsd-core/workflows/execute-plan.md @~/.claude/gsd-core/templates/summary.md @CLAUDE.md @.planning/STATE.md @apps/web/src/app/layout.tsx @apps/web/src/app/globals.css @apps/web/src/components/layout/header.tsx @apps/web/src/components/layout/sidebar.tsx @apps/web/src/components/layout/sidebar.test.tsx @apps/web/src/app/(auth)/login/page.tsx @apps/web/src/app/(auth)/reset-password/page.tsx @apps/web/src/components/settings/account-settings-form.tsx @apps/web/src/messages/de.json Task 1: Markenbausteine — Icon-Dateien, Farbkonstanten, TesseraLogo-Komponente, i18n-Schluessel apps/web/src/app/icon.svg, apps/web/src/app/apple-icon.png, apps/web/src/components/brand/brand.ts, apps/web/src/components/brand/tessera-logo.tsx, apps/web/src/components/brand/tessera-logo.test.tsx, apps/web/src/messages/de.json, apps/web/src/messages/en.json - Variante `mark`: es entsteht ein Element mit der Rolle `img`, dessen zugaenglicher Name aus dem Uebersetzungsschluessel `common.brand.logoAlt` stammt. Ein sichtbarer Schriftzug ist NICHT vorhanden. - Variante `horizontal`: Rolle `img` vorhanden UND der Schriftzug aus `common.appName` ist als Text sichtbar. - Variante `stacked`: Rolle `img` vorhanden UND der Schriftzug ist sichtbar. - Die Platte traegt bei Vorgabewerten eine Klasse, die die Kontur nur im dunklen Erscheinungsbild einschaltet; mit `plateOutline={false}` fehlt diese Klasse. - Die Grundflaeche der Bildmarke enthaelt fuenf Kacheln, davon genau eine mit dem Gelbwert der Marke und genau eine mit einer Drehung. - Sowohl `de.json` als auch `en.json` definieren `common.brand.logoAlt` — der Test liest beide Dateien und vergleicht die Schluesselmenge unterhalb von `common.brand`, damit keine Sprache spaeter allein ergaenzt werden kann. Kopiere `user-files/logo/favicon.svg` unveraendert nach `apps/web/src/app/icon.svg` und `user-files/logo/apple-touch-icon.png` unveraendert nach `apps/web/src/app/apple-icon.png`. Nur diese beiden Dateien — die uebrigen gelieferten Dateien bleiben ausserhalb des Repositories, `tessera-logo.svg` ist nachweislich byteweise identisch zu `favicon.svg` und wuerde eine zweite Kopie desselben Inhalts erzeugen. Beide Zieldateien MUESSEN unter `src/app/` liegen, damit Next.js sie als dateibasierte Metadaten erkennt; `layout.tsx` wird nicht angefasst. Lege `apps/web/src/components/brand/brand.ts` an. Die Datei exportiert drei benannte Konstanten fuer die drei Farben der Marke: den Gelbwert der gedrehten Signalkachel als `BRAND_YELLOW`, den Olivton der vier uebrigen Kacheln als `BRAND_OLIVE` und die Farbe der abgerundeten Grundplatte als `BRAND_PLATE`. Die Werte werden NICHT aus dieser Beschreibung abgeschrieben, sondern beim Umsetzen direkt aus `user-files/logo/favicon.svg` uebernommen — dort steht die Plattenfarbe am `rect` mit `rx="14"`, der Olivton an den vier achsenparallelen Kacheln und der Gelbwert an der Kachel mit `transform="rotate(...)"`. Ein kurzer Dateikommentar haelt fest, dass dies die einzige Stelle im Web-Quellcode ist, an der eine Markenfarbe notiert wird, und dass eine spaetere mandantenspezifische Gestaltung hier ansetzt. Schreibe in diesen Kommentar keinen weiteren Farbwert hinein — die Konstanten tragen die Werte, der Kommentar beschreibt nur ihren Zweck. Lege `apps/web/src/components/brand/tessera-logo.tsx` an, als `'use client'`-Komponente, weil sie `useTranslations` aus `next-intl` benutzt. Exportiere `TesseraLogo` mit den Eigenschaften `variant` (`'mark' | 'horizontal' | 'stacked'`, Vorgabe `'horizontal'`), `size` (Kantenlaenge der Bildmarke in Pixeln, Vorgabe 32), `plateOutline` (Vorgabe `true`), `className` und `wordmarkClassName`. Die Bildmarke wird als JSX-SVG von Hand gesetzt: `viewBox="0 0 72 72"`, `width`/`height` aus `size`, `role="img"` und `aria-label` aus `t('brand.logoAlt')` bei `useTranslations('common')`. Geometrie und Reihenfolge der sechs Rechtecke werden 1:1 aus `user-files/logo/favicon.svg` uebernommen — Position, Groesse, Eckenradius und die Drehung der Signalkachel unveraendert. Die Fuellfarben kommen aus den drei Konstanten, nicht als Zahlenwerte im JSX. Setze das SVG als echte JSX-Elemente zusammen; verwende ausdruecklich NICHT `dangerouslySetInnerHTML` und lies die Datei auch nicht zur Laufzeit ein. Die Grundplatte erhaelt zusaetzlich `strokeWidth={2}` und, sofern `plateOutline` wahr ist, eine Klassenkombination, die die Kontur im hellen Erscheinungsbild durchsichtig laesst und im dunklen auf eine halbtransparente weisse Linie schaltet (Tailwind-Utilities `stroke-transparent` und die zugehoerige `dark:`-Variante, etwa mit rund 25 Prozent Deckung). Ist `plateOutline` falsch, bleibt es bei der durchsichtigen Kontur ohne `dark:`-Variante. So bleibt die Umschaltung reines CSS — kein Theme-Hook, kein Flackern beim Seitenaufbau. Die Variante `mark` liefert nur das SVG. `horizontal` liefert einen `inline-flex`-Container mit Bildmarke und daneben dem Schriftzug aus `t('appName')`, gesetzt in halbfett, leicht negativer Laufweite und in der Vordergrundfarbe der Oberflaeche (nicht in der Primaerfarbe); `wordmarkClassName` wird an das Schriftzug-Element angehaengt, damit aufrufende Stellen die Farbe ueberschreiben koennen. `stacked` liefert dieselben beiden Bausteine als zentrierte Spalte, Bildmarke oben, Schriftzug darunter deutlich groesser. `inline-flex` ist Absicht: die aufrufenden Seiten zentrieren ueber `text-center` des Elternelements, ein Block-Element wuerde dort links kleben. Ergaenze in `de.json` und `en.json` unterhalb von `common` das Objekt `brand` mit dem Schluessel `logoAlt` ("Tessera Logo" beziehungsweise "Tessera logo"). Beide Dateien bekommen exakt denselben Schluesselbaum. Schreibe `apps/web/src/components/brand/tessera-logo.test.tsx` nach dem Muster von `sidebar.test.tsx`: `useTranslations` aus `next-intl` per `vi.mock` ersetzen, Rueckgabe ueber eine flache Zuordnung mit `appName` und `'brand.logoAlt'`. Decke die unter `behavior` genannten Faelle ab und lies fuer die Sprachparitaet `de.json` und `en.json` direkt als Modul ein, so wie es `src/messages/tenderRadar-parity.spec.ts` vormacht. cmp user-files/logo/favicon.svg apps/web/src/app/icon.svg && cmp user-files/logo/apple-touch-icon.png apps/web/src/app/apple-icon.png && pnpm --filter @tessera/web exec vitest run src/components/brand && pnpm --filter @tessera/web type-check Die beiden Icon-Dateien liegen byteidentisch zur Lieferung unter `src/app/`. `TesseraLogo` rendert in allen drei Varianten, der Test ist gruen, beide Sprachdateien tragen den neuen Schluessel, die Typpruefung ist sauber. Task 2: Alle sechs Marken-Stellen auf die Komponente umstellen und den Gelbwert zusammenfuehren apps/web/src/components/layout/header.tsx, apps/web/src/components/layout/sidebar.tsx, apps/web/src/components/layout/sidebar.test.tsx, apps/web/src/app/(auth)/login/page.tsx, apps/web/src/app/(auth)/reset-password/page.tsx, apps/web/src/app/(auth)/reset-password/[token]/page.tsx, apps/web/src/components/settings/account-settings-form.tsx - `sidebar.test.tsx` bleibt gruen: die Uebersetzungs-Attrappe kennt zusaetzlich den Schluessel `brand.logoAlt` im Bereich `common`, sodass die Bildmarke in der mobilen Schublade einen echten zugaenglichen Namen traegt statt des Schluesselnamens. - Die vorhandenen Aussagen von `sidebar.test.tsx` werden nicht abgeschwaecht und keine geloescht. Ersetze in `header.tsx` das Schriftzug-Element neben der Menue-Schaltflaeche durch ``. Der umgebende Flex-Container mit `gap-3` bleibt unveraendert. Ersetze in `sidebar.tsx` das Schriftzug-Element in der Kopfzeile der mobilen Schublade durch ``. Die Schliessen-Schaltflaeche rechts daneben bleibt unveraendert. In `sidebar.test.tsx` ergaenze die Uebersetzungs-Attrappe im Bereich `common` um den Eintrag `'brand.logoAlt'` mit einem sprechenden Wert; die Attrappe arbeitet bereits mit punktierten Schluesseln. Baue in `login/page.tsx` die linke, gelbe Spalte um. Der Gelbwert steht dort heute als Hintergrund-Utility in eckigen Klammern und ein zweites Mal im Kopfkommentar der Datei. Beide Vorkommen verschwinden: das Utility wird durch ein Inline-`style` mit `backgroundColor: BRAND_YELLOW` ersetzt (Import aus `@/components/brand/brand`), alle uebrigen Klassen der Spalte bleiben stehen; der Kopfkommentar wird ohne Nennung eines Farbwerts neu formuliert und verweist stattdessen auf die Konstante. Setze auf derselben Spalte zusaetzlich `className="text-gray-900"` an den inneren, zentrierten Container, damit der Schriftzug die dunkle Schrift des Panels erbt. Innerhalb der gelben Spalte entfaellt die Schriftzug-Ueberschrift ersatzlos, und ebenso ersatzlos entfaellt der handgezeichnete Platzhalter — das ist der ``-Block mit den vier gleich grossen Rechtecken im 24er-Koordinatensystem samt seinem `mt-4`-Wrapper. An ihre Stelle tritt, als erstes Element der Spalte, ``. `plateOutline={false}` ist hier Pflicht: das Panel ist unabhaengig vom Erscheinungsbild immer gelb, eine helle Kontur waere dort falsch. Der bestehende Absatz mit `t('branding.tagline')` bleibt unveraendert darunter stehen. In der nur auf schmalen Fenstern sichtbaren Marken-Gruppe derselben Datei entfaellt die Schriftzug-Ueberschrift ebenfalls ersatzlos; an ihre Stelle tritt ``. Der Container traegt bereits `text-center`, die Komponente zentriert sich dadurch von selbst. Der Absatz mit dem Werbesatz bleibt. Ersetze in `reset-password/page.tsx` und in `reset-password/[token]/page.tsx` jeweils die Schriftzug-Ueberschrift ueber der Seitenueberschrift durch ``. Die jeweils folgende `

` mit dem Seitentitel bleibt unveraendert — sie ist die eigentliche Ueberschrift der Seite und darf nicht mitentfernt werden. Ersetze in `account-settings-form.tsx` den Rumpf der lokalen Konstante `DEFAULT_ACCENT` durch den aus `@/components/brand/brand` importierten `BRAND_YELLOW`. Der Name `DEFAULT_ACCENT` bleibt bestehen, damit die drei bestehenden Verwendungsstellen unveraendert bleiben; nur der Wert kommt jetzt aus der Markendatei. set -e; test "$(grep -rl 'ffed00' apps/web/src --include='*.ts' --include='*.tsx')" = 'apps/web/src/components/brand/brand.ts'; test "$(grep -rl 'font-extrabold' 'apps/web/src/app/(auth)' --include='*.tsx' | wc -l)" -eq 0; test "$(grep -c 'width="7" height="7"' 'apps/web/src/app/(auth)/login/page.tsx' || true)" -eq 0; for f in apps/web/src/components/layout/header.tsx apps/web/src/components/layout/sidebar.tsx 'apps/web/src/app/(auth)/login/page.tsx' 'apps/web/src/app/(auth)/reset-password/page.tsx' 'apps/web/src/app/(auth)/reset-password/[token]/page.tsx'; do grep -q 'TesseraLogo' "$f"; done; pnpm --filter @tessera/web exec vitest run; pnpm --filter @tessera/web type-check; docker compose build web && docker compose up -d --force-recreate web; for i in $(seq 1 30); do curl -sf -o /dev/null http://localhost:3000/login && break || sleep 2; done; curl -sf -o /dev/null http://localhost:3000/login Der Gelbwert der Marke steht im gesamten Web-Quellcode nur noch in `brand.ts`. In den Anmelde- und Passwort-Seiten steht kein von Hand gesetzter Schriftzug mehr und kein Platzhalter-Icon. Alle fuenf Verwendungsstellen ziehen `TesseraLogo`. Die vollstaendige Web-Testsuite und die Typpruefung sind gruen. Das LOKALE Web-Abbild ist neu gebaut, der Container laeuft mit dem neuen Stand und die Anmeldeseite antwortet — die Abnahme in Task 3 sieht damit garantiert den neuen Code. Der Testserver alpha.tessera.ctl.de wird dabei NICHT angefasst. Task 3: Abnahme im Browser — beide Erscheinungsbilder, sieben Stellen Das gelieferte Tessera-Logo ist an sechs Stellen der Oberflaeche fest eingebaut und die Icon-Dateien fuer den Browser-Tab liegen im Repository. Alle Stellen ziehen aus einer einzigen Komponente `apps/web/src/components/brand/tessera-logo.tsx`; die Markenfarben stehen ausschliesslich in `apps/web/src/components/brand/brand.ts`. Der handgezeichnete Vier-Quadrate-Platzhalter auf der Anmeldeseite ist entfernt. Der Schriftzug wird als echter Text in der Hausschrift gesetzt, nicht als Bilddatei, damit er in beiden Erscheinungsbildern lesbar bleibt. Task 2 hat das lokale Web-Abbild bereits neu gebaut und den Container ersetzt — unter `http://localhost:3000` laeuft der neue Stand. Der Testserver alpha.tessera.ctl.de wurde nicht angefasst. `http://localhost:3000` oeffnen. Anmeldung mit `admin` / `admin123`; fuer die Nutzersicht stehen `nutzer1` / `Test1234!` und `nutzer2` / `Test1234!` bereit. Das Erscheinungsbild wird ueber den Umschalter rechts in der Kopfleiste gewechselt. Sollte die Oberflaeche wider Erwarten noch den alten Stand zeigen, hilft `docker compose build web && docker compose up -d --force-recreate web` — ein blosses `up` baut nicht neu. Jeder der folgenden sieben Punkte ist EINMAL im hellen und EINMAL im dunklen Erscheinungsbild anzusehen: 1. **Browser-Tab** — im Tab steht die dunkle abgerundete Kachel-Marke, nicht mehr das leere Standard-Blatt. Bei hartnaeckigem alten Symbol den Tab neu laden mit geleertem Zwischenspeicher. 2. **Kopfleiste** — Bildmarke links neben dem Schriftzug "Tessera", in einer Zeile, senkrecht mittig zur Zeilenhoehe, nicht abgeschnitten. 3. **Seitenleisten-Schublade** — Browserfenster schmal ziehen, bis das Menue-Symbol erscheint, Schublade oeffnen: dieselbe Marke in der Kopfzeile der Schublade, die Schliessen-Schaltflaeche daneben unveraendert erreichbar. 4. **Anmeldeseite, breite Ansicht** — abmelden, `http://localhost:3000/login`, Fenster breit: das gelbe Panel links zeigt oben die Bildmarke, darunter "Tessera", darunter den bekannten Werbesatz. Das handgezeichnete Vier-Quadrate-Symbol ist verschwunden. Das Gelb des Panels ist unveraendert dasselbe wie vorher. 5. **Anmeldeseite, schmale Ansicht** — Fenster schmal ziehen: das gelbe Panel entfaellt wie bisher, ueber dem Anmeldeformular steht die Marke einzeilig und mittig. 6. **Passwort-vergessen-Seite** — `http://localhost:3000/reset-password`: Marke oben, darunter unveraendert die Seitenueberschrift und das E-Mail-Feld. 7. **Passwort-neu-setzen-Seite** — `http://localhost:3000/reset-password/test123` (irgendein Zeichenkettenwert genuegt, die Seite rendert vor der Pruefung): Marke oben, darunter unveraendert Ueberschrift und beide Passwortfelder. Beim Durchgang im DUNKLEN Erscheinungsbild zusaetzlich pruefen: die dunkle Platte der Bildmarke hebt sich in Kopfleiste, Schublade und auf den Passwort-Seiten durch eine feine helle Kontur vom Hintergrund ab und verschwimmt nicht mit ihm. Auf dem gelben Anmelde- Panel darf diese Kontur NICHT zu sehen sein. **Zwei Aenderungen ausdruecklich zur Freigabe oder Zurueckweisung:** - Der Schriftzug "Tessera" ist in Kopfleiste und Schublade jetzt dunkel (beziehungsweise hell im dunklen Erscheinungsbild) statt wie bisher gelb. Passt das, oder soll das bisherige Gelb zurueck? - Auf dem gelben Anmelde-Panel steht die Bildmarke jetzt oben statt unten. Passt diese Reihenfolge, oder soll die Marke wieder unter den Text? "passt" — wenn alle sieben Punkte in beiden Erscheinungsbildern stimmen und beide Gestaltungsfragen freigegeben sind. Andernfalls die abweichende Stelle benennen (welcher Punkt, welches Erscheinungsbild, was ist zu sehen) beziehungsweise die gewuenschte Antwort auf die zwei Gestaltungsfragen geben. Beides wird noch in diesem Plan nachgezogen und nicht vertagt. ## Trust Boundaries | Boundary | Description | |----------|-------------| | Lieferant -> Repository | Die Logo-Dateien stammen von aussen und werden ins Repository uebernommen | | Browser -> ausgelieferte Icon-Route | Next.js liefert `icon.svg` unter einer oeffentlichen Route aus, ohne Anmeldung | ## STRIDE Threat Register | Threat ID | Category | Component | Severity | Disposition | Mitigation Plan | |-----------|----------|-----------|----------|-------------|-----------------| | T-FGV-01 | Tampering | tessera-logo.tsx | medium | mitigate | Eine fremde SVG-Datei als Zeichenkette in die Seite zu geben waere ein Einfallstor fuer eingebettete Skripte. Task 1 verbietet `dangerouslySetInnerHTML` und jedes Einlesen der Datei zur Laufzeit ausdruecklich: die Bildmarke wird als JSX von Hand gesetzt, React entwertet dabei jeden Inhalt. | | T-FGV-02 | Information Disclosure | apps/web/src/app/icon.svg | low | accept | Das Icon wird ohne Anmeldung ausgeliefert. Es ist eine Bildmarke ohne jeden Bezug zu Mandanten oder Nutzern — genau das ist der Zweck eines Favicons. Bewusst getragen. | | T-FGV-03 | Tampering | user-files/logo/ | low | mitigate | Die Lieferung liegt in einem vom Repository ausgeschlossenen Ordner und koennte unbemerkt ausgetauscht werden. Der Verify-Schritt von Task 1 vergleicht die kopierten Dateien byteweise gegen die Lieferung, sodass Kopie und Quelle nachweislich uebereinstimmen; ab dem Commit ist die Repository-Fassung die massgebliche. | | T-FGV-SC | Tampering | npm/pip/cargo installs | n/a | n/a | Dieser Plan installiert kein Paket — keine neue Abhaengigkeit, kein Legitimitaets-Tor noetig. | - `pnpm --filter @tessera/web exec vitest run` ist vollstaendig gruen (der bestehende Bestand plus der neue Komponententest). - `pnpm --filter @tessera/web type-check` ist sauber. - Der Gelbwert der Marke kommt im Web-Quellcode genau einmal vor, in `brand.ts`. - `apps/web/src/app/icon.svg` und `apps/web/src/app/apple-icon.png` sind byteidentisch zu den gelieferten Dateien. - In `apps/web/src/app/(auth)/` steht kein von Hand gesetzter Marken-Schriftzug mehr. - Die Abnahme aus Task 3 ist in beiden Erscheinungsbildern durchlaufen. - Browser-Tab, Kopfleiste, Seitenleisten-Schublade, Anmeldeseite in breiter und schmaler Ansicht sowie beide Passwort-Seiten zeigen das gelieferte Logo. - Schriftzug und Bildmarke bleiben in hellem UND dunklem Erscheinungsbild lesbar; die dunkle Platte verschwimmt im dunklen Erscheinungsbild nicht mit dem Hintergrund. - Es existiert genau eine Datei, die eine spaetere mandantenspezifische Gestaltung anfassen muss: `apps/web/src/components/brand/` mit der Farbkonstanten-Datei und der Logo-Komponente. - Von der Lieferung wurde nichts doppelt uebernommen; die nicht uebernommenen Dateien sind mit Grund benannt. - Es wurde keinerlei Hochlade-, Einstellungs- oder Mandanten-Funktion fuer das Logo gebaut. Create `.planning/quick/260907-fgv-tessera-logo-und-favicon-fest-einbauen-b/260907-fgv-SUMMARY.md` when done. Die SUMMARY muss festhalten: - welche gelieferten Dateien uebernommen wurden und welche mit welcher Begruendung nicht, - warum der Schriftzug als Text und nicht als Bilddatei ausgeliefert wird (Farbe fest eingebacken UND Schriftart Archivo nicht geladen), - wie die Kontur der Platte im dunklen Erscheinungsbild geloest ist und warum sie auf dem gelben Anmelde-Panel abgeschaltet ist, - dass der Gelbwert jetzt an genau einer Stelle steht und `--primary` bewusst unberuehrt blieb, - wie der Benutzer die beiden zur Freigabe gestellten Gestaltungsfragen entschieden hat. Ausserdem als Backlog-Punkt zu vermerken (vom Orchestrator einzureichen, in diesem Plan NICHT zu bauen): "Administrator kann das Erscheinungsbild je Mandant anpassen — eigenes Logo hochladen, Markenfarben setzen. Ansatzpunkt ist `apps/web/src/components/brand/`; die Oberflaeche zieht das Logo bereits ausschliesslich von dort."