# nees Relaunch

Neuaufbau des TYPO3-Sitepackages für nees (Brandschutz-Ingenieurbüro).
Grundlage: Website-Manual v1 und 29 Layout-PDFs.

Projektsprache ist Deutsch — Kommentare, Commits und Dokumentation ebenfalls.

## Das Staging läuft auf TYPO3 14.3.6 (21.08.2026)

Das Upgrade ist durch. Stand nach dem Sprung:

| | |
|---|---|
| TYPO3 | **14.3.6** (von 13.4.34) |
| bootstrap_package | **16.0.0** (von 15.0.4) |
| content-blocks | **2.4.8** (von 1.6.3) |
| container-elements | `dev-main` — v14 noch ohne Tag |
| Frontend | **134 von 136 Seiten HTTP 200** |
| Backend | erreichbar |
| Schema | keine offenen Änderungen, 0 Wizards |

Verbleibende zwei: `/aktuell/detail` liefert 404 ohne News-Parameter, das ist
korrekt. `/layoutseite/styleguide` liefert 500 — eine Restseite des
Sitepackage-Generators, deren Template als Altlast entfernt wurde.

**Vier Dinge mussten dafür repariert werden:**

1. `File::FILETYPE_IMAGE` ist in v14 entfallen. In
   `Configuration/TCA/tx_bootstrappackage_carousel_item.php` durch
   `FileType::IMAGE->value` ersetzt — so macht es bootstrap_package 16 selbst.
2. **scssphp ist nicht Dart Sass.** bootstrap_package kompiliert mit scssphp,
   und das rechnet `1.7rem + 2.4vw` selbst aus statt es dem Browser zu lassen.
   22 `clamp()`-Ausdrücke mussten in `calc()` gefasst werden.
3. **scssphp löst bei `EXT:`-Pfaden keine Partials auf.** Die Importe in
   `theme.scss` tragen den Unterstrich jetzt ausdrücklich: `Page/_fonts`
   statt `Page/fonts`.
4. Die Site-Konfiguration verlangte die Site Sets von powermail, das entfernt
   wurde. Zwei Zeilen aus `config/sites/main/config.yaml` gestrichen.

**Die 60 Content-Element-Overrides sind entfallen.** Sie liefern Arrays, wo
TYPO3 14 Record-Objekte erwartet, und legten damit das Frontend lahm. Ohne sie
läuft alles. Das bestätigt Festlegung 4: Bordmittel nutzen, nicht nachbauen.
Sicherung unter `/html/backup/ce-overrides-v13-20260821-1605`.

**Rückweg**, falls nötig — Sicherungen unter `/html/backup`, Stempel `20260821-1605`:
Datenbank, `composer.json` der v13-Fassung, altes Package, Site-Konfiguration.

## Site Sets — zwei Einträge, die leicht übersehen werden

In TYPO3 14 liefern Extensions ihre Rendering-Definitionen über Site Sets. Fehlt
das Set in `config/sites/main/config.yaml`, rendert die Extension **stillschweigend
nichts** — die Seite liefert HTTP 200, im Inhalt steht nur
`ERROR: Content Element … has no rendering definition!`.

- **`buepro/container-elements` fehlte.** Damit war kein einziges der 150
  Container-Elemente gerendert. Nachgetragen am 24.08.2026.
- **powermails Sets mussten raus**, weil die Extension entfernt wurde.

Bei jeder Extension, die Inhaltselemente mitbringt, gehört ihr Set in diese Liste.

## Fußbereich kommt aus dem Backend

`Partials/Page/Structure/FooterContent.html` rendert `colPos 10`, `11` und `12`
mit `slide: -1` — Unterseiten erben den Fußbereich von der Startseite.

Fest im Template stehen nur Wortmarke und Social-Icons, weil sie im Backend keine
Entsprechung haben. **Adressen und Navigation nicht ins Template schreiben** — die
erste Fassung hatte die drei Standorte fest verdrahtet und damit die gepflegten
Inhalte übergangen.

## Logo — drei Fallen auf einmal (26.08.2026)

Im Kopf stand das **quadratische Signet auf 200 × 200 gequetscht**, doppelt.
Drei voneinander unabhängige Ursachen:

**1. `logo.svg` und `logo-inverted.svg` sind nicht nees.** Sie zeigen
„S/PKG/BLD", das Platzhalterlogo des Sitepackage-Generators. Genau darauf zeigte
`Configuration/Sets/SitePackage/settings.yaml` — unter dem Kommentar „BELEGT:
Die Wortmarke ist die bevorzugte Fassung". Hätten die Site-Einstellungen nicht
überschrieben, stünde „S/PKG/BLD" im Kopf. **Die beiden Dateien gehören
gelöscht.**

**2. `config/sites/main/settings.yaml` schlägt jede Paket-Einstellung.** Dort
standen `page.logo.file` und `page.logo.fileInverted` beide auf
`nees-logo.svg` mit 200 × 200. **Diese Datei liegt nur auf dem Server, nicht im
Repo** — bei jeder Logo-, Farb- oder Theme-Frage zuerst dort nachsehen. Sie
trägt auch `config.no_cache: true` und die SCSS-Grundfarben.

**3. bootstrap_package gibt immer beide Fassungen aus** — `navbar-brand-logo-normal`
und `-inverted`. Ohne CSS-Weiche stehen beide nebeneinander. Das war im Kopf und
im Fuß der Fall, nur nicht zu erkennen, weil beide Felder auf dieselbe Datei
zeigten. Die Weiche steht jetzt in `Page/_navigation.scss` (Kopf: normal) und
`Page/_footer.scss` (Fuß: invertiert).

### Die Wortmarke ist neu gebaut

`nees-wortmarke.svg` und `nees-wortmarke-invertiert.svg`. Das Signet enthält die
Buchstaben **zweizeilig**, das Layout zeigt sie **einzeilig** — die Wortmarke ist
deshalb aus seinen Buchstaben neu gesetzt:

| | |
|---|---|
| Quelle | `nees-logo.svg`, Elemente nach Füllfarbe getrennt |
| „nees" | 4 Pfade `#e84e0f` → auf `#E05206` gezogen |
| „INGENIEURE" | 10 Elemente `#575756` — **nicht nur Pfade**, auch `rect` und `polygon`. Beim ersten Versuch fehlten dadurch sieben Buchstaben. |
| Baum und Rahmen | 6 Pfade `#b2b2b2`, verworfen |
| Maße aus dem PDF | „nees" 20.8 zu Versalhöhe 22.3, Wortabstand 13.0 |
| Ergebnis | Verhältnis **11.86 : 1** gegen **11.84 : 1** im PDF |

Größen wie bei der Typo-Skala mit Deckel bei 1512: Kopf 213 × 18 (PDF 266.5 × 22.5),
Fuß 277 × 23 (PDF 377.0 × 29.2). Der linke Rand des Logos sitzt auf 121 und damit
auf demselben Raster wie der Inhalt — nachgemessen.

Das quadratische Signet bleibt das **Favicon**, dafür ist es gedacht.

### Die Kopfnavigation trägt vier Punkte

`Startseite.pdf` zeigt Leistungen, Referenzen, Über uns, Karriere — dazu `DE`
und der Burger. Live stehen sieben, weil `nav_hide = 1` für **19** (Aktuell),
**28** (Service) und **182** (Layoutseite) noch als Entwurf im Workspace liegt.
Nach dem Freigeben stimmt die Kopfzeile ohne weiteres Zutun.

## Ordner

| Pfad | Zweck |
|---|---|
| `altes-Site/` | Bestand als Referenz. **Nicht weiterentwickeln**, nur nachschlagen. |
| `neue-Site/` | Das neue Package. Hier wird gebaut. |
| `Kundenmaterial/` | Manual und Layout-PDFs. Nicht versioniert (156 MB), liegt in OneDrive. |
| `nees-bausteine.md` | **Baukasten.** Wiederkehrende Anordnungen mit CType, Feldern, Markup und Massen. Zuerst hier nachsehen. |
| `nees-komponenten.csv` | Bauliste: 22 Komponenten mit Bordmittel-Zuordnung und Zieldateien. |
| `nees-elementvergleich.html` | Bestandsanalyse Ist/Soll. |

## Designsystem (verbindlich aus dem Manual)

- **Genau vier Farben**, keine weiteren: `#E05206` Orange, `#FFFFFF`, `#141414`, `#58585A`
- **Eine Schrift**: Jost in Light 300 / Regular 400 / Medium 500
- **Radius**: 0 überall, Ausnahme Bildkacheln 10px
- **Fließtext**: Blocksatz mit `hyphens: auto`
- **Überschriften**: Highlight-Zeile orange in Versalien, darunter orangener Trenner, dann Hauptüberschrift
- **Aufzählung**: **standardmäßig ohne Zeichen und ohne Einzug** — Vorgabe des
  Auftraggebers vom 26.08.2026. Das quadratische Zeichen in Orange aus dem Manual
  gibt es weiter, aber nur über die Klasse `list-square` am `<ul>`. `<ol>` bleibt
  unberührt, sonst verlöre eine nummerierte Aufzählung ihre Aussage.
