Selaa lähdekoodia

Fix: Template-Script ergaenzt, Duplikate entfernt, fonts/ Platzhalter

Claude (vision4it) 6 päivää sitten
vanhempi
sitoutus
4b35e1e560
4 muutettua tiedostoa jossa 263 lisäystä ja 391 poistoa
  1. 0 0
      fonts/.gitkeep
  2. 0 29
      templates/README.md
  3. 0 362
      templates/SKILL.md
  4. 263 0
      templates/build_angebot_beispiel.js

+ 0 - 0
fonts/.gitkeep


+ 0 - 29
templates/README.md

@@ -1,29 +0,0 @@
-# vision4it-branding
-
-Zentrale Branding-Assets und Claude-Anleitung für vision4it-Kundendokumente
-(Angebote, Kurzkonzepte, Anleitungen im GSS-Stil).
-
-## Inhalt
-
-| Pfad | Inhalt |
-|---|---|
-| `SKILL.md` | Anleitung für Claude-Instanzen: Workflow, Struktur, Code-Template |
-| `colors.md` | Verbindliche Farbpalette |
-| `logo/logo.png` | Standard-Logo, transparent (helle Seiten) |
-| `logo/logo-white.png` | Weiße Variante (dunkles Deckblatt) |
-| `fonts/` | Font-Dateien (nur für PDF-Workflows — docx bettet keine Fonts ein) |
-| `templates/build_angebot_beispiel.js` | Geprüftes Referenz-Script: komplettes Angebot mit Deckblatt |
-
-## Nutzung durch Claude
-
-```bash
-git clone --depth 1 https://git.services.vision4it.de/vision4it/vision4it-branding.git /home/claude/branding
-```
-
-Details in `SKILL.md`.
-
-## TODO
-
-- [x] Logos vorhanden (aus Logo_vision4it_rgb_2023.pdf freigestellt)
-- [ ] Vektorversion des Logos (AI/EPS/SVG) von der Agentur besorgen und ablegen
-- [ ] Ggf. Fonts (Design System 500R, Presario Text — kommerziell) ablegen und in SKILL.md dokumentieren

+ 0 - 362
templates/SKILL.md

