slimcore-website/docs/handoff.md
Pascal Oelmann 3c79b63db5
Some checks failed
Deploy Marketing-Site / Lint + Smoke-Tests (push) Failing after 1m9s
Deploy Marketing-Site / Deploy auf Marketing-VPS (push) Failing after 0s
Deploy Marketing-Site / Deploy-Notification (push) Successful in 9s
Initial Astro-Build, Deployment-Setup und Forgejo-Workflow
- Astro 6 + React + Tailwind v4 Projekt-Skelett mit allen Marketing-Seiten
  (Home, Module, Tester, Souveränität, Roadmap, Kontakt, Impressum, Datenschutz)
- Self-hosted Outfit + JetBrains Mono Fonts (DSGVO)
- Marketing-Komponenten gemäss CLAUDE.md §5.6 (NumberedItem, ModuleCard,
  StatusDot, TechStrip, SovereigntyBlock, RoadmapTimeline, etc.)
- Module-Daten in src/content/module.ts als Single Source of Truth
- E2E Smoke-Tests via Playwright
- OG-Image-Generator
- Forgejo Workflow .forgejo/workflows/deploy.yml für Tier-2 Static Deploy
- Infra-as-Code Snapshot in infra/marketing-vps/
- Brand-System Submodule auf Forgejo umgezogen (war GitHub)
- Deployment- und Handoff-Dokumentation
- .DS_Store aus Tracking entfernt, .gitignore um Test-Artefakte ergaenzt
2026-05-05 01:59:35 +02:00

25 KiB
Raw Permalink Blame History

Phase 0 — Handoff

Stand: 2026-05-04

Was funktioniert

  • Astro 6.2.2 mit Static-Output, @astrojs/react, @astrojs/sitemap, @astrojs/mdx
  • Tailwind v4 via @tailwindcss/vite — alle Design-Tokens im @theme inline-Block in src/styles/global.css
  • Self-hosted Fonts in public/fonts/: Source Serif 4 Variable, Inter Variable, JetBrains Mono Variable (latin + latin-ext). Kein Google Fonts CDN.
  • Design-Tokens aus digiformer-brand-system.md korrekt umgesetzt: Electric Persimmon oklch(0.71 0.22 38), Text-on-Accent #2A0F02, alle Surface-/Text-/Border-Token
  • shadcn/ui-Primitives: Button (3 Varianten: default/secondary/ghost), Input, Textarea, Label, Dialog — alle mit Brand-Token statt hardcodierten Farben
  • BaseLayout mit korrektem <head> (OG-Tags, hreflang, canonical, font-preload), NavBar, Footer
  • NavBar: sticky, Mono-Wortmarke, 4 Nav-Links, DE/EN-Switcher (EN ausgegraut), CTA-Button, Hamburger-Icon mobil, Border-on-scroll
  • Footer: 4-Spalten (Marke, Produkt, Stack, Kontakt), Bottom-Bar mit Copyright + Impressum/Datenschutz
  • Home (index.astro): Hero-Sektion mit Eyebrow, H1 (Persimmon-Akzent auf "Grenzenlos"), Lead-Text, zwei CTAs
  • pnpm dev läuft auf localhost:4321, Typografie und Farben visuell verifiziert
  • Astro-Telemetrie deaktiviert

Verzeichnisstruktur

src/
├── components/
│   ├── primitives/     ← Button, Input, Textarea, Label, Dialog (React)
│   ├── marketing/      ← NavBar.astro, Footer.astro
│   └── islands/        ← (leer, für Phase 3: TesterForm, ContactForm)
├── content/            ← (leer, für Phase 1: module.ts)
├── layouts/            ← BaseLayout.astro
├── pages/              ← index.astro
└── styles/             ← global.css

Was als Nächstes (Phase 1 — Komponenten-Bibliothek)

Laut CLAUDE.md §9, Phase 1:

  1. Eyebrow, SectionHeading, NumberedItem — primitive Layout-Bausteine
  2. StatusDot, ModuleCard, ModuleGrid — Modul-Visualisierung
  3. TechStrip, SovereigntyBlock, RoadmapTimeline — Sektions-Komponenten
  4. ObjectionAnswer, CTABlock — Argumentations- und Abschluss-Komponenten
  5. Dev-Seite: /dev/components.astro — alle Bausteine in allen Varianten