- Keine organischen Formen, keine dekorativen Elemente

## Festlegungen

1. **Farben führen in `_variables.scss`**, nicht in `settings.yaml`. Im Altbestand
   widersprachen sich beide (`settings.yaml` `#eb3e4a` gegen SCSS `#e05206`).
   In `settings.yaml` gehört nur, was wirklich redaktionell variabel sein soll.

2. **`_variables.scss` enthält ausschließlich Variablen, Maps und Mixins.**
   Kein CSS-Output — der gehört nach `Page/_base.scss`. Die alte Datei gab am Ende
   `:root`- und Klassenregeln mit `!important` aus; das nicht wiederholen.

3. **Abgeleitete Töne per `mix()`** aus den vier Markenfarben, keine neuen Hexwerte.
   Hover- und Linienfarben braucht die Seite, aber die Palette bleibt formal bei vier.

4. **Keine neuen Content Blocks ohne Not.** 13 der 15 neuen Komponenten lassen sich
   aus bootstrap_package-Bordmitteln plus SCSS bauen — `Timeline`, `Quote`, `IconGroup`,
   `MenuCardDir`, `CardGroup`, `Textteaser` sind direkte Treffer. Zuordnung steht in
   `nees-komponenten.csv`. Echter Neubau nur bei der Bundesländer-Karte und bei neele.

5. **Design absichern, aber nicht über das CType-Feld.** Die erste Fassung
   hatte dort eine Weißliste — sie ließ rund 45 Typen zu, verfügbar sind über 90.
   Es fehlten sämtliche `container_*` von b13, `ce_accordion`, `ce_tile_unit`,
   die meisten `menu_*` und fast alle News-Plugins. **Aufgehoben am 25.08.2026
   auf Ansage des Auftraggebers:** Die Redaktion soll sehen, was zur Verfügung
   steht. Abgesichert wird über Backend-Layouts und den Assistenten
   „Neues Inhaltselement", nicht über das Feld selbst.

6. **Kommentar-Konvention in SCSS**: `BELEGT` = durch das Manual festgelegt,
   `ANNAHME` = von uns gesetzt, Bestätigung durch den Kunden steht aus.
   Beim Ändern von `ANNAHME`-Werten die Markierung mitpflegen.

## Abhängigkeiten (Stand 21.08.2026, gegen Packagist geprüft)

`neue-Site/composer.json` steht. TYPO3 v14.3.6 ist stabil, alle 26 Core-Extensions
haben v14-Releases, PHP bleibt bei `^8.2`.

- **bootstrap_package ^16.0 kann v14** (`^13.4 || ^14.0`). Die Grundsatzfrage nach der
  Herkunft der Content-Element-Templates ist damit beantwortet.
- **`b13/container` ^4.1** ist jetzt direkte Abhängigkeit. Es hängt an
  `typo3/cms-backend ^13.4 || ^14.3`, nicht am Core.

**Drei Pakete sind geparkt**, Begründung steht in `composer.json` unter
`extra.nees-geparkt`:

| Paket | Lage |
|---|---|
| `buepro/typo3-container-elements` | stabil nur `^13.4`, `dev-main` schon auf `^14.3` — Release abwarten |
| `in2code/powermail` | kein v14, auch nicht auf dev. Ersatz durch `typo3/cms-form` prüfen |
| `passionweb/ai-seo-helper` | Version 0.x auf `^13.4`. Verzicht empfohlen |

## Laufende Arbeit: Startseite nach dem PDF

**Vorgehen: abschnittsweise.** Erst Navigation, dann Hero, dann Leistungen und
so fort. Nach jedem Abschnitt prüft der Kunde und gibt frei.

**Drei Festlegungen vom 24.08.2026:**

1. **Bei Widersprüchen gilt das PDF, nicht das Manual.** Der Kunde hat die PDFs
   gesehen und abgenommen. Konkret: Knöpfe und Karten haben dort 4–6px Radius,
   obwohl das Manual „Radius 0" sagt.
2. **Fußbereich: drei Spalten**, nicht vier — Marke mit Kurztext und drei
   Social-Icons, „LINKS", „STANDORTE" mit orangenen Kartennadeln. Kein
   Newsletter-Block, der ist nur ein Link in der Fußzeile.
3. Die Kopfzeile trägt genau vier Punkte, der Burger die vollständige Navigation.

**Stand:** Navigation, Hero und die Abschnitte 2 bis 4 sind gebaut. Als
Nächstes Abschnitt 5, „Aktuelles".

### Aufbau von Startseite.pdf

1920 × 7782 pt, neun Abschnitte im Wechsel hell und dunkel:

| # | Abschnitt | Grund |
|---|---|---|
| 1 | Hero halbiert — Highlight zweizeilig, H1, Subline, zwei Knöpfe, rundes Portrait unten links | weiß |
| 2 | „Unsere Leistungsbereiche" — drei Karten mit Kontur, `01`/`02`/`03` in Orange | dunkel |
| 3 | „Referenzen" — Kacheln, Titel plus Jahr in Orange, Aktionslink rechts auf Höhe der H2 | weiß |
| 4 | Kennzahlen `#1`–`#4`, große Zahlen, Label darunter | dunkel |
| 5 | „Aktuelles" — Filterzeile, dann waagerechte Kartenschiene | weiß |
| 6 | „Unser Anspruch" — nur Highlight, Trenner, große Aussage | dunkel |
| 7 | „Einblicke" halbiert, rechts Video mit orangem Abspielknopf | weiß |
| 8 | Referenz-Überlagerung auf Vollbild | Bild |
| 9 | Fußbereich, drei Spalten | dunkel |

Wiederkehrend: Highlight-Zeile in Versalien, darunter kurzer oranger Trenner,
darunter die Überschrift. Aktionslinks sind orange mit `›`.

### Offene Entwürfe im Workspace

Der MCP schreibt in „MCP Workspace for technik@wow." — **nicht live**, bis jemand
im Backend freigibt. Derzeit offen:

- `tt_content` **1731** — das Hero-Element auf Seite 171. Offen sind
  `frame_layout = h-100`, `space_before_class` leer und das zweite Bild
  (Platzhalter für das Portrait der Ansprechpartnerin).
- `tt_content` **1428** — `hidden = 1` für den alten Vollbild-Carousel. Er
  stand über dem neuen Hero und schob ihn aus dem ersten Bildschirm.
- `tt_content` **1734** — „Unsere Leistungsbereiche", `card_group` mit drei
  Karten (`tx_bootstrappackage_card_group_item` 1–3), `columns = 3`
- `tt_content` **1735** — der Aktionslink „Zu unseren Leistungen ›" darunter
- `tt_content` **1736** — Kopfzeile Referenzen, `ce_columns2` mit
  `row.class = nees-kopfzeile`; Kinder **1737** (Überschrift, colPos 101) und
  **1738** (Aktionslink, colPos 102)
