Relaunch Phase 1: Hero mit Deutschland-Karte, Kundenportal-Bühne, Leistungen, Preise, Unterseiten im neuen Layout

- Hero: wechselnde Überschrift (ein Durchlauf, Pause-Knopf), Karte zeichnet
  sich, roter Punkt auf Falkenstein
- Kundenportal: Bühne mit Browserfenster und Handy-Rahmen (Desktop),
  Wisch-Reihe (Handy), Schrittzeile statt „So arbeiten wir“
- Leistungen als nummerierte Zeilen, Preise aus src/data/abos.ts
- Unterseiten ohne eigenen Kopfbereich, Wortlaut der Rechtstexte unverändert
- Tests für Hero, Kundenportal, Leistungen/Preise und Layout

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-01 15:28:38 +00:00
1 parent 69d006334f
commit 1c8cfa5c1c
54 files changed
+4761 -1166

No files matched your search

+104
View File
@@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""
Relaunch: verkleinert die Rohexporte der Kundenportal-Screenshots für die Website.
docs/relaunch/assets/portal/*.webp (Rohdateien, doppelte Auflösung)
-> public/relaunch/portal/<name>-<breite>.webp
Aufruf (aus dem Projektverzeichnis): python3 scripts/relaunch-bilder.py
Nur die Bilder, die der Abschnitt "Kundenportal" (Spezifikation 7.2) wirklich zeigt, landen in public/.
Format und Budgets laut Spezifikation 9.5:
- WebP, Qualität etwa 80, Desktop-Bilder in 800 / 1200 / 1600 px Breite, Handy-Bilder in 390 / 780 px
- ein einzelnes Desktop-Bild höchstens 120 KB, ein einzelnes Handy-Bild höchstens 60 KB
(der lange Screenshot hat ein eigenes, größeres Budget, weil er mehr als dreimal so hoch ist)
- wird ein Budget überschritten, sinkt die Qualität pro Datei schrittweise (nicht unter MIN_QUALITAET)
Braucht Python 3 und Pillow mit WebP-Unterstützung.
"""
from pathlib import Path
import sys
from PIL import Image
WURZEL = Path(__file__).resolve().parent.parent
QUELLE = WURZEL / "docs" / "relaunch" / "assets" / "portal"
ZIEL = WURZEL / "public" / "relaunch" / "portal"
QUALITAET = 80
MIN_QUALITAET = 46
SCHRITT = 4
KB = 1024
BUDGET_DESKTOP = 120 * KB
BUDGET_HANDY = 60 * KB
# Der lange Screenshot ist gut dreimal so hoch wie ein Handy-Bild (5428 statt 1688 px). Sein Budget
# wächst mit der Höhe (je Bildschirmhöhe weiterhin unter 60 KB), sonst wäre der Text nur noch Matsch.
BUDGET_LANG = {390: 80 * KB, 780: 150 * KB}
# Dateiname (ohne .webp) -> (Art, Zielbreiten)
BILDER = {
# Desktop-Fenster (Roh 2880 x 1800)
"desktop-1-projekte": ("desktop", (800, 1200, 1600)),
"desktop-2-fortschritt": ("desktop", (800, 1200, 1600)),
"desktop-3b-chat-foto": ("desktop", (800, 1200, 1600)),
"desktop-4-rechnungen": ("desktop", (800, 1200, 1600)),
"desktop-7-news-pinnwand": ("desktop", (800, 1200, 1600)),
# Handy (Roh 780 x 1688, das lange Bild 780 x 5428)
"phone-1-projekte": ("handy", (390, 780)),
"phone-3-projektdetails": ("handy", (390, 780)),
"phone-4b-chat-foto": ("handy", (390, 780)),
"phone-5-rechnungen": ("handy", (390, 780)),
"phone-8-news-vorschau": ("handy", (390, 780)),
# langer Screenshot: behält sein hohes Seitenverhältnis (eigenes Budget, siehe BUDGET_LANG)
"phone-2-projekte-lang": ("handy", (390, 780)),
}
def speichern(bild: Image.Image, ziel: Path, budget: int) -> tuple[int, int]:
"""Speichert als WebP; senkt die Qualität, bis das Budget passt. Gibt (Bytes, Qualität) zurück."""
qualitaet = QUALITAET
while True:
bild.save(ziel, "WEBP", quality=qualitaet, method=6)
groesse = ziel.stat().st_size
if groesse <= budget or qualitaet - SCHRITT < MIN_QUALITAET:
return groesse, qualitaet
qualitaet -= SCHRITT
def main() -> int:
ZIEL.mkdir(parents=True, exist_ok=True)
# alte Ausgaben entfernen, damit nichts Verwaistes liegen bleibt
for alt in ZIEL.glob("*.webp"):
alt.unlink()
fehler = 0
summe = 0
print(f"{'Datei':44} {'Pixel':>11} {'KB':>7} {'Q':>3} Budget")
for name, (art, breiten) in BILDER.items():
quelle = QUELLE / f"{name}.webp"
if not quelle.exists():
print(f"FEHLT: {quelle}", file=sys.stderr)
fehler += 1
continue
roh = Image.open(quelle).convert("RGB")
for breite in breiten:
if name.endswith("-lang"):
budget = BUDGET_LANG[breite]
else:
budget = BUDGET_DESKTOP if art == "desktop" else BUDGET_HANDY
if breite > roh.width:
print(f"übersprungen: {name} {breite} (Rohbild nur {roh.width} px breit)")
continue
hoehe = round(roh.height * breite / roh.width)
klein = roh if breite == roh.width else roh.resize((breite, hoehe), Image.LANCZOS)
datei = ZIEL / f"{name}-{breite}.webp"
groesse, qualitaet = speichern(klein, datei, budget)
summe += groesse
ok = "ok" if groesse <= budget else f"ÜBER {budget // KB} KB"
print(f"{datei.name:44} {breite:>5}x{hoehe:<5} {groesse / KB:7.1f} {qualitaet:>3} {ok}")
print(f"Summe aller Dateien: {summe / KB:.0f} KB")
return 1 if fehler else 0
if __name__ == "__main__":
sys.exit(main())