Update 2026-05-04 — Hero Dark-Variante (verbindlich)

CLAUDE.md §7.1 wurde mit verbindlicher Spec für dunkle Hero-Variante erweitert. Umsetzung:

  • src/pages/index.astro — Hero-Sektion komplett umgebaut: #0E0F14-Hintergrund, Off-White-Text #F5F5F0, Persimmon-Eyebrow mit -Prefix in #FF6B2C. Wort „Grenzenlos" als Background-Highlight (#FF6B2C bg, #2A0F02 fg, padding 0 12px, box-decoration-break: clone, kein border-radius). Tech-Strip läuft im selben dunklen Hintergrund weiter (border 0.5px solid rgba(245,245,240,0.08), STACK-Label bei 0.85-Opacity, Items bei 0.55). Sentinel <div data-hero-end> markiert das Ende der dunklen Zone für die NavBar.
  • src/components/marketing/NavBar.astro — Dual-Mode-Implementierung. CSS-Klassen .nav-bar--dark und .nav-bar--light definieren je eigene Farb-Sätze (Logo, Links, Lang-Switch, CTA). Toggle per Scroll-Listener: getBoundingClientRect() des data-hero-end-Sentinels gegen NavBar-Höhe. Auf Nicht-Home-Seiten (Sentinel fehlt) immer Light-Mode. Transition 200ms.

Visuell verifiziert via preview_inspect: bg = rgb(14,15,20), Highlight-Farben korrekt, box-decoration-break: clone aktiv (mit -webkit-Prefix), NavBar-Dark-Mode bei scrollY=0, Light-Mode-Switch beim Sentinel-Crossing nachgewiesen.

Phase 1 abgeschlossen — Komponenten-Bibliothek

Alle 11 Marketing-Komponenten plus Daten-Foundation und Showcase-Seite stehen.

Neue Dateien:

  • src/content/module.ts — Modul-Typen, 19 Module aus Appendix A, statusLabel/pillarTitles-Maps
  • src/components/marketing/Eyebrow.astro — Mono-Caption mit Tone-Varianten (tertiary / accent / inverse), optionalem prefix (z.B. ) und Status-Pille
  • src/components/marketing/StatusDot.astro — vier Status-Glyphen rein per CSS (gefüllt / halbgefüllt / outline / dashed), monochrom, mit inverse-Variante für dunkle Hintergründe
  • src/components/marketing/SectionHeading.astroh2 mit optionalem Eyebrow + Subtitle, display-Variante für „große Versprechen"-Sektionen, inverse-Modus
  • src/components/marketing/NumberedItem.astro — Mono-Nummer + Serif-Title + Body-Slot
  • src/components/marketing/ModuleCard.astro / ModuleGrid.astro — eine Säulen-Karte und das 4-Spalten-Grid mit optionaler Status-Legende
  • src/components/marketing/TechStrip.astro — extrahiert aus index.astro, hat dark/light-Varianten
  • src/components/marketing/SovereigntyBlock.astro — 2-Spalten-Layout mit Display-Headline links und 2×2-Border-Left-Items rechts
  • src/components/marketing/RoadmapTimeline.astro — vertikale Linie mit Phase-Markern, current-Pille
  • src/components/marketing/ObjectionAnswer.astro — Serif-Frage als Quote, Body-Slot
  • src/components/marketing/CTABlock.astro — Eyebrow + Headline + Body + bis zu 3 CTAs (primary/secondary/ghost), inverse-Modus
  • src/pages/dev/components.astro — interne Showcase-Seite mit jeder Komponente in jeder Variante (nicht im Sitemap)
  • public/og-default.png — 1200×630 Platzhalter, gespiegeltes Hero-Layout, generiert via node scripts/generate-og.mjs
  • scripts/generate-og.mjs — Skript zur Regeneration
  • sharp zur Dev-Dependency-Liste ergänzt (für OG-Generierung; war ohnehin transitiv via Astro vorhanden)