- `tt_content` **1744** — `nees_referenzauswahl`, zieht die Referenzen ueber
  die Kategorie. Ersetzt **1739** samt Kindern **1740**/**1741**, die auf
  `hidden = 1` stehen.
- `tt_content` **1325** und **1369** — Kategorie „Startseite" und Jahr gesetzt
  (LAV Halle 2024, Bildungsakademie Karlsruhe 2025)
- `sys_category` **52** — „Startseite" im Kategorienbaum unter `/kategorien`
- `tt_content` **1749** — Kopfzeile Referenzen als Textelement,
  `frame_class = kopfzeile`
- `tt_content` **1754** — Kennzahlen-Band, `ce_columns4` mit
  `frame_class = kennzahlen`; Kinder **1755**–**1758** in colPos 101–104
- `pages` **19, 28, 182** — `nav_hide = 1` für Aktuell, Service, Layoutseite

**Solange das offen ist, sieht die Startseite den alten Carousel zuoberst und
den Hero darunter.** Die Gestaltung des Heros ist live, seine Einbettung nicht.

Templates und SCSS gehen dagegen als Dateien sofort live. Der Live-Stand zeigt
also neue Gestaltung, aber noch alte Struktur.

### Werkzeuge und Eigenheiten

**PDFs ansehen:** `pdftoppm` fehlt, aber `fitz` (PyMuPDF) ist da. Die Seiten sind
sehr hoch, deshalb in Bänder schneiden:

```python
import fitz
d = fitz.open("Kundenmaterial/Website Export Pdf/Startseite.pdf")
s = d[0]; n = 6; h = s.rect.height / n
for i in range(n):
    clip = fitz.Rect(0, i*h, s.rect.width, (i+1)*h)
    s.get_pixmap(matrix=fitz.Matrix(0.62, 0.62), clip=clip).save(f"start-{i+1}.png")
```

**Beides ist unzuverlässig:** Der erste SSH-Versuch wird oft abgewiesen, der MCP
antwortet manchmal nicht. In beiden Fällen hilft schlicht ein zweiter Anlauf.

## Was der Hero gekostet hat (25.08.2026)

Sechs Dinge, die beim nächsten Abschnitt Zeit sparen.

**1. Die Maße stehen im PDF, man muss sie nicht schätzen.** `fitz` liefert
Textspans mit Größe und Farbe und alle Zeichnungen mit Koordinaten:

```python
for b in seite.get_text("dict")["blocks"]:
    for l in b.get("lines", []):
        for sp in l["spans"]:
            print(sp["bbox"], sp["size"], hex(sp["color"]), sp["text"])
for dr in seite.get_drawings():
    print(dr["rect"], dr.get("fill"), dr.get("color"))
```

So kamen die Werte heraus, die vorher geraten waren: Highlight-Zeile **30pt**
statt 13px, Trenner **77.5pt** statt 130px, Knopfradius **10pt**, Seitenrand
**121pt**. Beide Werte waren im Altbestand abgeschrieben und in acht bzw.
sieben Fundstellen des PDFs jeweils identisch — die Belege sind eindeutig.

**2. bootstrap_package rechnet in Zeilen, nicht in Rasterspalten.** Die Regel

```css
.textpic-right .textpic-item { width: calc(50% - (40px / 2)) }   /* ab 768px */
```

geht davon aus, dass beide Hälften nebeneinander in **einer** Zeile liegen.
Im Grid bezieht sich `50%` auf die Rasterspalte, beide Hälften schrumpfen
also auf die Hälfte ihrer Spalte. Vom Textbereich blieben 160px übrig und
die Überschrift brach nach jedem Wort um. Wer `.textpic` zum Grid macht,
muss `width: 100%` auf den Items nachsetzen.

**3. `.frame` bringt `padding: 42px 0` mit.** Für einen bildschirmfüllenden
Bereich sind das 84px zu viel, und der Abschnitt darunter schließt nie bündig
an. `.frame-half` setzt deshalb `padding-block: 0`.

**4. Alle Bilder eines Elements liegen in EINER `.gallery-row`.** Nicht eine
Zeile je Bild — siehe `Partials/ContentElements/Media/Gallery.html` in
bootstrap_package. Das zweite Bild ist deshalb `.gallery-item + .gallery-item`.
**Konvention für Halbbilder:** erstes Bild = großes Bild, zweites Bild =
rundes Portrait unten links.

**5. Höhe gehört an `frame_layout`, nicht an `frame_class`.** Der erste Anlauf
hängte `min-height: 100vh` an `.frame-half` — und machte damit auch die vier
gewöhnlichen Text-Bild-Abschnitte auf `/ueber-uns` bildschirmhoch. Der
Bildschirm-Hero ist jetzt `frame_class = half` **plus** `frame_layout = h-100`.

**6. `space_before_class` kennt kein `none`.** Gültig sind nur `extra-small`
bis `extra-large`; die Klasse `frame-space-before-none` entsteht aus dem
**leeren** Wert. Die `addItems`-Zeile `none = Kein Abstand` in der
`page.tsconfig` bietet also etwas an, das die TCA zurückweist. Ebenso gibt es
`space_after_class` bei `textpic` gar nicht. *(Beides noch nicht bereinigt.)*

**Jost ist eine variable Schrift.** Google liefert keine drei Schnitte, sondern
zwei Dateien (`latin`, `latin-ext`) mit der Gewichtsachse 100–900, zusammen
43 KB. `Page/_fonts.scss` bindet sie mit `font-weight: 300 500` ein. Vorher
schlug das Laden fehl, der Browser setzte Segoe UI — und das läuft rund 10 %
breiter, wodurch jede Breitenmessung daneben lag. **Erst die Schrift, dann
messen.**

Was sich davon nicht beheben ließ: Der Satz im PDF läuft rund **9 % enger** als
echtes Jost. Die Subline-Zeile „Ganzheitliche Planung, Nachweise und
Simulationen" braucht 675pt statt der gezeichneten 622pt. Statt die Schrift zu
stauchen, bekam der Textbereich rechts einen schmaleren Rand — der Umbruch
fällt jetzt wie im PDF.

## Was Abschnitt 2 gezeigt hat (25.08.2026)

**Die Bauliste lag beim Bordmittel daneben.** `nees-komponenten.csv` nennt für
die Leistungsbereiche `ListGroup`. Das PDF zeigt aber drei Karten nebeneinander
mit Kontur, keine Liste - `card_group` trifft das und bringt je Karte
Ueberschrift, Text und optional Bild und Link mit. Die Spaltenzahl steht in der
FlexForm (`columns = 3`), die Kinder liegen in
`tx_bootstrappackage_card_group_item`. **Bei den uebrigen Komponenten die
CSV-Zuordnung gegen das PDF pruefen, nicht blind uebernehmen.**

**Der Seitencontainer sass auf dem falschen Raster.** `.frame-container-default`
brachte Bootstraps `max-width: 1320px` mit festem 16px-Polster mit. Das PDF setzt
den Inhalt durchgaengig auf 121pt Rand bei 1920pt Breite, der Inhalt laeuft also
1677pt breit. `$content-max-width` steht jetzt auf 1920px, dazu `$nees-inset`
als gemeinsames Raster fuer Container und Halbbild.

**Die Typografieskala ist belegt**, nicht geschaetzt: **90 / 45 / 35 / 30pt** bei
1920pt Breite, jeweils mehrfach im PDF. H2 und H3 waren zu klein gedeckelt
(40px und 28px). Der orangene Trenner ist **4pt stark**, nicht 2px - an allen
sieben Fundstellen.

**Abstaende im PDF sind Oberkante zu Oberkante.** 73.4 zwischen Nummer und Titel
heisst nicht `margin-bottom: 73.4` - die Zeilenhoehe kommt obendrauf. Beim ersten
Anlauf war die Karte dadurch 88 zu hoch. Der Zwischenraum ist der PDF-Wert
minus Schriftgroesse.

**Kontrast:** Der Kartentext steht im PDF auf `#58585a`, auf dem dunklen
Kartengrund ergibt das **2.2:1** und ist nicht lesbar. Aufgehellt auf rund 7:1.
Der Kunde plant Barrierefreiheit selbst - hier waere die Vorlage sonst ein
Eigentor. Rueckbau waere eine Zeile in `_numbered-list.scss`.

**Offen aus diesem Schritt:**

- Der Abschnitt hat im PDF ein dunkles Betonfoto als Grund, gebaut ist er mit
  `frame_class = dark` als Flaeche. Das Bild kann ueber `background_image` am
  Element nachgetragen werden.
- **Waagerechter Ueberlauf auf der Startseite**, 2027 statt 1905 Breite. Ursache
  ist der Slider `#c1681`: seine Slides liegen bei 3810 offen nebeneinander,
  weil Swiper nicht initialisiert wird - in der Konsole stehen
  `$ is not defined` und Folgefehler aus `selectize.js`. **jQuery fehlt.**
  Das ist unabhaengig vom Design und gehoert zum Altlasten-Komplex.

## Was Abschnitt 3 gezeigt hat (25.08.2026)

**Container duerfen verwendet werden**, auch wenn sie dem Redakteur nicht im
Assistenten angeboten werden - Festlegung des Auftraggebers vom 25.08.2026.
Damit lassen sich Strukturen bauen, die mit einem einzelnen Inhaltselement
nicht gehen, etwa Ueberschrift links und Aktionslink rechts.

**`ce_columns2` vergibt eigene Klassen.** In der FlexForm stehen `row.class`
sowie `columns.1.class` und `columns.2.class`. Damit greift das SCSS an einer
Klasse, die wir selbst gesetzt haben, statt an einer erratenen - das ist der
verlaesslichste Weg, den der Container bietet:

```
ce_columns2  row.class = nees-kopfzeile        Spalte 1 Ueberschrift, 2 Link
ce_columns2  row.class = nees-referenzkacheln  je Spalte ein textpic
```

Die Kinder tragen `tx_container_parent = <uid>` und `colPos` **101** oder
**102**. Markup: `.row > .ce-col.col-md-*`, also Bootstrap-Grid.

**Der Seitenrand hat sich in Containern verdoppelt.** Das in Abschnitt 2
gesetzte `padding-inline` an `.frame-container-default` wirkte auch auf die
Frames *innerhalb* eines Containers - 242 statt 121, die Spalten schrumpften
entsprechend. Das betraf **alle 150 Container-Elemente im Bestand**, nicht nur
die Referenzkacheln. Behoben ueber:

```scss
.ce-col .frame-container-default,
.frame .frame .frame-container-default { max-width: none; padding-inline: 0; }
```

**`textpic` ist eine Flexbox mit `gap: 40px`.** Der Abstand zwischen Bild und
Beschriftung kommt von dort, nicht aus einem `margin`. Ein zusaetzliches
Polster addiert sich nur darauf - beim ersten Anlauf standen 58 statt der
gezeichneten 31. Die Luecke gehoert ueber `gap` gesetzt.

**Der Kachelradius ist 7.6pt**, nicht die 10px aus dem Manual - derselbe Wert
wie Knoepfe und Karten. `$border-radius-tile` steht jetzt wie
`$border-radius-btn` auf 6px, alles Gerundete traegt damit denselben Radius.

**Das Jahr sitzt bei 50 % der Kachelbreite**, nicht direkt hinter dem Titel -
in beiden Kacheln gemessen (400 und 401 von 798). Umgesetzt ueber
`subheader_class = highlight` mit unterdruecktem Trenner, der Titel bekommt
`flex: 0 0 50%`.

## Referenzen ueber ein shortcut-Element (25.08.2026)

Der Abschnitt „Ausgewaehlte Projekte" fuegt die Referenzen als
**Datensatzverknuepfung** ein - `CType shortcut`, Feld `records`:

```
tt_content 1744   CType = shortcut
                  records = tt_content_1325,tt_content_1369
                  frame_class = referenzen
```