@@ -1,362 +0,0 @@
-# SKILL: vision4it Angebot & Kurzkonzept (GSS-Stil)
-
-## Zweck
-
-Diese Anleitung beschreibt, wie professionelle Kundendokumente im vision4it-Corporate-Design
-erstellt werden — Angebote/Kurzkonzepte (Deckblatt + Konzept) sowie Kundenanleitungen — im Stil des GSS-Angebots (German Security Service GmbH,
-Terminal-Server-Infrastrukturprojekt). Der gleiche Look wurde bereits für Autohaus Elegance
-(Managed Services) und das JTL/Reonic-Kurzkonzept (Solar-Kunde) wiederverwendet.
-
-**Zielgruppe:** Claude-Instanzen, die für Matthias / vision4it GmbH Angebotsdokumente erstellen.
-
----
-
-## Schreibweise (WICHTIG)
-
-**"vision4it" wird IMMER klein geschrieben** — auch am Satzanfang, in Überschriften,
-Fußzeilen und im Firmennamen "vision4it GmbH". Niemals "Vision4IT", "Vision4it" oder
-"VISION4IT" schreiben. Einzige Ausnahme: das Logo selbst (Grafik).
-
-## Kontext vision4it
-
-- **Firma:** vision4it GmbH, Rommerskirchen (NRW), MSP für KMU im Rheinland, ~10 Mitarbeiter
-- **Standard-Stack (taucht oft in Konzepten auf):** Proxmox/Ceph, OPNsense, TacticalRMM, PMG/Rspamd,
-  Exchange on-premises, 3CX, UniFi, Hudu (Doku), FileMaker
-- **Grundsatz:** ausschließlich Self-Hosted-Lösungen, keine Cloud-Abhängigkeiten
-- **Stundensatz (Referenz aus früheren Angeboten):** 120 €/h netto
-- **Sprache der Dokumente:** Deutsch, professionell, sachlich, kundengerecht
-
-**Verbindliche Firmenangaben (Fußzeilen, Impressumsblöcke):**
-```
-vision4it GmbH · Siemensstraße 8 · 41542 Dormagen
-Tel. 02133 8688890 · office@vision4it.de · vision4it.de
-Geschäftsführer: Matthias Gehlhaar, Phillip Gehlhaar
-Amtsgericht Mönchengladbach, HRB 22650 · USt-IdNr. DE369304034
-```
-
----
-
-## Technischer Workflow
-
-1. `SKILL.md` des docx-Skills lesen: `/mnt/skills/public/docx/SKILL.md` (Pflicht vor Codebeginn)
-2. **Branding-Assets aus dem Git-Repo holen** (Logo, Farben, ggf. Fonts):
-   ```bash
-   git clone --depth 1 https://git.services.vision4it.de/vision4it/vision4it-branding.git /home/claude/branding
-   ```
-   Danach verfügbar:
-   - `/home/claude/branding/logo/logo.png` — Standard-Logo (transparent, für helle Seiten)
-   - `/home/claude/branding/logo/logo-white.png` — weiße Variante (für dunkles Deckblatt)
-   - `/home/claude/branding/colors.md` — verbindliche Farbpalette
-   - `/home/claude/branding/fonts/` — Font-Dateien (nur für PDF-Workflows relevant)
-
-   Falls der Clone fehlschlägt (Netzwerk/Auth): ohne Logo weiterarbeiten, Firmenname
-   typografisch setzen (wie im Template) und Matthias auf das Problem hinweisen.
-3. Arbeitsverzeichnis anlegen: `mkdir -p /home/claude/<kunde>`
-4. `docx`-Library prüfen/installieren: `npm list -g docx || npm install -g docx`
-5. Build-Script(s) in Node.js erstellen (siehe Template unten):
-   - `build_deckblatt.js` → Deckblatt (eigene Datei oder erste Section)
-   - `build_doc.js` → Kurzkonzept (nummerierte Kapitel)
-   - Alternativ: ein Script mit mehreren Sections (Deckblatt = Section 1 ohne Kopf-/Fußzeile)
-6. Generieren: `node build_doc.js`
-7. **Validieren (Pflicht):** `python3 /mnt/skills/public/docx/scripts/office/validate.py <datei>.docx`
-8. Nach `/mnt/user-data/outputs/` kopieren und mit `present_files` präsentieren
-
----
-
-## Corporate Design (Farben)
-
-```javascript
-// vision4it Farbpalette (Hex ohne #, wie von docx-Library erwartet)
-const C_DARK   = "1A1A1A";  // Haupttext, Überschriften, dunkle Flächen (Deckblatt)
-const C_GREEN  = "13A983";  // Akzentfarbe (Linien, Hervorhebungen, Status-Elemente)
-const C_GREY   = "5C5C5C";  // Sekundärtext, Untertitel, Metadaten
-const C_LIGHT  = "F4F4F4";  // Helle Hintergrundflächen (Tabellen-Header, Infoboxen)
-const C_BORDER = "D0D0D0";  // Tabellenrahmen, Trennlinien
-```
-
-Regeln:
-- Grün (`13A983`) sparsam als Akzent einsetzen — Linien unter Überschriften, Kennzahlen,
-  Summenzeilen. Nicht als Flächenfarbe für Fließtext-Hintergründe.
-- Deckblatt IMMER hell/weiß (druckfreundlich!) — KEINE vollflächig dunklen Deckblätter,
-  die sind auf Bürodruckern nicht praktikabel. Kontrast über Typografie und Teal-Akzente.
-- Kein übermäßiges Bold im Fließtext.
-
----
-
-## Dokumentstruktur
-
-### Deckblatt (hell, druckfreundlich)
-- Weißer Hintergrund — nie vollflächig dunkel (Druckkosten, Qualität auf Bürodruckern)
-- Logo in Originalfarben (`logo.png`) OBEN RECHTS (alignment: RIGHT), groß (~247×60)
-- Kicker in Teal mit Sperrsatz (z. B. "ANGEBOT & KURZKONZEPT"), darunter großer dunkler Titel
-- Teal-Linie unter dem Untertitel als Akzent
-- Dokumenttyp: „Angebot" bzw. „Kurzkonzept"
-- Projekttitel (z. B. „IT-Infrastruktur Terminal-Server-Umgebung")
-- Kundenname + Standort
-- Datum, ggf. Angebotsnummer / Version
-- Kontaktblock vision4it GmbH (unten)
-- Keine Kopf-/Fußzeile auf dem Deckblatt (eigene Section)
-
-### Innenseiten: Kopf & Fuß
-- Logo klein (~124×30) OBEN RECHTS auf der ersten Innenseite
-- Fußzeile ZWEIZEILIG mit den vollständigen Firmenangaben (Block oben) in 7 pt Grau,
-  plus Angebotsnummer und Seitenzahl. Nie nur "vision4it GmbH · Seite X" — die
-  vollständigen Angaben sind Pflicht.
-
-### Kurzkonzept (nummerierte H1-Kapitel)
-Bewährte Gliederung aus dem GSS-Dokument (an Scope anpassen, nicht sklavisch kopieren):
-
-1. Ausgangslage / Ist-Zustand
-2. Zielsetzung
-3. Ziel-Architektur (Netz, Server, Virtualisierung)
-4. Netzwerk & Segmentierung (VLANs, Firewall, NAC/RADIUS)
-5. Server-Rollen (DC, Terminal Server, Exchange, Fileserver, Fachanwendungen)
-6. Sicherheit (Firewall-Regelwerk, Logging, Auditing)
-7. Backup & Monitoring (TacticalRMM, Backup-Konzept)
-8. Drucken (z. B. Pull-Printing) — falls relevant
-9. Fernzugriff / VPN
-10. E-Mail / Kollaboration
-11. Endgeräte-Management (GPO, TacticalRMM, BitLocker, kein lokaler Admin, USB gesperrt)
-12. Nächste Schritte (Bullets: Begehung, Detail-Design, Bestellung, Rollout, Schulung, Übergabe)
-13. Zeitplan & Aufwand (Phasentabelle + Kalenderzeit)
-
-**Bei Managed-Services-Verträgen** (wie Autohaus Elegance): Service-Beschreibung statt
-Projekt-Phasenplan — Leistungsumfang, SLA/Reaktionszeiten, monatlicher Preis, Laufzeit.
-
-### Phasentabelle (Projektangebote)
-Format wie bei GSS (Positionen mit Stunden, Zwischensumme, Puffer, Gesamtsumme):
-
-| Pos | Leistung | Stunden |
-|-----|----------|--------:|
-| 1 | Vorbereitung & Planung | … |
-| 2 | Hardware-Aufbau & Basis-Infrastruktur | … |
-| 3 | Server-Rollen | … |
-| 4 | Sicherheit & Monitoring | … |
-| 5 | Migration & Rollout | … |
-| 6 | Übergabe & Dokumentation (Hudu, Hypercare, Abnahme) | … |
-| | **Zwischensumme** | **…** |
-| 7 | Projektpuffer (15 %) | … |
-| | **Gesamt Dienstleistung** | **… h** |
-
-Dazu immer:
-- **Kalenderzeit** (z. B. „8–10 Wochen bei einem Techniker mit ~70 % Auslastung")
-- **Nicht enthalten:** Anfahrt, Hardware-Lieferzeit, laufender Wartungsvertrag, spätere Erweiterungen
-
-Preise nur nennen, wenn Matthias es explizit will — es gab Fälle, in denen die Preis-/Phasensektion
-nachträglich entfernt werden musste. Im Zweifel nachfragen.
-
----
-
-## Code-Template (Node.js, docx-Library)
-
-**Vollständiges, geprüftes Referenz-Script:** `templates/build_angebot_beispiel.js` im
-Branding-Repo — erzeugt ein komplettes Angebot (helles Deckblatt mit Logo rechts,
-Kurzkonzept, Phasentabelle, Firmenangaben-Fußzeile). Am besten dieses Script kopieren
-und Inhalte austauschen, statt von Null zu starten. Der Ausschnitt unten zeigt nur
-die Grundstruktur:
-
-```javascript
-const fs = require('fs');
-const {
-  Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
-  AlignmentType, HeadingLevel, BorderStyle, WidthType, ShadingType,
-  PageNumber, Header, Footer, VerticalAlign, LevelFormat
-} = require('docx');
-
-// vision4it Farben
-const C_DARK = "1A1A1A", C_GREEN = "13A983", C_GREY = "5C5C5C",
-      C_LIGHT = "F4F4F4", C_BORDER = "D0D0D0";
-
-// --- Helpers ---------------------------------------------------------------
-const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
-const noBorders = { top: noBorder, bottom: noBorder, left: noBorder, right: noBorder };
-
-const cell = (children, opts = {}) => new TableCell({
-  borders: opts.borders || noBorders,
-  width: opts.width ? { size: opts.width, type: WidthType.DXA } : undefined,
-  shading: opts.shade ? { fill: opts.shade, type: ShadingType.CLEAR } : undefined,
-  margins: opts.margins || { top: 60, bottom: 60, left: 80, right: 80 },
-  verticalAlign: opts.valign,
-  columnSpan: opts.colSpan,
-  children: Array.isArray(children) ? children : [children],
-});
-
-const H1 = (text) => new Paragraph({
-  heading: HeadingLevel.HEADING_1,
-  spacing: { before: 320, after: 160 },
-  children: [new TextRun({ text, bold: true, color: C_DARK, size: 28 })],
-});
-
-const H2 = (text) => new Paragraph({
-  heading: HeadingLevel.HEADING_2,
-  spacing: { before: 240, after: 120 },
-  children: [new TextRun({ text, bold: true, color: C_DARK, size: 24 })],
-});
-
-const P = (text) => new Paragraph({
-  spacing: { after: 120 },
-  children: [new TextRun({ text, size: 22, color: C_DARK })],
-});
-
-const Bullet = (text) => new Paragraph({
-  numbering: { reference: "bullets", level: 0 },
-  spacing: { after: 60 },
-  children: [new TextRun({ text, size: 22, color: C_DARK })],
-});
-
-// --- Dokument --------------------------------------------------------------
-const doc = new Document({
-  numbering: {
-    config: [{
-      reference: "bullets",
-      levels: [{
-        level: 0, format: LevelFormat.BULLET, text: "\u2022",
-        alignment: AlignmentType.LEFT,
-        style: { paragraph: { indent: { left: 720, hanging: 360 } } },
-      }],
-    }],
-  },
-  sections: [
-    // Section 1: Deckblatt (ohne Kopf-/Fußzeile)
-    {
-      properties: { page: { margin: { top: 1134, bottom: 1134, left: 1134, right: 1134 } } },
-      children: [
-        // Branding-Block, Titel, Kunde, Datum — mit Tabellen/Paragraphs layouten
-      ],
-    },
-    // Section 2: Kurzkonzept (mit Fußzeile + Seitenzahlen)
-    {
-      properties: { page: { margin: { top: 1134, bottom: 1134, left: 1134, right: 1134 } } },
-      footers: {
-        default: new Footer({
-          children: [new Paragraph({
-            alignment: AlignmentType.RIGHT,
-            children: [
-              new TextRun({ text: "vision4it GmbH  |  Seite ", size: 16, color: C_GREY }),
-              new TextRun({ children: [PageNumber.CURRENT], size: 16, color: C_GREY }),
-            ],
-          })],
-        }),
-      },
-      children: [
-        H1("1. Ausgangslage"),
-        P("…"),
-        // weitere Kapitel …
-      ],
-    },
-  ],
-});
-
-Packer.toBuffer(doc).then(buffer => {
-  fs.writeFileSync("/home/claude/Kurzkonzept_<Kunde>.docx", buffer);
-  console.log("OK");
-});
-```
-
-### Logo einbinden (ImageRun)
-
-Logo-Fakten (aus `Logo_vision4it_rgb_2023.pdf` extrahiert):
-- Seitenverhältnis **4,12 : 1** (828 × 201 px nativ, Rasterquelle — nicht größer als ~7 cm
-  Breite in Druckdokumenten verwenden, sonst wird es unscharf)
-- Empfohlene Maße: Deckblatt `{ width: 247, height: 60 }`, Kopfzeile `{ width: 124, height: 30 }`
-- CI-Schriften im Logo: Design System 500R (Schriftzug), Presario Text (Tagline) —
-  beide kommerziell; Fließtext in Dokumenten trotzdem Calibri/Arial.
-
-```javascript
-const { ImageRun } = require('docx');
-
-const logoBuffer = fs.readFileSync('/home/claude/branding/logo/logo.png'); // Standard: farbiges Logo
-
-new Paragraph({
-  alignment: AlignmentType.LEFT,
-  children: [new ImageRun({
-    data: logoBuffer,
-    transformation: { width: 206, height: 50 }, // Seitenverhältnis 4.12:1 einhalten!
-    type: 'png',
-  })],
-});
-```
-
-- Überall `logo.png` (farbig). `logo-white.png` nur für dunkle digitale Anwendungen
-  (Präsentations-Titelfolien, Website), NICHT für Druck-Deckblätter.
-
-Hinweise:
-- A4, 2 cm Ränder (1134 DXA).
-- Umlaute/Sonderzeichen im Script als Unicode-Escapes nur wo nötig — UTF-8 funktioniert direkt.
-- Bei Bullets: `indent` nicht zusätzlich zur `numbering`-Referenz setzen (Konflikt, bekannter Bug
-  aus früherer Session).
-- Tabellen mit `C_LIGHT`-Header-Zeile und `C_BORDER`-Rahmen; Summenzeilen bold, ggf. grüner Akzent.
-
----
-
-## Dokumenttyp: Kundenanleitung
-
-Für Schritt-für-Schritt-Anleitungen an Endanwender (z. B. RDP-Zugang, VPN einrichten,
-E-Mail auf dem Handy). Gleiche Farben, gleiche Fußzeile mit Firmenangaben, Logo oben
-rechts — und IMMER mit Deckblatt (gleicher Aufbau wie beim Angebot, eigene Section
-ohne Word-Fußzeile). Auf dem Deckblatt: Kicker "ANLEITUNG" in Teal statt
-"ANGEBOT & KURZKONZEPT", Titel = Thema (z. B. "RDP-Verbindung einrichten"),
-Untertitel = Zielgruppe/System, Metadaten-Tabelle mit Kunde, Datum, Version —
-ohne Angebots-Nr. In der Innenseiten-Fußzeile entfällt die Angebots-Nr. ebenfalls;
-stattdessen Dokumenttitel + Seitenzahl.
-
-**Oberste Regel: KURZ UND KNAPP.** Zielgruppe sind Endanwender, keine Techniker.
-Erfahrungswert: Eine frühere RDP-Anleitung wurde als zu ausführlich zurückgewiesen —
-lieber 1–2 Seiten, die jemand wirklich liest, als 5 Seiten Vollständigkeit.
-
-Struktur (nach dem Deckblatt):
-1. Kurzer Einleitungssatz (was erreicht der Anwender mit dieser Anleitung)
-2. Infobox (C_LIGHT-Hintergrund, dünner Rahmen): die konkreten Verbindungsdaten /
-   Zugangsinfos als Label-Wert-Tabelle (Server-IP, Benutzername-Schema o. ä.)
-3. Voraussetzungen: max. 3–4 Bullets
-4. Schritt-für-Schritt: nummerierte Schritte, je Schritt EIN Satz Anweisung, optional
-   ein kurzer Hinweis. Keine Screenshots-Platzhalter, keine Theorie-Exkurse.
-5. Bei Problemen: kleine Tabelle Problem → Lösung (max. 4–5 Zeilen), darunter
-   ein Satz: "Weitere Hilfe: vision4it Support, Tel. 02133 8688890, office@vision4it.de"
-
-Sprachliche Regeln:
-- Anwender werden gesiezt ("Öffnen Sie...", "Geben Sie ... ein")
-- Imperativ, aktiv, ein Schritt = eine Handlung
-- Fachbegriffe nur wenn nötig, dann in einem Halbsatz erklärt
-- Platzhalter für kundenspezifische Werte klar markieren: [IHR BENUTZERNAME]
-
-Dateiname: `Anleitung_<Thema>_<Kunde>.docx`
-
----
-
-## Dokumenttyp: Managed-Services-Angebot
-
-Wie Angebot/Kurzkonzept, aber: Service-Beschreibung statt Projekt-Phasenplan.
-Abschnitte: Leistungsumfang (Bullets pro Service-Baustein), Reaktionszeiten/SLA,
-monatlicher Preis, Laufzeit & Kündigung, Nicht enthalten. Referenz: Autohaus
-Elegance (IT Essential, 1.200 €/Monat).
-
----
-
-## Checkliste vor Abgabe
-
-- [ ] docx-SKILL.md gelesen
-- [ ] Branding-Repo geclont, farbiges Logo eingebunden
-- [ ] Farben exakt aus der Palette (kein Blau, kein Apfelgrün 7CB342 aus Altdokumenten — aktueller
-      Standard ist Dunkelgrau/Teal aus dem Logo)
-- [ ] Deckblatt als eigene Section ohne Word-Fußzeile, hell/weiß, Logo oben rechts,
-      Firmenangaben-Block unten auf der Seite
-- [ ] Fußzeile Innenseiten: vollständige Firmenangaben (Adresse, GF, HRB, USt-IdNr.)
-- [ ] Kapitel durchnummeriert, Nummerierung konsistent (bei Löschungen neu durchzählen!)
-- [ ] Phasentabelle: Zwischensumme + 15 % Puffer + Gesamtsumme
-- [ ] Kalenderzeit-Angabe und „Nicht enthalten"-Block vorhanden
-- [ ] `validate.py` erfolgreich durchgelaufen
-- [ ] Rendern (soffice → pdftoppm) und Seitenzahl/Überlauf prüfen — Spacer-Höhen auf dem
-      Deckblatt sind die häufigste Überlauf-Ursache
-- [ ] Datei in `/mnt/user-data/outputs/`, per `present_files` präsentiert
-- [ ] Bei Unsicherheit über Preisnennung: Matthias fragen
-
----
-
-## Bekannte Varianten (Referenzprojekte)
-
-| Projekt | Typ | Besonderheit |
-|---|---|---|
-| GSS German Security Service | Infrastruktur-Projekt | Vollständiger Phasenplan, 221 h, Dual EPYC/ZFS/VLAN/NAC |
-| Autohaus Elegance | Managed Services | Service-Beschreibung statt Phasenplan, 1.200 €/Monat IT Essential |
-| Solar-Kunde (JTL/Reonic) | Konzept + Middleware | Kurzkonzept ohne Preissektion (nachträglich entfernt), Hosting-Produkte separat |

+ 263 - 0
templates/build_angebot_beispiel.js

@@ -0,0 +1,263 @@
+const fs = require('fs');
+const {
+  Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
+  AlignmentType, HeadingLevel, BorderStyle, WidthType, ShadingType,
+  PageNumber, Footer, VerticalAlign, LevelFormat, ImageRun
+} = require('docx');
+
+// vision4it Farben (aus branding/colors.md)
+const C_DARK = "1A1A1A", C_GREEN = "13A983", C_GREY = "5C5C5C",
+      C_LIGHT = "F4F4F4", C_BORDER = "D0D0D0";
+
+const LOGO = fs.readFileSync('/home/claude/branding/logo/logo.png');
+const LOGO_WHITE = fs.readFileSync('/home/claude/branding/logo/logo-white.png');
+
+// --- Helpers ---------------------------------------------------------------
+const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
+const noBorders = { top: noBorder, bottom: noBorder, left: noBorder, right: noBorder };
+const thinBorder = { style: BorderStyle.SINGLE, size: 4, color: C_BORDER };
+const boxBorders = { top: thinBorder, bottom: thinBorder, left: thinBorder, right: thinBorder };
+
+const H1 = (text) => new Paragraph({
+  heading: HeadingLevel.HEADING_1,
+  spacing: { before: 340, after: 60 },
+  border: { bottom: { style: BorderStyle.SINGLE, size: 8, color: C_GREEN, space: 4 } },
+  children: [new TextRun({ text, bold: true, color: C_DARK, size: 28, font: "Calibri" })],
+});
+
+const H2 = (text) => new Paragraph({
+  heading: HeadingLevel.HEADING_2,
+  spacing: { before: 240, after: 100 },
+  children: [new TextRun({ text, bold: true, color: C_DARK, size: 24, font: "Calibri" })],
+});
+
+const P = (text, opts = {}) => new Paragraph({
+  spacing: { after: 120 },
+  alignment: AlignmentType.JUSTIFIED,
+  children: [new TextRun({ text, size: 22, color: opts.color || C_DARK, font: "Calibri" })],
+});
+
+const Bullet = (text) => new Paragraph({
+  numbering: { reference: "bullets", level: 0 },
+  spacing: { after: 60 },
+  children: [new TextRun({ text, size: 22, color: C_DARK, font: "Calibri" })],
+});
+
+const Spacer = (h) => new Paragraph({ spacing: { after: h }, children: [] });
+
+// Phasentabelle
+const phaseRow = (pos, leistung, stunden, opts = {}) => new TableRow({
+  children: [
+    new TableCell({
+      borders: { top: noBorder, left: noBorder, right: noBorder, bottom: thinBorder },
+      width: { size: 900, type: WidthType.DXA },
+      shading: opts.shade ? { fill: opts.shade, type: ShadingType.CLEAR } : undefined,
+      margins: { top: 80, bottom: 80, left: 100, right: 100 },
+      children: [new Paragraph({ alignment: AlignmentType.CENTER, children: [
+        new TextRun({ text: pos, size: 22, bold: opts.bold, color: C_DARK, font: "Calibri" })] })],
+    }),
+    new TableCell({
+      borders: { top: noBorder, left: noBorder, right: noBorder, bottom: thinBorder },
+      width: { size: 6600, type: WidthType.DXA },
+      shading: opts.shade ? { fill: opts.shade, type: ShadingType.CLEAR } : undefined,
+      margins: { top: 80, bottom: 80, left: 100, right: 100 },
+      children: [new Paragraph({ children: [
+        new TextRun({ text: leistung, size: 22, bold: opts.bold, color: C_DARK, font: "Calibri" })] })],
+    }),
+    new TableCell({
+      borders: { top: noBorder, left: noBorder, right: noBorder, bottom: thinBorder },
+      width: { size: 1860, type: WidthType.DXA },
+      shading: opts.shade ? { fill: opts.shade, type: ShadingType.CLEAR } : undefined,
+      margins: { top: 80, bottom: 80, left: 100, right: 100 },
+      children: [new Paragraph({ alignment: AlignmentType.RIGHT, children: [
+        new TextRun({ text: stunden, size: 22, bold: opts.bold, color: opts.green ? C_GREEN : C_DARK, font: "Calibri" })] })],
+    }),
+  ],
+});
+
+const phaseTable = () => new Table({
+  width: { size: 9360, type: WidthType.DXA },
+  columnWidths: [900, 6600, 1860],
+  rows: [
+    new TableRow({ tableHeader: true, children: [
+      ...[["Pos", 900, AlignmentType.CENTER], ["Leistung", 6600, AlignmentType.LEFT], ["Stunden", 1860, AlignmentType.RIGHT]].map(([t, w, al]) =>
+        new TableCell({
+          borders: { top: noBorder, left: noBorder, right: noBorder, bottom: { style: BorderStyle.SINGLE, size: 8, color: C_GREEN } },
+          width: { size: w, type: WidthType.DXA },
+          shading: { fill: C_LIGHT, type: ShadingType.CLEAR },
+          margins: { top: 80, bottom: 80, left: 100, right: 100 },
+          children: [new Paragraph({ alignment: al, children: [
+            new TextRun({ text: t, size: 22, bold: true, color: C_DARK, font: "Calibri" })] })],
+        })),
+    ]}),
+    phaseRow("1", "Vorbereitung & Planung (Bestandsaufnahme, Detail-Design, Projektmanagement)", "10"),
+    phaseRow("2", "Basis-Infrastruktur (Proxmox-Host, OPNsense, VLAN-Segmentierung)", "14"),
+    phaseRow("3", "Server-Rollen (DC, Fileserver, Fachanwendung)", "18"),
+    phaseRow("4", "Sicherheit & Monitoring (Firewall-Regelwerk, TacticalRMM, Backup)", "12"),
+    phaseRow("5", "Migration & Rollout (Datenübernahme, Client-Onboarding, Schulung)", "16"),
+    phaseRow("6", "Übergabe & Dokumentation (Hudu, Hypercare, Abnahme)", "8"),
+    phaseRow("", "Zwischensumme", "78", { bold: true, shade: C_LIGHT }),
+    phaseRow("7", "Projektpuffer (15 %)", "12"),
+    phaseRow("", "Gesamt Dienstleistung", "90 h", { bold: true, shade: C_LIGHT, green: true }),
+  ],
+});
+
+// Infozeile Deckblatt
+const coverMeta = (label, value) => new Paragraph({
+  spacing: { after: 60 },
+  children: [
+    new TextRun({ text: label + "   ", size: 20, color: "9E9E9E", font: "Calibri" }),
+    new TextRun({ text: value, size: 22, color: "FFFFFF", font: "Calibri" }),
+  ],
+});
+
+// --- Dokument --------------------------------------------------------------
+const doc = new Document({
+  numbering: {
+    config: [{
+      reference: "bullets",
+      levels: [{
+        level: 0, format: LevelFormat.BULLET, text: "\u2022",
+        alignment: AlignmentType.LEFT,
+        style: { paragraph: { indent: { left: 720, hanging: 360 } } },
+      }],
+    }],
+  },
+  styles: {
+    default: { document: { run: { font: "Calibri", size: 22, color: C_DARK } } },
+  },
+  sections: [
+    // ============ Section 1: Deckblatt (hell, druckfreundlich, ohne Fußzeile) ============
+    {
+      properties: { page: { margin: { top: 1700, bottom: 1134, left: 1418, right: 1418 } } },
+      children: [
+        new Paragraph({
+          alignment: AlignmentType.RIGHT,
+          spacing: { after: 3000 },
+          children: [new ImageRun({ data: LOGO, transformation: { width: 247, height: 60 }, type: 'png' })],
+        }),
+        new Paragraph({
+          spacing: { after: 100 },
+          children: [new TextRun({ text: "ANGEBOT & KURZKONZEPT", size: 22, color: C_GREEN, bold: true, font: "Calibri", characterSpacing: 40 })],
+        }),
+        new Paragraph({
+          spacing: { after: 140 },
+          children: [new TextRun({ text: "IT-Infrastruktur", size: 72, bold: true, color: C_DARK, font: "Calibri" })],
+        }),
+        new Paragraph({
+          spacing: { after: 200 },
+          border: { bottom: { style: BorderStyle.SINGLE, size: 12, color: C_GREEN, space: 12 } },
+          children: [new TextRun({ text: "Modernisierung Serverumgebung & Netzwerk", size: 30, color: C_GREY, font: "Calibri" })],
+        }),
+        Spacer(2600),
+        new Table({
+          width: { size: 9071, type: WidthType.DXA },
+          columnWidths: [2200, 6871],
+          rows: [
+            ["Kunde", "Musterfirma GmbH, Rommerskirchen"],
+            ["Datum", "21. Juli 2026"],
+            ["Angebots-Nr.", "AN-2026-042  \u00b7  Version 1.0"],
+            ["Ansprechpartner", "Matthias, vision4it GmbH"],
+          ].map(([label, value]) => new TableRow({
+            children: [
+              new TableCell({
+                borders: { top: noBorder, left: noBorder, right: noBorder, bottom: thinBorder },
+                width: { size: 2200, type: WidthType.DXA },
+                margins: { top: 100, bottom: 100, left: 0, right: 100 },
+                children: [new Paragraph({ children: [new TextRun({ text: label, size: 20, color: C_GREY, font: "Calibri" })] })],
+              }),
+              new TableCell({
+                borders: { top: noBorder, left: noBorder, right: noBorder, bottom: thinBorder },
+                width: { size: 6871, type: WidthType.DXA },
+                margins: { top: 100, bottom: 100, left: 100, right: 0 },
+                children: [new Paragraph({ children: [new TextRun({ text: value, size: 22, color: C_DARK, font: "Calibri" })] })],
+              }),
+            ],
+          })),
+        }),
+        Spacer(2200),
+        new Paragraph({
+          border: { top: { style: BorderStyle.SINGLE, size: 4, color: C_BORDER, space: 8 } },
+          spacing: { after: 40 },
+          children: [new TextRun({ text: "vision4it GmbH  \u00b7  Siemensstra\u00dfe 8  \u00b7  41542 Dormagen  \u00b7  Tel. 02133 8688890  \u00b7  office@vision4it.de  \u00b7  vision4it.de", size: 16, color: C_GREY, font: "Calibri" })],
+        }),
+        new Paragraph({
+          children: [new TextRun({ text: "Gesch\u00e4ftsf\u00fchrer: Matthias Gehlhaar, Phillip Gehlhaar  \u00b7  Amtsgericht M\u00f6nchengladbach, HRB 22650  \u00b7  USt-IdNr. DE369304034", size: 16, color: C_GREY, font: "Calibri" })],
+        }),
+      ],
+    },
+    // ============ Section 2: Kurzkonzept (hell, mit Fußzeile) ============
+    {
+      properties: { page: { margin: { top: 1134, bottom: 1134, left: 1134, right: 1134 } } },
+      footers: {
+        default: new Footer({
+          children: [
+            new Paragraph({
+              border: { top: { style: BorderStyle.SINGLE, size: 4, color: C_BORDER, space: 4 } },
+              spacing: { after: 20 },
+              children: [new TextRun({ text: "vision4it GmbH  \u00b7  Siemensstra\u00dfe 8  \u00b7  41542 Dormagen  \u00b7  Tel. 02133 8688890  \u00b7  office@vision4it.de", size: 14, color: C_GREY, font: "Calibri" })],
+            }),
+            new Paragraph({
+              children: [
+                new TextRun({ text: "Gesch\u00e4ftsf\u00fchrer: Matthias Gehlhaar, Phillip Gehlhaar  \u00b7  Amtsgericht M\u00f6nchengladbach, HRB 22650  \u00b7  USt-IdNr. DE369304034", size: 14, color: C_GREY, font: "Calibri" }),
+                new TextRun({ text: "\u2003\u2003Angebot AN-2026-042  \u00b7  Seite ", size: 14, color: C_GREY, font: "Calibri" }),
+                new TextRun({ children: [PageNumber.CURRENT], size: 14, color: C_GREY, font: "Calibri" }),
+              ],
+            }),
+          ],
+        }),
+      },
+      children: [
+        new Paragraph({
+          alignment: AlignmentType.RIGHT,
+          spacing: { after: 300 },
+          children: [new ImageRun({ data: LOGO, transformation: { width: 124, height: 30 }, type: 'png' })],
+        }),
+
+        H1("1. Ausgangslage"),
+        P("Die Musterfirma GmbH betreibt derzeit einen einzelnen physischen Server (Windows Server 2016) ohne Virtualisierung, auf dem Domänencontroller, Dateiablage und die Warenwirtschaft gemeinsam laufen. Das Backup erfolgt manuell auf USB-Festplatten, eine Netzwerksegmentierung existiert nicht. Der Support für das Betriebssystem ist ausgelaufen; Ausfälle einzelner Dienste betreffen unmittelbar den gesamten Betrieb."),
+
+        H1("2. Zielsetzung"),
+        Bullet("Ablösung der Altumgebung durch eine virtualisierte, wartbare Infrastruktur"),
+        Bullet("Trennung der Dienste in eigene virtuelle Maschinen mit definierten Ressourcen"),
+        Bullet("Automatisiertes, überwachtes Backup mit täglicher Erfolgskontrolle"),
+        Bullet("Segmentiertes Netzwerk mit zentraler Firewall und klarem Regelwerk"),
+        Bullet("Proaktives Monitoring und Patch-Management durch vision4it"),
+
+        H1("3. Ziel-Architektur"),
+        P("Als Virtualisierungsplattform kommt Proxmox VE auf einem neuen Server-Host zum Einsatz. Die bisherigen Dienste werden in getrennte virtuelle Maschinen aufgeteilt: Domänencontroller, Fileserver und Applikationsserver für die Warenwirtschaft. Am Netzwerkrand ersetzt eine OPNsense-Firewall den vorhandenen Consumer-Router und übernimmt Routing, Regelwerk und VPN-Zugänge. Alle Systeme werden self-hosted beim Kunden betrieben; es entstehen keine Cloud-Abhängigkeiten."),
+
+        H1("4. Netzwerk & Segmentierung"),
+        P("Das Netzwerk wird in getrennte VLANs aufgeteilt: Server, Clients, Drucker, Gäste-WLAN und Management. Der Verkehr zwischen den Segmenten läuft ausschließlich über die OPNsense und wird durch ein dokumentiertes Regelwerk kontrolliert. Der Gastzugang bleibt vollständig vom Firmennetz isoliert."),
+
+        H1("5. Backup & Monitoring"),
+        P("Die Datensicherung erfolgt über Proxmox Backup Server auf ein dediziertes Sicherungsziel mit täglichen, verschlüsselten Sicherungen und definierter Aufbewahrung. Alle Systeme werden in das Monitoring von vision4it (TacticalRMM) aufgenommen: Patch-Management, Ereignisüberwachung und tägliche Backup-Kontrolle inklusive."),
+
+        H1("6. Nächste Schritte"),
+        Bullet("Standortbegehung und Bestandsaufnahme"),
+        Bullet("Detail-Design mit Netzplan, Firewall-Regelwerk und IP-Konzept"),
+        Bullet("Hardware-Bestellung (Server, Switch, Firewall)"),
+        Bullet("Umsetzung mit Parallelbetrieb und geplanter Umschaltung"),
+        Bullet("Einweisung der Mitarbeiter und Übergabe in den Regelbetrieb"),
+
+        H1("7. Zeitplan & Aufwand"),
+        P("Die Umsetzung gliedert sich in sechs Phasen. Die Schätzung umfasst Aufbau, Konfiguration, Migration und eine Hypercare-Phase nach dem Go-Live."),
+        Spacer(100),
+        phaseTable(),
+        Spacer(160),
+        H2("Kalenderzeit"),
+        P("Bei einem Techniker mit etwa 70 % Auslastung auf diesem Projekt ergibt sich eine Laufzeit von rund 4 bis 5 Wochen vom Kick-off bis zum Ende der Hypercare-Phase."),
+        H2("Nicht enthalten"),
+        Bullet("Anfahrtszeiten (separate Abrechnung)"),
+        Bullet("Hardware-Lieferzeiten"),
+        Bullet("Laufender Wartungsvertrag (separates Angebot)"),
+        Bullet("Spätere Erweiterungen"),
+      ],
+    },
+  ],
+});
+
+Packer.toBuffer(doc).then(buffer => {
+  fs.writeFileSync("/home/claude/<kunde>/Kurzkonzept_<Kunde>.docx", buffer);
+  console.log("OK");
+});