Geänderte Dateien:

  • src/pages/index.astro — Tech-Strip-Inline-Code durch <TechStrip variant="dark" /> ersetzt, identisches visuelles Ergebnis
  • CLAUDE.md §0 + §4.1 — „Astro 5" → „Astro 6" (stillschweigender Versions-Update wie abgesprochen)

Visuell verifiziert: Komplettes Durchscrollen von /dev/components und Re-Check von /. Status-Glyphen monochrom korrekt, Persimmon-Akzent sparsam (nur Hero-Highlight, current-Pille, primary CTAs, Eyebrow-accent-Variante, Border-Left auf SovereigntyBlock-Items), keine Drop-Shadows oder Gradients.

Phase 2 abgeschlossen — Home

Alle 9 Sektionen aus CLAUDE.md §7.1 sind in src/pages/index.astro komponiert:

  1. Hero (dark) — unverändert aus Phase 0/1
  2. Tech-Strip (dark) — unverändert
  3. Modul-Landschaft — <ModuleGrid /> mit Legende, Eyebrow nutzt modules.length (aktuell 19), Footer-Link auf /module
  4. Was uns trennt — 3 <NumberedItem> 01/02/03, Surface-White-Hintergrund
  5. Souveränität — <SovereigntyBlock> mit dreizeiliger Display-Headline, vier Border-Left-Items
  6. Roadmap-Snapshot — <RoadmapTimeline> mit 4 Phasen, „Heute" mit Aktuell-Pille (Persimmon)
  7. Schnell-Argumentarium — 5 <ObjectionAnswer> im 2-Spalten-Grid, fünfter wraps unten allein (per Spec)
  8. Tester-Programm-Teaser — <CTABlock> mit Primary + Ghost-Mail-Link
  9. Final-CTA — <CTABlock inverse> mit Primary + Secondary, dunkler Hintergrund symmetrisch zum Hero

Visueller Rhythmus: Dunkel (Hero+Strip+Sentinel) → hell → surface-white → hell → surface-white → hell → surface-white → dunkel (Final-CTA). Wechselnde Bg-Töne strukturieren ohne Bordüren.

NavBar Dual-Mode verifiziert: bei scrollY=0 über Hero nav-bar--dark aktiv, beim Body-Scroll wechselt korrekt zu nav-bar--light. Beim Final-CTA bleibt sie hell (Sentinel ist nach Sektion 2 platziert).

Verifiziert per DOM-Inspection: Alle Sektionen rendern mit korrekter Bg, Eyebrow, Headline. Auto-Count im Modul-Eyebrow funktioniert (VIER SÄULEN · 19 MODULE).