**Wo die Referenzen liegen:** alle **263** als `mask_referenz` im Ordner
**pid 23** (`doktype 254`, haengt unter „Home-alt"). Der Ordner wird nicht
ausgeliefert - fuer einen Datensatzordner ohne Belang. *(Die Ordner 75 und 27
enthalten sie nicht: 75 sind fuenf Kategorie-Unterseiten mit gewoehnlichem
Inhalt, 27 ist ein leerer Slider-Ordner.)*

**Gerendert wird mit dem eigenen Template der Referenz**,
`ContentBlocks/ContentElements/referenz/templates/frontend.html`:

```
.referenz > .referenz-inner > .image-outer + .overlay
          > .name > .referenz-titel + .referenz-jahr + .referenz-leistung
```

Die drei `span` in `.name` sind neu - vorher stand dort nur der Titel. Auf der
Referenzenseite bleiben Jahr und Leistung meist leer, der Block sieht dort aus
wie bisher. Die Startseite stellt ueber `frame_class = referenzen` zwei
Kacheln nebeneinander und blendet das Overlay aus, das zur Referenzenseite
gehoert.

**`mask_referenz` hatte kein Jahr.** Das Layout zeigt es orange neben dem
Titel, die Felder waren aber Bauherr, Architekt, Leistung, Highlights, Links,
Bild und Copyright. `tx_mask_jahr` ist im Content Block ergaenzt, das
Datenbankfeld per `database:updateschema "field.add"` angelegt.

### Die Exception im Backend kam aus einer leeren FlexForm

Der Stacktrace zeigt den Weg:

```
B13\Container\Listener\PageContentPreviewRendering
  -> Partial PageLayout/Record/ce_columns2/Header
  -> Record->toArray() -> processFlexForm(
       '<T3FlexForms><data><sheet index="sDEF">
        <language index="lDEF"></language></sheet></data></T3FlexForms>' )
  -> FlexFormTools::convertFlexFormContentToSheetsArray  Zeile 353
     foreach() argument must be of type array|object, string given
```

**Ueber den MCP liess sich die FlexForm von `ce_columns2` nicht befuellen.**
Geschrieben wurde ein leeres Geruest mit dem Sheet `sDEF` - der Container
nutzt aber `sGeneral` und `sCustom`. Die Backend-Vorschau lief darauf in eine
Exception. Bei `card_group` klappte es, dessen FlexForm hat nur ein Sheet und
heisst tatsaechlich `sDEF`.

**Folge fuer die Arbeitsweise:** Klassen fuer das SCSS nicht ueber
`row.class` in der Container-FlexForm setzen, sondern ueber `frame_class` -
das schreibt der MCP zuverlaessig. Die Kopfzeile ist deshalb jetzt ein
einzelnes Textelement mit `frame_class = kopfzeile`, kein Container mehr.

**Und: eine leere FlexForm faellt im Frontend nicht auf.** Die Seite lieferte
durchgehend HTTP 200, nur das Backend brach ab. Nach dem Anlegen von
Container-Elementen also immer die Seitenansicht im Backend oeffnen.

### Zwei Live-Datensaetze muessen von Hand weg

`tt_content` **1736** und **1739** auf Seite 171 - leere `ce_columns2` ohne
Funktion, mit der kaputten FlexForm. **Sie sind die Ursache der Exception und
liegen live** (`t3ver_wsid = 0`), nicht im Workspace.

Ueber den MCP nicht zu beheben: der schreibt ausschliesslich in den Workspace,
`delete` markiert dort nur die Arbeitsfassung. Direkte Schreibzugriffe auf die
Kundendatenbank sind bewusst unterblieben.

**Zu tun, eines von beiden:**

- im Backend auf Seite 171 die beiden leeren Container loeschen, oder
- den Workspace freigeben, dann greifen die vorgemerkten Loeschungen

Zum Vergleich: von 66 `ce_columns2` im Bestand tragen 62 eine befuellte
FlexForm. Die uebrigen stammen aus diesem Versuch.

## Kennzahlen-Band (25.08.2026)

Vier Werte auf dunklem Grund, jeder mit einer Marke `#1` bis `#4` in Orange,
darunter die Zahl und das Label.

| | |
|---|---|
| Aufbau | `ce_columns4`, `frame_class = kennzahlen` |
| Je Spalte | ein gewöhnliches `text`-Element: Überschrift = Zahl, Fließtext = Label |
| Marke | CSS-Counter auf `.ce-col::before`, nicht redaktionell gepflegt |

**Über den Container, nicht über `card_group`.** Der erste Anlauf nutzte eine
Kachelgruppe — das war ein Umweg. Der Container gibt Bootstrap-Spalten aus,
deren Breite sich im Backend über die FlexForm einstellen lässt (`col-md-3`,
`col-xs-6` und so fort). Bei `card_group` hängt die Aufteilung starr an der
Spaltenzahl. **Für alles Mehrspaltige gilt: Container nehmen.**

**Bootstraps `.row` muss entschärft werden.** Sie zieht sich über negative
Ränder aus dem Raster und gibt den Spalten dafür ein Polster — die Werte
standen dadurch 20pt zu weit links. Mit `--bs-gutter-x: 0` sitzen sie bündig
am Seitenrand, wie im Layout.

**Masse gegen Startseite.pdf** (Seitenbreite dort 1920pt): Bereich 548.5 statt
553 hoch, Marke bis Zahl 56.9 statt 56.7, Zahl bis Label 104.9 statt 105.1,
Schriftgrößen 29.9 / 89.9 / 29.9 gegen 30 / 90 / 30. Spaltenstarts 121, 537,
953, 1368 gegen 121, 536, 953, 1425 — die ersten drei treffen, die vierte ist
im Layout selbst aus der Reihe.

### Container: Spaltenbreiten stehen in der FlexForm

Die Aufteilung eines `ce_columns2/3/4` wird **nicht** über die Spaltenzahl
festgelegt, sondern je Spalte einzeln — deshalb sind Container einer
Kachelgruppe vorzuziehen, sobald die Breiten variieren sollen:

| Feld | Werte |
|---|---|
| `columns.N.width` | `equal`, **1–12**, `auto`, `none` — N geht bis zur Spaltenzahl |
| `columns.breakpoint` | `none`, `sm`, `md`, `lg`, `xl` |
| `row.class` | eigene Klasse an der Zeile |
| `columns.N.class` | eigene Klasse je Spalte |

Daraus baut `Partials/General/CeColumn.html` die Klasse zusammen:
`col` + `-{breakpoint}` + `-{width}`. Aus `width = 3` und `breakpoint = md`
wird also `col-md-3`, aus `equal` schlicht `col-md`.

**Das XML trägt zwei Sheets**, die Feldnamen darin sind flach mit Punkten:

```xml
<sheet index="sGeneral">   <!-- Breiten und Umschaltpunkt -->
  <field index="columns.1.width"><value index="vDEF">3</value></field>
  <field index="columns.breakpoint"><value index="vDEF">md</value></field>
<sheet index="sCustom">    <!-- die Klassen -->
  <field index="row.class"><value index="vDEF"></value></field>
```

**Der MCP liest das korrekt** und gibt es verschachtelt zurück
(`{"columns":{"1":{"width":"3"},...,"breakpoint":"md"}}`) — genau in dem
Format, das er beim Schreiben erwartet. Am Format lag es also nie.

**Gestaltung an `.ce-col` hängen, nicht an der Breitenklasse.** Dann bleibt
das SCSS unabhängig davon, was in der FlexForm eingestellt ist. Nachgemessen:
`col-md-3` und `col-md` ergeben bei vier Spalten dieselben 416pt.

### Container lassen sich über den MCP nicht anlegen

Der Grund steht im Serverprotokoll, nicht im Verhalten des Werkzeugs:

```
[CRITICAL] Hn.McpServer.MCP.Tool.Record.WriteTableTool:
The current ServerRequestInterface must be provided in key "request"
RuntimeException in FormDataCompiler.php:76
  #0 buepro/typo3-container-elements/Classes/Hooks/DataHandlerHook.php(74)
  #1 DataHandler->processDatamap_preProcessFieldArray()
  #2 hn/typo3-mcp-server WriteTableTool.php(457)
```

`container_elements` hängt einen DataHandler-Hook ein, der beim Anlegen eines
Containers den `FormDataCompiler` aufruft. Der verlangt einen Backend-Request.
Der MCP-Server läuft ohne einen solchen, der Hook bricht ab.

**Dieser Hook setzt die FlexForm-Vorgabewerte.** Bricht er ab, bleibt ein
leeres Gerüst mit dem Sheet `sDEF` stehen — während die Container `sGeneral`
und `sCustom` erwarten. Genau daran ist die Backend-Vorschau in eine Exception
gelaufen.

**Arbeitsteilung, die funktioniert:** Container im **Backend** anlegen und dort
die Breiten einstellen. Alles Weitere geht über den MCP — Kinder mit
`tx_container_parent` und `colPos` 101 ff. einhängen, Felder setzen,
Rahmenklassen vergeben.

**Noch offen — drei Container mit leerem Gerüst:** `tt_content` **1736** und
**1739** liegen live, **1745** ist die Arbeitsfassung dazu. Leere Container
ohne Funktion. Im Backend löschen oder den Workspace freigeben.

## Aktuelles - angefangen, Templatepfade greifen noch nicht (26.08.2026)

Abschnitt 5 der Startseite. **Filterzeile bewusst zurueckgestellt**, siehe
unten. Gebaut ist die waagerechte Kartenschiene:

| | |
|---|---|
| Element | `tt_content` **1510**, `news_newsliststicky`, liegt live |
| Layout | `templateLayout = slider` in der FlexForm, war bereits gesetzt |
| Daten | 91 Artikel auf **pid 18** - die vier im PDF sind echte Meldungen |
| Template | `Templates/News/List.html` mit Weiche auf das Slider-Layout |
| Partial | `Partials/News/List/Kartenschiene.html` |
| SCSS | `Components/_cardrail.scss` |

**Die Masse gehen exakt auf:** vier Karten a 363 plus drei Luecken a 75
ergeben genau die 1678 Inhaltsbreite. Die fuenfte Karte ragt heraus und zeigt
damit an, dass es weitergeht. Bild quadratisch 364, Titel 30pt grau darunter.

Die Schiene laeuft ueber `overflow-x` mit `scroll-snap`, also ohne
JavaScript. Die Swiper-Klassen stehen trotzdem im Markup, damit ein spaeter
initialisierter Slider dieselbe Struktur uebernehmen kann.

### Der Slider kommt aus EXT:newsslider

Nicht aus EXT:news. Das erklaert, warum eigene Templatepfade nicht griffen:

| | |
|---|---|
| Templatepfade | `templateRootPaths.**2444**` |
| Layout | `tx_news.templateLayouts { **44** = Swiperslider }` |
| Eigenes Template | `Resources/Private/Templates/News/List.html` |
| FlexForm | `Configuration/FlexForms/Swiperslider.xml`, Sheet `slider` |

Unsere `page.tsconfig` hatte einen eigenen Wert `slider = Slider` erfunden,
der auf kein Template zeigte - der richtige Wert ist **44**. Und unsere Pfade
lagen auf 100, also weit unter 2444.

**Vorerst ausgesetzt:** Die eigenen Templatepfade stehen auskommentiert in
`setup.typoscript`. Erst newssliders Ausgabe ansehen, dann entscheiden, ob
sie per SCSS gestaltet wird oder ein eigenes Template darueber kommt - dann
mit einer Nummer ueber 2444. Die Dateien liegen bereit:
`Templates/News/List.html` und `Partials/News/List/Kartenschiene.html`.

### FlexForms niemals ueber den MCP schreiben

**Der MCP zerstoert sie.** Bei `tt_content` 1510 hat ein `WriteTable` auf
`pi_flexform` folgendes erzeugt:

```xml
<data type="array">
  <sDEF type="array">
    <lDEF type="array">
      <settingsorderBy type="array"><vDEF></vDEF></settingsorderBy>
```

Richtig waere:

```xml
<data>
  <sheet index="sDEF">
    <language index="lDEF">
      <field index="settings.orderBy"><value index="vDEF"></value></field>
```

Die Punkte in den Feldnamen verschwinden, `sheet`/`field`/`value` fehlen, und
**alle vier Sheets werden zu einem verschmolzen** - `additional`, `template`
und `slider` waren weg. Daher auch die Exception im Backend: die displayCond
des Sliders sucht `template.settings.templateLayout`, und das Sheet gab es
nicht mehr.

Beim Anlegen ueber den MCP ohne `pi_flexform` bleibt das Feld `NULL` - das
ist sauber. **Einstellungen danach im Backend vornehmen.**

Das gilt zusammen mit dem Befund zu den Containern: FlexForms sind der eine
Bereich, den der MCP nicht bedienen kann.

### Stand: neues Element 1761

`tt_content` **1510** ist auf `hidden` gesetzt - seine FlexForm ist zerstoert.
Der intakte Originalwert liegt in `sys_history`, Eintrag **2916** vom
26.08.2026 09:39:29, falls er doch gebraucht wird.

**Neu angelegt: `tt_content` 1761**, `news_newsliststicky`, Ueberschrift
„Aktuelles von", FlexForm noch leer. **Im Backend einzustellen:**

| Feld | Wert |
|---|---|
| Startingpoint | Seite **18** (News) |
| Detailseite | **160** |
| Listenseite / Zurueck | **19** |
| Anzahl | **8** |
| Paginierung ausblenden | ja |
| Template Layout | **Swiperslider** |

### Die Filterzeile braucht erst Kategorien

Das PDF zeigt „Alle | Unsere Projekte | Forschung, Innovation &
Digitalisierung | Team, Kultur & Karriere | Fachbeitraege & News".

**Diese Kategorien gibt es nicht**, und die 91 Artikel sind unkategorisiert -
nur einer haengt an „Newsletter". Auf Ansage des Auftraggebers vom 26.08.2026
erst einmal zurueckgestellt, bis die Einteilung inhaltlich geklaert ist.

## Sitzung 26.08.2026, zweiter Teil — Leistungen, Typo, Listen, Logo

Vier Dinge in dieser Reihenfolge. **Alles ist auf dem Staging abgelegt und im
Browser nachgemessen. Nichts ist committet** — 14 geänderte und 2 neue Dateien
liegen im Arbeitsbaum auf `main`.

### 1 Leistungen-Startseite (pid 25) nach dem PDF gebaut

`Leistungen Startseite.pdf` ist 1920 × 4449 und hat sechs Abschnitte:

| # | y | Abschnitt | Element | Zustand |
|---|---|---|---|---|
| 1 | 110–1080 | Halbbild, **keine Knöpfe**, Portrait unten links | **1763** `textpic`, `half` + `h-100`, `imageorient 25` | live |
| 2 | 1080–1832 | Kartenschiene, Rail bis x=3671 → **8 Karten** | **1764** `card_group`, `dark` | live |
| 3 | 1832–2148 | „BERATUNG" / „Sprechen Sie mit uns…", nur Kopf | **1765** `header` | live |
| 4 | 2148–2702 | Kennzahlen `#1`–`#4` | **1767–1770** `text`, **hidden** | wartet auf Container |
| 5 | 2702–3782 | Halbbild **Bild links**, Anspruch + Aktionslink | **1766** `textpic`, `half` + `h-100`, `imageorient 26` | live |
| 6 | 3782+ | Fußbereich | steht bereits | live |

Die acht Karten sind `tx_bootstrappackage_card_group_item` **8–15**, jede mit
Link auf ihre Seite: 36, 41, 39, 35, 38, 34, 37, 40. Die Texte der Karten 01–04
stehen wörtlich im PDF, **05–08 sind meine Formulierung** — das PDF schneidet
sie ab. Vom Kunden bestätigen lassen.

**Offen an diesem Abschnitt:**

- **Kennzahlen-Band:** `ce_columns4` auf Seite 25 im Backend anlegen
  (`frame_class = kennzahlen`, Breiten `equal`, Breakpoint `md`). Danach lassen
  sich 1767–1770 als colPos 101–104 einhängen und einblenden. `colPos 101` wird
  ohne Container **aktiv abgewiesen** — deshalb liegen sie auf colPos 0, hidden.
- **Die Kartenschiene ist noch keine Schiene.** `columns` kann bei
  bootstrap_package höchstens 4, bei 8 Karten also zwei Reihen. Die waagerechte
  Schiene braucht SCSS auf `frame_class = leistungsschiene`.
- **Unter jeder Karte steht ein Knopf.** Das Template rendert bei gesetztem
  `link` zwingend einen `card-footer` mit `<a class="btn btn-">` und wiederholt
  darin den Titel. Im PDF hat die Karte keinen Knopf. **Entscheidung offen:**
  orangener Aktionslink oder Karte ganzflächig klickbar per `stretched-link`.
- **Altbestand:** `1621` (Überschrift) und `91` (`ce_grid` mit acht
  Icon-Kacheln) stehen weiterhin unter den neuen Abschnitten. Nicht angefasst.
- **Zwei Platzhalterbilder:** Hero-Portrait und die Architekturaufnahme in
  Abschnitt 5 — im Dateibestand gibt es keine dunkle Betonaufnahme.

**`frame_class` neu: `leistungsschiene` und `half-dark`** stehen in der
`page.tsconfig` und sind seit dem Deploy im Backend wählbar. **Noch nicht
umstellen** — dahinter liegt kein SCSS, die Elemente verlören dunklen Grund
bzw. Halbbild-Raster. 1764 steht deshalb auf `dark`, 1766 auf `half`.

**Die FlexForm von `card_group` darf über den MCP geschrieben werden.** Sie hat
genau ein Sheet `sDEF` und zwei flache Feldnamen ohne Punkte — die dokumentierte
Zerstörung kann dort nicht auftreten. Ohne `columns` bleibt die Klasse
`card-group-element-columns-` leer, und `--cardgroup-columns` steht per Vorgabe
auf **1**: alle Karten untereinander. Das ist die einzige Ausnahme von Regel 1.

### 2 Typo-Skala umgestellt — Deckel bei 1512 statt 1920

Der Kunde hat seine XD-Werte bestätigt (90 / 45 / 35 / 30) und ausdrücklich
freigestellt: verbindlich sind die **Verhältnisse**. Die Skala traf die Vorgabe
bei 1920 auf ein Zehntel genau, wirkte aber zu groß. Umgesetzt ist jetzt
dasselbe Verhältnis **3 : 1.5 : 1.17 : 1** mit Deckel bei 1512:

| | 375 | 1280 | **1512** | 1920 |
|---|---|---|---|---|
| H1 Startbild, große Zahlen | 40 | 64.8 | **72** | 72 |
| H2 Abschnitt | 28 | 34.2 | **36** | 36 |
| H3 Karten, Kacheln | 20 | 26.2 | **28** | 28 |
| Fließtext, Highlight | 16 | 22.2 | **24** | 24 |
| Knopfschrift | 15 | 20.8 | **22.4** | 22.4 |

Grund: Ein XD-Board wird nie 1:1 betrachtet, auf einem 1512er Schirm zeigt XD es
bei rund 79 %. Im Browser ist ein CSS-Pixel dagegen ein CSS-Pixel.

**Zwei Fehler steckten getrennt davon drin:**

1. `_hero.scss` griff auf `h1, .element-header` — also auf **jede** Überschrift
   in einem Halbbild. „Unser Anspruch" stand dadurch auf 89.9 statt 45. Der
   Selektor ist auf `h1` eingegrenzt.
2. **Fließtext außerhalb der gestalteten Bereiche blieb auf 16px.** Die Regel
   für `.frame-type-text p` existierte gar nicht. `$body-font-size` ist neu und
   hängt im `nees-bodytext`-Mixin.

**Alle Schriftgrößen stehen jetzt in `_variables.scss`**, in den Komponenten
keine einzige mehr — vorher lagen die 30pt in sechs Dateien einzeln. Die Skala
verschieben heißt vier Zeilen ändern. Tabelle in `nees-bausteine.md`.

### 3 Aufzählungen: Zeichen nur noch auf Ansage

Vorher setzte `.frame ul:not(.list-unstyled):not(.nav)` das orange Quadrat an
**jede** Liste in einem Rahmen. Jetzt:

```scss
.frame ul:not([class]), .frame ul.list-normal { list-style: none; padding-left: 0; }
.frame ul.list-square { … oranges Quadrat … }
```

Im Bestand kommen genau zwei Formen vor — `<ul>` ohne Klasse und
`<ul class="list-normal">` aus dem alten RTE. Beide sind namentlich erfasst
statt über `:not()`, sonst fängt die Regel auch Plugin-Markup ein.
**`<ol>` bleibt unberührt** — ohne Zahlen verliert eine nummerierte Aufzählung
ihre Aussage.

**Dabei aufgefallen:** Die alte Regel überschrieb die Symbollisten von
bootstrap_package — `list-check`, `list-info`, `list-warning`, `list-ban` und
`list-chevron` zeichnen ihr Symbol ebenfalls über `> li:before`, unser
`content: ""` löschte es. Ist mit behoben.

**Fund am Rande:** `page.tsconfig` setzt `RTE.default.preset = nees`, aber
**dieses Preset gibt es nicht** — im Paket liegt nur `Configuration/RTE/Default.yaml`,
und ohne `ext_localconf.php` ist nichts registriert. TYPO3 fällt still auf
`default` zurück. Folge: `list-square` steht der Redaktion **nicht** im
RTE-Menü zur Auswahl. Soll das Zeichen wählbar sein, muss das nees-Preset
angelegt und der Stil eingetragen werden.

### 4 Logo — Abschnitt „Logo — drei Fallen auf einmal" weiter oben

### Wie deployt wird

**Es gibt keine Automatik.** Kein Git auf dem Server, kein Build-Schritt.

```bash
scp <datei> nees-staging:/html/typo3-composer/packages/nees/<pfad>
ssh nees-staging 'cd /html/typo3-composer && vendor/bin/typo3 cache:flush'
```

Danach Prüfsummen vergleichen — CRLF liegt auf beiden Seiten, ein `md5sum`-Abgleich
ist also aussagekräftig. **scssphp kompiliert beim ersten Seitenaufruf**; ob es
durchlief, zeigt ein neuer Hash in
`/typo3temp/assets/bootstrappackage/css/theme-<hash>.css`.

**Sicherungen dieser Sitzung:**

| Pfad | Inhalt |
|---|---|
| `/html/backup/theme-20260826-typo` | komplettes `Theme/` und `page.tsconfig` vor der Typo-Umstellung |
| `…/theme-20260826-typo/Theme/Page/_typography.scss.vor-listen` | Fassung vor der Listen-Änderung |
| `/html/backup/settings.yaml-20260826-vor-logo` | Site-Einstellungen vor dem Logotausch |

Rückweg für das Theme:

```bash
ssh nees-staging 'cp -r /html/backup/theme-20260826-typo/Theme/. /html/typo3-composer/packages/nees/Resources/Public/Scss/Theme/ && cd /html/typo3-composer && vendor/bin/typo3 cache:flush'
```

### Was als Nächstes ansteht

1. **`nav_hide`-Entwürfe freigeben** (Seiten 19, 28, 182) — dann trägt die
   Kopfzeile die vier Punkte aus dem PDF statt sieben.
2. **`ce_columns4` für die Kennzahlen** auf Seite 25 im Backend anlegen, uid
   nennen, dann 1767–1770 einhängen.
3. **Kartenknopf entscheiden** — Aktionslink oder `stretched-link`.
4. **Kartenschiene bauen**, danach 1764 auf `leistungsschiene` und 1766 auf
   `half-dark` umstellen.
5. **`logo.svg` und `logo-inverted.svg` löschen** — Generator-Müll, wird
   nirgends mehr referenziert.
6. **Wortmarke als SVG beim Kunden anfordern.** Unsere ist aus den Buchstaben
   des Signets rekonstruiert; das Verhältnis trifft, aber das Original wäre
   sauberer.
7. **Committen.** Bisher liegt alles nur im Arbeitsbaum.

## Werkzeug-Extension `websitetools` (02.09.2026)

Legt das Layout-PDF über die Seite, blendet das Layoutraster und den
Element-Inspector ein —
**nur bei angemeldetem Backend-Benutzer**, Besucher bekommen kein Byte davon.
Löst das Lesezeichen aus `tools/pdf-auflage-bookmarklet.md` ab; die Leiste ist
auf jeder Seite da und merkt sich über den Seitenwechsel, was an war.

Auf dem Staging installiert (`composer require wow-gmbh/websitetools:@dev`,
Path-Repository `./packages/*`). Einzelheiten in der README der Extension.

**Zwei Fallen beim Bau, beide kosteten einen Fehlversuch:**

1. **Middleware-Zyklus.** `after: content-length-headers` zusammen mit
   `before: page-resolver` ließ die Kette in „dependencies have cycles" laufen —
   **jede Seite HTTP 500**, auch für Besucher. Die beiden `after` genügen; kein
   `before` setzen.
2. **Ohne `Configuration/Services.yaml` kein Konstruktor.** Eine Middleware mit
   Konstruktor-Injection muss im DI-Container stehen. Kontrolle:
   `grep -o "NeesWerkzeuge[^;]*" var/cache/code/di/DependencyInjectionContainer_*.php`
   nach einem `cache:flush` und einem Seitenaufruf.

**Warum Middleware und nicht TypoScript:** ein `page.includeJSFooter` aus einer
Extension hängt an der Ladereihenfolge — setzt ein Site Set später `page = PAGE`,
ist die Zuweisung weg. Die Middleware arbeitet am fertigen HTML und braucht
keinen Eintrag in `config/sites/main/config.yaml`, die nur auf dem Server liegt.

**Beide Werkzeuge saßen unten rechts** und der aktive Inspector blockierte die
Regler der PDF-Auflage — er fängt jeden Klick der Seite in der Capture-Phase ab.
Die mitgelieferte Fassung des Inspectors nimmt die Werkzeug-UI davon aus; die
drei Änderungen sind in der Datei mit `nees:` markiert.

**Das Raster liest `$nees-inset` nicht ab, sondern lässt es den Browser
rechnen** — die `clamp()`-Formel aus `_variables.scss` steht als `RAND_FORMEL`
in `raster.js` und wird einem versteckten Hilfselement als Breite gegeben.
Ändert sich die Formel im SCSS, gehört diese eine Zeile mitgezogen.

**Rückweg:** `composer remove wow-gmbh/websitetools`, Sicherung von
`composer.json`/`composer.lock` unter `/html/backup/werkzeuge-20260902`.

## Hier geht es weiter

**Stand 26.08.2026:** Die Abschnitte 1 bis 4 der Startseite sind gebaut,
freigegeben und live. Abschnitt 5 ist angefangen.

| # | Abschnitt | Element | Zustand |
|---|---|---|---|
| 1 | Hero | `1731` textpic, `half` + `h-100` | live |
| 2 | Leistungsbereiche | `1734` card_group `dark`, `1735` Aktionslink | live |
| 3 | Referenzen | `1749` Kopfzeile `kopfzeile`, `1744` shortcut `referenzen` | live |
| 4 | Kennzahlen | `1754` ce_columns4 `kennzahlen`, Kinder 1755–1758 | live |
| 5 | Aktuelles | `1761` news_newsliststicky | **Workspace, FlexForm leer** |

**Leistungen (pid 25)** ist am 26.08.2026 dazugekommen — Abschnitte 1, 2, 3 und 5
sind live, Abschnitt 4 wartet auf den Container. Siehe den Abschnitt
"Sitzung 26.08.2026, zweiter Teil" weiter oben.

### Drei Dinge, die als Naechstes anstehen

**1. Element 1761 im Backend einstellen.** Die FlexForm ist bewusst leer -
ueber den MCP darf sie nicht geschrieben werden, siehe unten. Einzustellen:

| Feld | Wert |
|---|---|
| Startingpoint | Seite **18** (News) |
| Detailseite | **160**, Listenseite und Zurueck | **19** |
| Anzahl | **8**, Paginierung ausblenden |
| Template Layout | **Swiperslider** (das ist Layout 44 aus EXT:newsslider) |

Danach dessen Markup ansehen und per SCSS nach dem PDF gestalten. Ein
eigenes Template liegt fertig bereit, die Pfade dafuer stehen
auskommentiert in `setup.typoscript` - Nummer muss ueber **2444** liegen.

**2. Die Reihenfolge auf der Seite stimmt nicht.** Die neuen Abschnitte
stehen hinter dem gesamten Altbestand:

```
ist:   Hero → Leistungsbereiche → Aktuelles → [Altbestand] → Referenzen → Kennzahlen
soll:  Hero → Leistungsbereiche → Referenzen → Kennzahlen → Aktuelles → ...
```

`1749`, `1744` und `1754` gehoeren nach oben, direkt hinter `1735`. Der
Altbestand dazwischen (1527, 1511, 1529, 1512, 1528, 1514–1518 und weitere)
gehoert gesichtet und ausgeblendet, sobald der jeweilige Abschnitt neu
gebaut ist.

**3. `1510` ausblenden.** Die alte News-Liste mit der zerstoerten FlexForm.
Die Aenderung liegt als `1762` im Workspace und wartet auf Freigabe.

### Danach: die Abschnitte 6 bis 9

| # | Abschnitt | Ansatz |
|---|---|---|
| 6 | „Unser Anspruch" | nur Highlight, Trenner, grosse Aussage - Frame-Variante |
| 7 | „Einblicke" | Halbbild wie der Hero, rechts Video mit orangem Abspielknopf |
| 8 | Referenz-Ueberlagerung | Vollbild, braucht `_reference-overlay.scss` plus JS |
| 9 | Fussbereich | steht bereits |

### Offene Punkte, unabhaengig vom Bau

- **Filterzeile in Abschnitt 5** - die vier Kategorien existieren nicht, die
  91 Artikel sind unkategorisiert. Zurueckgestellt bis zur inhaltlichen
  Klaerung.
- **Waagerechter Ueberlauf** auf der Startseite, 2027 statt 1905. Ursache
  ist der Slider `#c1681`, dessen Slides offen nebeneinander liegen, weil
  Swiper nicht initialisiert wird - in der Konsole `$ is not defined`.
  **jQuery fehlt.** Altlasten-Komplex.
- **Exception beim Bearbeiten** von News-Elementen: EXT:newsslider haengt
  sein Sheet `slider` auch in `background_image_options` von
  bootstrap_package, wo das Sheet `template` fehlt, auf das die displayCond
  verweist. Konflikt zweier Extensions. Behebbar ueber einen TCA-Override -
  **vorher abstimmen.**
- `sys_category` **52** „Startseite" und zwei Eintraege in
  `sys_category_record_mm` sind funktionslose Reste eines verworfenen
  Ansatzes. Harmlos, aber aufraeumbar.
- **Portrait im Hero** - derzeit ein oranger Platzhalter. Wer dort zu sehen
  sein soll, ist offen.
- `/layoutseite/styleguide` liefert 500, Restseite des Generators.

### Die drei Regeln, die diese Sitzung gekostet hat

1. **FlexForms niemals ueber den MCP schreiben.** Er zerstoert sie - bei
   Containern bleiben sie leer, bei Plugins wird das Format falsch. Anlegen
   ohne `pi_flexform`, Einstellungen im Backend.
2. **Container im Backend anlegen.** Der DataHandler-Hook von
   `container_elements` braucht einen Backend-Request, den der MCP nicht hat.
   Kinder einhaengen und Felder setzen geht danach.
3. **Erst das Markup ansehen, dann gestalten.** Von 17 Komponenten hingen
   die meisten an erfundenen Klassen. Bei jeder weiteren zuerst pruefen,
   was das Template tatsaechlich ausgibt.

Nachschlagewerk fuer wiederkehrende Anordnungen: `nees-bausteine.md`.

## Offen — vor dem Weiterbauen klären

- **„Frag neele"**: Eigenentwicklung, Fremddienst oder eingebundenes Widget? Nicht
  geschätzt, weil die Umsetzungsart offen ist. Auch DSGVO-Betrachtung nötig.
- **CType-Bezeichner in der CSV sind Vermutungen.** bootstrap_package ist im Repo
  nicht vendored, nur die Template-Namen sind verifiziert. Beim ersten Aufsetzen
  gegen die echten Werte ziehen.
- **`composer install` ist noch nie gelaufen.** Die Constraints sind gegen Packagist
  geprüft, aber nicht aufgelöst — Konflikte zwischen Paketen zeigen sich erst dabei.
- **`Über uns.pdf`** ist über 20 MB und nicht auswertbar. Kleinere Fassung anfordern.

## Staging-Server (SSH-Alias `nees-staging`)

`/html/typo3-composer`, TYPO3 **13.4.34**, PHP 8.4 (CLI), Site-Base
`https://nees-2026.wow-internet.de`. Sicherungen unter `/html/backup`.
Kein Git auf dem Server. `$HOME` zeigt auf `/` — beschreibbar sind nur
`/html`, `/tmp` und `var/`.

**Zwei Seitenbäume.** Die Site-Konfiguration nutzt `rootPageId: 171` (170 Seiten,
972 Elemente). Daneben liegt ein alter Baum unter „Home-alt" (uid 1, 26 Seiten,
516 Elemente), der **nicht** ausgeliefert wird. Bei jeder Auswertung zwischen
beiden unterscheiden — sonst entstehen falsche Zahlen.