Inhaltliche Hinweise an Pascal:

  • CLAUDE.md erwähnt mehrfach „16 Module", in module.ts (Appendix A) sind aber 19 hinterlegt. Der Eyebrow rendert dynamisch aus den Daten — 19 MODULE. Wenn 16 die richtige Marketing-Aussage ist, kürze ich die Modul-Liste; wenn 19 stimmt, aktualisiere ich CLAUDE.md §0/§1.
  • Die Aktuell-Pille auf der Roadmap nutzt Persimmon-Bg + dunklen Text — passt zum Brand-System §3.5 („Status-Pille aktuelle Phase").
  • Final-CTA-Headline ist bewusst eine zusammengefasste Version der zwei Sätze aus §7.1 §9 („30 Minuten, kostenlos, unverbindlich..." + „Sie schildern, wo Sie stehen..."). Wenn du beide Sätze willst, splitte ich es in body + headline.

Update — EN-Variante + Sprachumschaltung (vorgezogen aus Phase-2-Backlog)

EN war ursprünglich Phase 2 (CLAUDE.md §9 Phase 6: „EN-Variante mit astro-i18n oder Manual-Routing"). Auf Pascal-Wunsch jetzt vorgezogen.

Routing: Astro built-in i18n in astro.config.mjs, defaultLocale: 'de', prefixDefaultLocale: false. → / bleibt DE, /en/ ist EN.

Neue/Geänderte Dateien:

  • astro.config.mjs — i18n-Block, sitemap mit Locale-Map
  • src/content/module.tsnameEn, pillarTitleEn, descriptionEn pro Modul; statusLabelEn/pillarTitlesEn-Maps; Helper getModuleName/getStatusLabel/getPillarTitle(item, lang)
  • src/i18n/strings.ts — zentrale UI-Strings DE/EN für NavBar, Footer, Roadmap-Pille; getLangFromPath() und getLocalizedPath()
  • src/layouts/BaseLayout.astro — auto-detect Lang aus Pfad, <html lang>, hreflang DE/EN/x-default, og:locale, lokalisierte Default-Description
  • src/components/marketing/NavBar.astro — Sprachumschalter mit aktiv/inaktiv-States in beiden Modi (dark/light), Links via getLocalizedPath(), lang-spezifische Nav-Pfade (/module vs /en/module etc.)
  • src/components/marketing/Footer.astro — alle Strings aus i18n-Dict
  • src/components/marketing/StatusDot.astro / ModuleCard.astro / ModuleGrid.astro / RoadmapTimeline.astrolang-Prop (Default 'de')
  • src/pages/en/index.astro — komplette englische Home, gleiche 9 Sektionen mit übersetzten Inhalten

Verifiziert:

  • /<html lang="de">, deutsche Inhalte, DE aktiv im Switcher
  • /en/<html lang="en">, englische Inhalte, EN aktiv im Switcher
  • Switcher-Links: DE-Button auf /en/ führt zu /, EN-Button auf / führt zu /en/
  • hreflang reziprok auf beiden Seiten, x-default zeigt auf DE
  • Modul-Namen: „Aufgaben" (DE) / „Tasks" (EN), „Belege" / „Documents", etc. Status-Labels und Säulen-Titel ebenfalls übersetzt
  • NavBar-Dual-Mode funktioniert auf EN-Seite identisch (dark über Hero, light beim Body-Scroll)
  • /dev/components rendert weiterhin fehlerfrei (alle Komponenten haben lang-Default 'de')

Nicht-übersetzte Pfade (Hinweis): EN-NavBar verlinkt auf /en/module, /en/sovereignty, /en/roadmap, /en/tester — diese Seiten existieren noch nicht (auch nicht in DE), 404 bis Phase 3+4 sie liefern. Footer-Links analog. Sprachumschalter geht von /en/foo zurück nach /foo (oder umgekehrt) — wenn die DE-Variante nicht existiert, landet der User auf der DE-404 mit deutscher Sprache.

Inhaltliche Hinweise an Pascal:

  • Module-Eigennamen bleiben in beiden Sprachen identisch wo sinnvoll (CRM, Helpdesk, WhatsApp), übersetzte Konzepte sonst (Belege → Documents, Versand → Shipping, BuHa-Export → Accounting export, Personal → HR, Zeiterfassung → Time tracking)
  • „Tester" im EN als „Testers" für die Nav, „Become a tester" als CTA. Wenn du „Beta tester" oder „Pilot user" stärker findest, ändere ich
  • Hero-Headline EN: „Start lean. Grow without limits." mit Highlight nur auf „Grow" (statt „Grenzenlos"). Andere Variante denkbar: „Grow boundless." oder „Scale without limits." — sag wenn du eine bestimmte willst
  • Tech-Strip EN: DSGVO → GDPR (selbe Sache, andere Schreibung), Rest unverändert
  • Sektion 5 Souveränität: „Your data. Your country. Your control." spiegelt das DE 1:1
  • Imprint/Privacy: Deutsche Pflichtseiten haben deutsche URLs (/impressum, /datenschutz); EN-Footer linkt auf /en/imprint und /en/privacy. Diese müssen in Phase 4 angelegt werden — englische Pflichtseiten sind rechtlich zwar nicht erforderlich, aber Usability-relevant

Phase 3 abgeschlossen — Module + Tester (Scope C, ohne Form)

Pascal-Entscheidung: Form-Anbindung auf Phase 3.5 verschieben, weil app.slimcore.io produktiv noch nicht live ist (nur app.staging.slimcore.io). Marketing-Site geht ohne Form-Submission live, Tester-Anmeldung läuft über Mail + Calendly bis Production-App + Form fertig sind.

Neue Dateien:

  • src/pages/module.astro — DE Modul-Seite, Hero-light + ModuleFilter-Island + <noscript>-Fallback mit allen 19 Modulen statisch
  • src/pages/en/module.astro — EN-Variante
  • src/pages/tester.astro — DE Info-Seite mit 4 Sektionen (Wer/Was bekommen/Was geben/Worauf einstellen) + Pascal-Pull-Quote + CTA-Block (Mail + Calendly)
  • src/pages/en/tester.astro — EN-Variante
  • src/components/islands/ModuleFilter.tsx — React-Island mit Status- (5 Optionen) + Säulen-Filter (5 Optionen), Live-Counter, gerenderte Modul-Karten

Geänderte Dateien:

  • src/i18n/strings.tsfilter-Sektion mit Labels („Status", „Säule", „Alle", Singular/Plural), reziproke EN-Strings
  • src/styles/global.css.dot-Klassen (synchron mit StatusDot.astro) für die React-Island

Hydration-Issue gelöst: Erste Implementierung mit client:visible (laut CLAUDE.md §4.3 Empfehlung) hydratierte nicht — weder im Dev-Mode noch im Production-Build. Nach Bisect: client:load funktioniert sauber. Die exakte Ursache (vermutlich ein Bug in Astro 6 / React 19 mit client:visible + display: contents-Wrapper + IntersectionObserver auf 0×0-Element) habe ich nicht final isoliert, weil client:load für diese Seite ohnehin die richtige Wahl ist: Auf /module ist der Filter das Hauptinteraktionselement, sofortige Hydration kostet ~30 KB JS aber gibt unmittelbare Reaktivität ohne Layout-Shift. SSR-HTML enthält die volle Filter-UI + 19 Modul-Karten, also auch SEO sauber.

Verifiziert:

  • /module: H1 „Module", Eyebrow „MODULE · 19 GESAMT", Filter funktioniert (Verfügbar = 6 Module, Verfügbar + Pillar 01 = 1 Modul (CRM)), Counter aktualisiert sich live
  • /en/module: H1 „Modules", „Available" + „All" = 6 modules, Filter-Logik identisch
  • /tester: 5 Sektions-H2 inkl. „Anmeldung läuft aktuell per Mail oder Termin." (statt Form), Pascal-Pull-Quote im Serif-Style mit Border-Left-Akzent
  • /en/tester: spiegelnd mit „Sign-up currently runs by email or call."

Inhaltliche Hinweise an Pascal:

  • Tester-Seiten: Anmelde-Sektion ist als Übergangs-CTA gestaltet — Mail an hallo@slimcore.io?subject=Tester-Programm%20Phase%201 mit vorausgefülltem Subject, plus Calendly-Termin-Link. Sobald app.slimcore.io/api/public/leads produktiv steht, ersetzt der echte Form-Island diese Sektion.
  • Modul-Seite zeigt 19 Module aus module.ts als Single Source of Truth. Wenn du beim CLAUDE.md-Hinweis 16 → 19 noch eine Entscheidung treffen willst, gib Bescheid.

Phase 3.5 (verschoben) — Form-Anbindung

Aktivieren, sobald app.slimcore.io/api/public/leads produktiv ist:

  1. Form-Island als React client:load (oberhalb der Falte) auf /tester und /en/tester
  2. Felder gemäß CLAUDE.md §7.4: Firma, Name, E-Mail, Telefon (opt), Branche (Free-Text), Team-Größe (Slider 150), „Was nutzen Sie heute?", „Was wäre die größte Lücke?"
  3. Altcha-Integration (Self-hosted Proof-of-Work) + Honeypot-Feld
  4. POST an PUBLIC_LEADS_API_URL (env-var), Default Staging während Übergang
  5. Doppel-Opt-in über Brevo SMTP (Mail-Render in App, Versand Brevo)
  6. /danke und /en/thank-you Bestätigungsseiten
  7. API-Spec: slimcore-go-to-market.md §3 (liegt im App-Repo) — vor Implementierung lesen

Update — Dunkles Design auf allen Hero-Sektionen

Pascal-Wunsch: alle Seiten dunkler Hero, nicht nur Home. Umgesetzt für /module, /en/module, /tester, /en/tester und alle Phase-4-Seiten unten. NavBar wechselt überall sauber zwischen dark (über Hero) und light (Body) per [data-hero-end]-Sentinel.

Kleine Komponenten-Erweiterung: SectionHeading.astro bekam eyebrowPrefix (z.B. „▸") und eyebrowTone (default: accent bei inverse=true, sonst tertiary).

Phase 3.5 (verschoben) — Form-Anbindung

Aktivieren, sobald app.slimcore.io/api/public/leads produktiv ist:

  1. Form-Island als React client:load (oberhalb der Falte) auf /tester und /en/tester
  2. Felder gemäß CLAUDE.md §7.4
  3. Altcha-Integration + Honeypot
  4. POST an PUBLIC_LEADS_API_URL (env-var)
  5. Brevo SMTP für DOI
  6. /danke und /en/thank-you Bestätigungsseiten
  7. API-Spec: slimcore-go-to-market.md §3 (App-Repo) — vor Implementierung lesen

Phase 4 abgeschlossen — Restliche Seiten

10 neue Seiten (5 DE + 5 EN), alle mit dunklem Hero und Sentinel-NavBar-Toggle.

Neue Dateien:

  • src/pages/souveraenitaet.astro / src/pages/en/sovereignty.astro — 6 nummerierte Long-form-Sektionen (Warum jetzt → Hosting → OSS-Stack mit Tabelle → Keine US-Abhängigkeiten → Kein Lock-in → Optionaler Compliance-Layer)
  • src/pages/roadmap.astro / src/pages/en/roadmap.astro — 4 Phasen, dynamisch aus module.ts (alle Module mit Status, Säule, Beschreibung pro Phase)
  • src/pages/kontakt.astro / src/pages/en/contact.astro — 3 Kontaktwege (Mail, Calendly extern, Post). Calendly ist nur ein externer Link, kein Embed — vermeidet Cookie-Pflicht
  • src/pages/impressum.astro / src/pages/en/imprint.astro — TMG-Template mit [Platzhaltern] für Adresse/Geschäftsführer/HRB/USt-ID
  • src/pages/datenschutz.astro / src/pages/en/privacy.astro — DSGVO-Template, nennt Hetzner (DE), Brevo (FR), Calendly (US-Übergangs-Realität) als Auftragsverarbeiter. Bestätigt: keine Cookies, keine Analytics, self-hosted Fonts

Geänderte Dateien:

  • src/components/marketing/SectionHeading.astroeyebrowPrefix-Prop, eyebrowTone-Override; default Eyebrow-Tone wechselt smartly mit inverse
  • src/pages/{module,tester}.astro und EN-Pendants — dunkler Hero + Sentinel
  • src/pages/index.astro und EN-Pendant — Tech-Strip-Refactor (Phase 1)

Verifiziert: pnpm build erzeugt 17 Pages in 1.21s, alle 10 neuen Routen liefern HTTP 200, Hero-Bg = rgb(14,15,20), NavBar = dark bei scrollY=0, Tabellen rendern (Souveränität: 7 Reihen Stack-Tabelle), Footer-Links auf Impressum/Datenschutz funktionieren.

Inhaltliche Hinweise an Pascal — vor Production-Launch nötig:

  • Impressum: [Straße + Hausnummer], [PLZ Ort], [Rufnummer], [Amtsgericht], HRB [Nummer], DE [Nummer] — alle Platzhalter durch echte digiFORMER-Daten ersetzen.
  • Datenschutz: Template bildet Phase-1-Realität ab (keine Cookies, keine Analytics). Sobald Phase-3.5-Form aktiviert wird, muss Sektion „Tester-Anmeldung" mit Form-Daten, DOI, Altcha-PoW ergänzt werden. Empfehlung: vor Production-Launch durch Datenschutzberatung freigeben lassen.
  • Souveränität: Long-form ist als Astro-Markup geschrieben, nicht MDX. Falls du den Text öfter editierst, kann ich auf .mdx umbauen — aber für statische Marketing-Seite ist Astro-Markup näher dran.
  • Roadmap: Phasen-Beschreibungen und „Aktuell"-Pille für „Heute" gesetzt. Inhalte (welche Module welche Phase) kommen automatisch aus module.ts Status-Feld — wenn du die Liste pflegst, aktualisiert sich die Roadmap.
  • Kontakt: Adress-Block hat Platzhalter wie das Impressum. Zusätzlich erwähnt der Calendly-Block, dass es Übergangs-Realität ist und ein eigenes Termin-Modul folgt.

Update — Schriftarten auf Outfit umgestellt (Brand-System-Override)

Pascal-Entscheidung: SlimCore wechselt von Source Serif 4 + Inter (Brand-System §4.1 Default) auf Outfit für Headlines und Body. JetBrains Mono bleibt als Familien-Anker für Eyebrows, Stack-Strip und Wortmarke.

Begründungen:

  • Mobile-Performance: 833 KB Fonts → 102 KB (88 % Ersparnis)
  • Lighthouse Mobile: Performance 81 → 100/100, LCP 5.3s → 1.4s
  • Differenzierung zur Mutter-Marke digiFORMER (Editorial-Serif) — SlimCore positioniert sich klar als „Werkzeug-Marke" mit geometric-modern Sans
  • Tonalität §11 („pragmatisch, nicht idealistisch"): Outfit liest sich präziser-bestimmter als Inter

Brand-System-Implikation (für Pascal): Brand-System §4.1 nennt Source Serif 4 + Inter als Familien-Anker. Mit dem SlimCore-Switch auf Outfit wird einer der drei Familien-Anker gebrochen. Zwei Pfade vorwärts:

  1. SlimCore-spezifischer Override dokumentieren — andere Marken (digiFORMER, trendscout, SlimSafe) bleiben bei Source Serif 4 + Inter. Aktuell die De-facto-Realität, weil nur SlimCore live ist.
  2. Familien-System anpassen — Brand-System §4.1 darum erweitern, dass jede Marke eigene Sans wählen darf, solange JetBrains Mono als Familien-Anker bleibt. Die nummerierten Sektionen + Status-Glyphen + Mono-Eyebrows tragen dann die Familien-Konsistenz.

Empfehlung Variante 2 — JetBrains Mono ist eh der „technische Familien-Marker", und Editorial-Serif vs Geometric-Sans als Marken-Differenzierung ist eine starkere Architektur als „alle drei Schriften überall identisch".

Geänderte/Entfernte Dateien:

  • src/styles/global.css@font-face für Outfit (Latin + Latin-Ext via unicode-range), --font-sans/--font-serif zeigen beide auf Outfit (Alias bleibt für Tailwind-Kompatibilität), h1/h2/h3 mit letter-spacing: -0.01em für Geometric-Sans-Headlines
  • src/layouts/BaseLayout.astro — Preload-Tags auf Outfit + JetBrains Mono
  • public/fonts/outfit-variable.woff2 (32 KB) + outfit-variable-latin-ext.woff2 (15 KB) hinzugefügt
  • public/fonts/source-serif-4-variable.woff2 und inter-variable.woff2 entfernt (~778 KB gespart)
  • src/pages/dev/fonts.astro und public/fonts/comparison/ entfernt — Vergleichsseite hat ihren Zweck erfüllt
  • 11 temporäre fontsource-Packages aus devDependencies entfernt; nur @fontsource-variable/outfit bleibt

Verifiziert: Lighthouse Mobile 100/100/100/100, Desktop 100/100, alle 24 Playwright-Smoke-Tests grün.

Phase 5 — Polish & Deploy (im Gang)

5.1 ✓ robots.txt + Sitemap (sitemap-index.xml mit hreflang-Pairs, /dev/-Routen ausgefiltert) 5.2 ✓ Playwright-Smoke-Tests (24 Tests, alle Routen + Filter-Island + Sprachumschalter + Footer-Links) 5.3 ✓ Build-Artefakt-Analyse + Lighthouse-Audit (Performance/A11y/Best-Practices/SEO alle 100/100 Mobile + Desktop nach Outfit-Switch) 5.4 ✓ Deployment-Konzept als Tier-2 Standard dokumentiert: docs/deployment.md (Forgejo-First, kein Coolify, kein GitHub-Übergang)

Familienweiter Deployment-Standard (statt Coolify):

  • Tier 1 — Production SaaS mit Docker (SlimCore-Pattern): raw docker-compose.*.yml + Forgejo Actions SSH-Deploy + Branch-Protection. Für app.slimcore.io, app.fonboard.io, ggf. später app.slimsafe.io. Detaillierte Spec liegt im cockpit-Repo (docs/superpowers/specs/2026-04-30-production-infra-phase1-design.md).
  • Tier 2 — Static Marketing-Sites: Caddy + rsync (kein Docker pro Site nötig). Eine geteilte Caddy-Instanz auf einem Marketing-VPS hostet slimcore.io, digiformer.eu, slimsafe.io, fonboard.io. Forgejo Actions baut + rsync't pro Repo. Spec: docs/deployment.md in diesem Repo.

Warum kein Coolify: Single Point of Failure für alle Deployments, Drift zwischen UI-State und Repo-State, schwierig für HA-State-Cluster (Patroni + etcd + Redis Sentinel im SlimCore-App). Beim Tier-2-Pattern ist Docker nicht mal nötig — die Marketing-Site ist statisch, Caddy + rsync reicht.

Warum kein GitHub: Brand-System §7.5 sieht Forgejo als Migrationsziel vor. Für eine neu gebaute Marketing-Site lohnt sich der GitHub-Umweg nicht — wir starten direkt auf Forgejo. Cockpit (SlimCore-App) wird später migriert; Marketing-Site dient als niedrig-risikoreicher Forgejo-Pilot.

Neue Dateien (in diesem Repo):

  • docs/deployment.md — kompletter Deployment-Guide (Phase A: Forgejo-Server-Setup, Phase B: Marketing-VPS-Setup, Phase C: dieses Repo deployen, Phase D: weitere Marken nachziehen, Phase E: cockpit später migrieren)
  • .forgejo/workflows/deploy.yml — funktionsbereiter Workflow (Test → Build → Rsync → Verifikation → Mail-Notification via Brevo)
  • infra/marketing-vps/Caddyfile — Caddy-Config-Template mit Blöcken für alle 4 Marken-Sites + Status-Page
  • infra/marketing-vps/docker-compose.yml — Caddy + Uptime-Kuma-Stack
  • infra/marketing-vps/README.md — VPS-Setup-Anleitung inkl. rsync-deploy-User mit rrsync-Restriktion

Kostenbild Tier 2 (alle Marketing-Sites zusammen): ~€20/mo (Forgejo-VPS €5 + Marketing-VPS €5 + Storage-Box €10 geteilt mit anderen Backups).

Was Pascal jetzt tun muss

5.5 — Forgejo-VPS bestellen und Phase A durcharbeiten (~1 h) 5.6 — Marketing-VPS bestellen und Phase B durcharbeiten (~30 min) 5.7 — DNS-Records bei Registrar setzen (forge.digiformer.eu, slimcore.io, etc.) 5.8 — slimcore-website-Repo auf Forgejo spiegeln, Workflow läuft auto bei git push 5.9 — Smoke-Test gegen https://slimcore.io/, dann Launch

5.10 — Spätere Marketing-Sites einzeln nach Phase D nachziehen 5.11 — Cockpit-Migration als separater Plan in cockpit/docs/infrastructure/forgejo-migration.md

Offene Fragen vor Phase 2

  1. Mobile Hamburger-Drawer noch nicht implementiert — entschieden: Astro-Script + CSS-Transition. Wird in Phase 2 mit dem Home-Ausbau erledigt, da der Drawer nur sinnvoll testbar ist, wenn die Nav-Ziele auch existieren.
  2. OG-Image — erledigt, Platzhalter generiert. Replacement nur bei Bedarf, sonst Phase 5 (dynamisch).
  3. Astro-Version — erledigt, v6 behalten und in CLAUDE.md angepasst.