## Mask und Content Blocks — geklärt

Die Content Blocks im Sitepackage sind **Ersatz an Ort und Stelle**, kein
Migrationsziel:

```yaml
name: mask/referenz
typeName: mask_referenz     # übernimmt den Mask-CType
prefixFields: false         # nutzt die vorhandenen tx_mask_*-Spalten
```

**Eine Datenmigration ist nicht nötig.** Am 21.08.2026 auf `/ueber-uns` verifiziert:
17 Mitarbeiter-Elemente rendern mit Namen, Zitaten und Bildern aus den
`tx_mask_*`-Spalten.

Der aktive Baum nutzt nur **74 Mask-Elemente** — `mask_mitarbeiter` (51) und
`mask_breadcrumb` (22). Die 263 `mask_referenz` liegen im alten Baum in einem
Ordner (`doktype 254`) und werden nicht ausgeliefert.

`mask/mask` hat **keine v14-Fassung und keine in Arbeit**. Der Weg nach v14 führt
über content-blocks, das den CType weiterträgt.

## Verifizierte Fakten statt Vermutungen

**Echte CType-Bezeichner** (Punkt B3, teilweise erledigt):

| vermutet | tatsächlich |
|---|---|
| `list_group` | `listgroup` |
| `nees_imagehover` | `wowgmbh_imagehover` |
| `carousel_small` | im Einsatz: `carousel`, `carousel_fullscreen` |

Bestätigt: `texticon`, `icon_group`, `accordion`, `timeline`.
Container: `ce_columns2/3/4`, `ce_container`, `ce_grid`, `ce_tabs`, `ce_slider`, `ce_card`.

**powermail ist unbenutzt** — 0 Formulare, 0 Felder, 0 Einsendungen. Ersatzlos streichen.

**container-elements** ist mit 150 Struktur-Elementen und 507 Kindern im Einsatz.
v14 liegt auf `main` ohne Tag, Alias `8.0.x-dev`, zieht `buepro/typo3-pvh`
(`4.0.x-dev`) nach — beide müssen in die Wurzel-`composer.json`.

## Stand Staging (21.08.2026)

**Behoben:**

- `friendsoftypo3/content-blocks` **1.6.3** nachinstalliert — letzte Fassung für
  `^13.4.19`. Behebt den Totalausfall.
- Die 9 Content-Block-Definitionen schemakonform gemacht: 97 Zeilen `1`/`0` → `true`/`false`,
  leere `minitems` entfernt, `type: link` → `type: Link` in `breadcrumb`.
  **`content-blocks:lint` meldet keine Fehler mehr.**
- `extension:setup` läuft durch, Exit-Code 0, alle 47 Extensions.
- `database:updateschema --dry-run`: keine offenen Schemaänderungen.
- **134 von 136 Seiten des aktiven Baums liefern HTTP 200.**

Die korrigierten Definitionen liegen in `neue-Site/packages/nees/ContentBlocks/` —
damit sind die Content Blocks portiert.

**Aufgeräumt (Entscheidung des Kunden):**

- **Instagram entfernt.** 5 Elemente auf `deleted=1` (umkehrbar), UIDs in
  `/html/backup/entfernte-instagram-uids.txt`. Rückgängig:
  `UPDATE tt_content SET deleted=0 WHERE uid IN (60,62,83,673,1283);`
  `/aktuell/wow-bot` liefert seitdem HTTP 200.
- **Logdateien geleert** — waren 611 MB und 441 MB (logisch, sparse).

**Bewusst nicht angefasst:**

- **Dateispeicher „EXT" (uid 2)**, 135 Einträge, davon 23 referenziert. Alle 23
  hängen an `textpic`-Elementen im Ordner `/slider-elemente` (uid 7) — der liegt
  im **alten Baum, den der Kunde als Archiv behalten will**. Basispfad
  `public/typo3conf/ext/` existiert im Composer-Modus nicht, die Bilder sind also
  tot, gehören aber zum Archivbestand.
- **Alter Seitenbaum „Home-alt"** bleibt als Archiv erhalten.

- **Referenzindex aktualisiert** — 15602 Korrekturen über 59 Tabellen,
  Ergebnis „Index Integrity was perfect!". Vor dem Upgrade wiederholen.

**Noch offen:**

- **189 verwaiste `sys_file`-Sätze** unter `/typo3temp/assets/tx_instagram/`,
  null Referenzen, plus das Verzeichnis auf der Platte. Rückstand des entfernten
  Plugins. **Es gibt kein Bordmittel dafür**: `typo3_console` 8.x hat seine
  `cleanup:`-Befehle abgeschafft, der Core bietet nur `referenceindex:update`.
  Die Install-Tool-Funktionen decken andere Fälle ab. Bleibt SQL oder liegenlassen.
- **64 `html`-Elemente** im aktiven Baum — bei der Designumstellung problematisch.
- Die gemeldete TypoScript-Bedingung `[ == 1]` war weder in Dateien noch in
  `sys_template` auffindbar.

## Nicht übernehmen (Altlasten)

Typekit mit futura-pt,
Google Fonts Poppins und Source Sans 3 (geladen, aber ungenutzt), League Spartan,
`styleguide-bak.html`, `Example.html`, `new_slope.scss`, die `slope`-Frames,
die Farbvariablen `tertiary` und `quaternary`, `nees_1.0.0.zip`.

Ebenfalls korrigiert: `$font-color`, `$font-size` und `$font-line-height` im
Altbestand sind keine Bootstrap-Variablennamen und waren wirkungslos. Richtig sind
`$body-color`, `$font-size-base` und `$line-height-base`.

## SCSS

Aufbau unter `neue-Site/packages/nees/Resources/Public/Scss/Theme/`:

| Ordner | Inhalt |
|---|---|
| `theme.scss` | Einstieg. **Reihenfolge ist bindend**: `_variables.scss` vor bootstrap_package, sonst greifen dessen `!default`-Werte. |
| `_variables.scss` | Nur Variablen, Maps, Mixins. Kein CSS. |
| `Page/` | Grundlagen: fonts, base, typography, buttons, frames, navigation, footer |
| `Components/` | 17 Komponenten, benannt wie in `nees-komponenten.csv` |
| `Modules/` | news |

Klassenschema aus `page.tsconfig`: `frame_class` → `.frame-<wert>`,
`frame_options` → `.frame-option-<wert>`, `space_before_class` →
`.frame-space-before-<wert>`.

**Abgeleitete Farben über `nees-mix()`**, nicht über das blosse `mix()`. Letzteres
gibt in aktuellem Dart Sass Prozentwerte mit voller Nachkommagenauigkeit aus
(`rgb(78.2431372549%, ...)`). Die Hülle rundet auf ganze Kanäle.

## Stand

Das SCSS **kompiliert** — geprüft mit Dart Sass 1.103 gegen eine Hülle, die die
Bootstrap-Mixins ersetzt, Ergebnis 1463 Zeilen CSS ohne Fehler. Im kompilierten
Ergebnis stehen genau vier deckende Farbwerte, alle `rgba()`-Angaben sind
Transparenzen davon.

Der echte Build **mit** bootstrap_package läuft inzwischen auf dem Staging.
Die Überschreibungen greifen, die Klassennamen passen zu den Templates —
am Hero durchgemessen, siehe „Was der Hero gekostet hat".

**Am 26.08.2026 nach der Typo-Umstellung bestätigt:** scssphp kompiliert
fehlerfrei durch, 406 KB CSS, HTTP 200. **Dart Sass liegt lokal nicht mehr
vor**, und die Hülle aus der ersten Prüfung ist nicht im Repo — vor Ort lässt
sich also nicht kompilieren. Kontrolle geht nur über den Staging-Build:
nach dem `cache:flush` die Seite laden und prüfen, ob in
`/typo3temp/assets/bootstrappackage/css/` ein neuer `theme-<hash>.css`
mit plausibler Größe liegt. Bleibt der alte Hash stehen, ist der Compiler
auf einen Fehler gelaufen.

Dabei hat sich gezeigt: **SCSS zu schreiben, das nirgends greift, fällt nicht
auf.** Der `.nees-hero`-Block gestaltete eine Klasse, die in keinem Template
vorkommt. Träger ist `.frame-half`. Bei den übrigen 16 Komponenten dasselbe
zuerst prüfen, bevor an ihren Werten gefeilt wird.
`@import` erzeugt Deprecation-Warnungen — bewusst beibehalten, weil die Umstellung
auf `@use` von bootstrap_package abhängt.
