/*
 * Abschnitt: Projekt
 *
 * Große Aussage auf heller Fläche. Beim Hereinscrollen steigen die einzelnen
 * Buchstaben aus einer unsichtbaren Kante nach oben ins Bild — versetzt und
 * nicht der Reihe nach, sondern durcheinander.
 *
 * assets/js/sections/projekt.js zerlegt die Überschrift und setzt am
 * Abschnitt nur eine einzige Klasse: `is-satz`, sobald oben angedockt ist.
 * Wann welcher Buchstabe dran ist, rechnet das CSS aus dessen eigenem Verzug
 * (--ld-buchstaben-d) aus.
 */

/*
 * **Genau ein Bildschirm, seit die Bildfolge gestrichen ist.**
 *
 * Der Abschnitt war vorher hoch: oben 240 Bildpunkte Polster, unten keines,
 * darunter die scrollgesteuerte Bildfolge. Ohne sie bleiben Leitsatz,
 * Einleitung und die sechs Zahlen — und die sollen **zusammen auf einen
 * Bildschirm**, denn hier dockt der Auftakt an. Bei den alten Werten stand der
 * Abschnitt auf 1337 bei 1203 Fensterhöhe, die untere Zahlenreihe fiel also
 * heraus.
 *
 * **`box-sizing` gehört an dieselbe Stelle wie die Höhe.** Das Theme steht auf
 * `content-box`; ohne diese Zeile käme das Polster auf die 100svh obendrauf und
 * der Abschnitt wäre wieder zu hoch. Derselbe Fall wie auf der Wohnungsseite.
 */
/*
 * **Der Abschnitt ist ein Schacht mit klebender Bühne, seit dem 16.09.2026.**
 *
 * Vorher war er genau ein Bildschirm. Jetzt läuft darin eine Sequenz in vier
 * Stufen, alle am Scrollrad:
 *
 *   1  Der Bildschirm dockt an, Überschrift und Einleitung bauen sich auf.
 *   2  Es zoomt hinein: Die Überschrift wächst, die Einleitung tritt ab.
 *   3  In den Buchstaben erscheint der Film — die Zeichen sind das Fenster.
 *   4  Die Buchstaben sind so groß, dass nur noch Film übrig ist; er läuft.
 *   5  Die Bühne lässt los, der Film schiebt nach oben, die Zahlen kommen.
 *
 * **Alles hängt an einer Zahl:** `--ld-zoom-p` läuft von 0 bis 1 über den
 * Schacht; `projekt.js` setzt sie, das Aussehen macht ausschließlich diese
 * Datei. Dieselbe Bauart wie die Galerie und die Badvariationen.
 *
 * **Der Schacht ist drei Bildschirme lang** — einer für den Zoom, einer für
 * den laufenden Film, einer fürs Hochschieben. Zum Vergleich: „Die Idee" hat
 * 6,6, die Galerie 6,16.
 */
.ld-projekt {
	/*
	 * **Ohne Skript ist der Abschnitt ein Bildschirm.** Der Schacht wächst
	 * erst, wenn `projekt.js` die Ausstanzung gebaut hat — sonst stünden hier
	 * zwei Bildschirmhöhen leere Fläche, in denen nichts passieren kann.
	 * Dieselbe Vorsicht wie beim Knopf „Mehr anzeigen" unter der
	 * Wohnungsliste, der ohne Skript gar nicht erst auftaucht.
	 */
	--ld-schacht: 100svh;
	padding: 0;
}

.ld-projekt.is-zoombereit {
	--ld-schacht: 300svh;
}

/*
 * **Der Schacht nimmt die volle Fensterbreite, und dafür braucht es beides.**
 *
 * `align: full` im Markup nimmt ihm die `contentSize` — ohne sie wäre er 720
 * Bildpunkte breit, und Bühne und Film stünden als hochkantes Feld mitten auf
 * der Seite. Genau so stand es einen Tag lang da.
 *
 * **Die negativen Ränder muss man ihm danach wieder abnehmen.** WordPress
 * zieht jedes `alignfull` um das Polster der Wurzel nach außen — und rechnet
 * dabei mit dem Polster, das der Abschnitt *hätte*. Dieser hier steht auf
 * `padding: 0`, also schoss der Schacht um zweimal 30,7 Bildpunkte über das
 * Fenster hinaus und die Seite ließ sich seitlich schieben.
 *
 * **Daraus die Regel: Wer einem Block das Polster nimmt, nimmt seinen
 * `alignfull`-Kindern die negativen Ränder.** Die beiden gehören zusammen und
 * stehen an zwei verschiedenen Stellen.
 *
 * **Und der Selektor braucht dieselbe Gewichtung wie die Regel, die er
 * aufhebt.** WordPress schreibt `.has-global-padding > .alignfull` und zählt
 * damit zwei Klassen; eine einzelne eigene Klasse verliert dagegen —
 * wirkungslos und ohne Fehlermeldung. Derselbe Fall wie beim Wort „Menü" in
 * der Kopfzeile auf dem Telefon.
 */
.ld-projekt > .ld-projekt__schacht.alignfull {
	position: relative;
	height: var(--ld-schacht);
	margin: 0;
	max-width: none;
}

.ld-projekt__buehne {
	position: sticky;
	inset-block-start: 0;
	height: 100svh;
	overflow: clip;
	margin-block: 0;
	/*
	 * Der Grund gehört der Bühne, nicht dem Abschnitt: Der Film liegt darin
	 * und soll nirgends durchscheinen, solange er nicht an der Reihe ist.
	 */
	background: var(--wp--preset--color--surface-mist);
	/*
	 * **Hier stand `isolation: isolate`, und es hatte nichts zu isolieren.**
	 * In diesem Abschnitt mischt keine Ebene. Die Zeile macht aber einen
	 * eigenen Stapelraum auf, und ein Video darin kann der Browser nicht mehr
	 * unmittelbar auf den Schirm legen — er muss es in jedem Videobild mit
	 * allem anderen zusammensetzen. Bei einem bildschirmfüllenden Film ist
	 * das keine Kleinigkeit.
	 *
	 * **Die Regel: `isolation` nur, wo wirklich gemischt wird.** Vorsorglich
	 * gesetzt kostet sie, ohne etwas zu leisten.
	 */
}

/*
 * Der Text sitzt mittig auf der Bühne — dieselbe Anordnung wie vorher, nur
 * jetzt in einer freigestellten Ebene statt im Fluss.
 */
.ld-projekt__inhalt {
	position: absolute;
	inset: 0;
	z-index: 2;
	box-sizing: border-box;
	margin: 0;
	padding-block: clamp(3.5rem, 7svh, 6.5rem);
	display: flex;
	flex-direction: column;
	justify-content: center;
}

/*
 * **Die Einleitung tritt ab, sobald der Zoom einsetzt.** Vom Kunden so
 * entschieden: Der Bildschirm räumt sich frei für die wachsende Überschrift,
 * statt zwei Dinge gleichzeitig zu zeigen. Beim Zurückscrollen kommt sie
 * wieder — der Wert hängt am Scrollweg, nicht an einer Klasse.
 */
.ld-projekt__einleitung {
	opacity: calc(1 - var(--ld-zoom-p, 0) * 6);
}

/*
 * **Film und Ausstanzung.** Der Film liegt ganz hinten, darüber die Fläche in
 * Papierfarbe, aus der die Buchstaben ausgestanzt sind — durch sie hindurch
 * sieht man den Film.
 *
 * **Warum ausgestanzt und nicht gefüllt:** Ein Video lässt sich nicht als
 * Hintergrund in Text legen. Der Weg, der trägt, ist umgekehrt.
 */
.ld-projekt__zoom {
	position: absolute;
	inset: 0;
	z-index: 1;
	margin: 0;
	pointer-events: none;
}

/*
 * Der Vermerk „Visualisierung, KI-gestützt" (base.css) gehört zum Film, nicht
 * zum Papier: Er kommt erst, wenn die Fläche aufgelöst ist und der Film frei
 * steht — und geht beim Zurückscrollen als Erstes wieder.
 */
.ld-projekt__zoom > .ld-ki-vermerk {
	opacity: 0;
	transition: opacity 0.45s var( --ld-ease );
}

.ld-projekt.is-frei .ld-projekt__zoom > .ld-ki-vermerk {
	opacity: 1;
}

/*
 * **Der Film blendet ein, nicht die Ausstanzung** — und das ist der Griff, an
 * dem der ganze Übergang hängt.
 *
 * Die Papierfläche mit den ausgeschnittenen Buchstaben liegt **von Anfang an**
 * da. Sie hat genau die Farbe des Abschnittsgrundes, durch die Löcher sieht
 * man also denselben Ton wie daneben: Sie ist unsichtbar, solange hinter ihr
 * nichts liegt. Erst der Film macht die Löcher sichtbar.
 *
 * **Andersherum ginge es nicht.** Würde die Fläche eingeblendet, läge in dem
 * Moment davor der Film offen über dem ganzen Bildschirm — ein Aufblitzen des
 * Videos, bevor die Überschrift überhaupt an der Reihe ist.
 *
 * Die leichte Vergrößerung ist die Tiefe dahinter: Der Film kommt einem
 * entgegen, während die Buchstaben auseinanderlaufen.
 */
.ld-projekt__film {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	margin: 0;
	opacity: clamp(0, calc(var(--ld-zoom-p, 0) * 16), 1);
	/*
	 * **Kein zusätzlicher Zoom auf dem Film — er hat 12 Prozent Bild
	 * gekostet.** Die leichte Vergrößerung sollte Tiefe geben; sie hat aber
	 * genau das weggenommen, worum es am Schluss geht. **Vom Kunden
	 * gemeldet:** „der Endframe sieht sehr reingecroppt aus." Jede
	 * Vergrößerung des Medienkastens über sein Fenster hinaus ist Beschnitt,
	 * und zwar quadratisch — die Regel steht schon bei der Kachel in „Die
	 * Lage".
	 *
	 * **Und der Ausschnitt hängt unten, nicht in der Mitte.** Der Film ist
	 * 1,337 zu 1, die Bühne rund 1,59 — es fällt oben und unten etwas weg.
	 * Mittig verankert schnitt das **das Vorderhaus unten an**; unten
	 * verankert fällt statt dessen Himmel weg. Der Wert steht auch in
	 * `projekt.js`, denn die Marke muss denselben Beschnitt rechnen.
	 */
	object-position: 50% 82%;
}

/*
 * **Die Ausstanzung.** Eine Fläche in Papierfarbe über dem Film, aus der die
 * beiden Zeilen ausgeschnitten sind. `projekt.js` baut sie als SVG aus dem
 * Text der Überschrift und deren gemessenen Maßen — sie sitzt deshalb auf den
 * Bildpunkt genau dort, wo die Überschrift steht.
 *
 * **Sie kommt erst, wenn der Zoom einsetzt**, und löst die echte Überschrift
 * in einer kurzen Blende ab. Beide zeigen denselben Satz in derselben Schrift
 * und Größe; der Wechsel ist deshalb nicht zu sehen.
 */
.ld-projekt__stanze {
	position: absolute;
	inset: 0;
	z-index: 1;
	display: block;
	width: 100%;
	height: 100%;
}

/*
 * Die Farbe steht hier und nicht im Skript — `theme.json` ist die einzige
 * Design-Quelle, und eine Fläche in Papierfarbe ist eine Gestaltungsfrage.
 */
.ld-projekt__stanze-flaeche {
	fill: var(--wp--preset--color--surface-mist);
}

/*
 * **Die Blende hängt am Scrollweg, nicht an der Uhr.**
 *
 * Hier stand einmal eine Überblendung von einer Viertelsekunde, ausgelöst
 * über eine Klasse. Sie hatte einen Fehler, den man erst auf dem Telefon
 * sah: Die Ausstanzung wächst am Scrollrad, die Blende lief an der Uhr —
 * wer zügig scrollte, sah die echte Überschrift in Originalgröße und die
 * ausgestanzte schon halb so groß wieder darüber. **Ein Doppelbild.**
 *
 * Beide Deckungen hängen jetzt an derselben Zahl. Damit sind sie im Moment
 * des Wechsels immer gleich groß, und was man sieht, ist allein der Wechsel
 * der Füllung: aus grüner Schrift wird Film.
 *
 * Der Faktor 16 heißt: durch bei einem Sechzehntel der Fahrt, also bei rund
 * Faktor 1,2. Diesen Rest an Größenunterschied sieht niemand.
 */
.ld-projekt__titel {
	opacity: clamp(0, calc(1 - var(--ld-zoom-p, 0) * 16), 1);
}

/*
 * **Gezoomt wird über `scale`, nicht über den Schriftgrad.** Ein wachsender
 * Grad zwänge den Browser, den Text in jedem Einzelbild neu zu setzen und die
 * Maske neu zu rastern — genau der Fehler, der im Auftakt geruckelt hat. Eine
 * Skalierung ist eine Verwandlung und kostet nichts.
 *
 * **Und am Ende blendet das Papier weg.** Bei Faktor 14 stehen zwischen den
 * Strichen der Buchstaben immer noch Papierflächen; sie wegzuzoomen bräuchte
 * ein Vielfaches davon, und dann wären die Kanten unscharf. Die letzten
 * Prozent der Fahrt lösen die Fläche deshalb auf, und übrig bleibt der Film.
 */
/*
 * **Skaliert wird das ganze SVG, nicht die Buchstaben in der Maske.** Papier
 * und Löcher wachsen dadurch miteinander, und das Ergebnis sieht gleich aus —
 * es kostet aber nicht dasselbe: Eine Verwandlung **in** der Maske ändert
 * deren Inhalt in jedem Einzelbild, und der Browser muss die ganze
 * bildschirmfüllende Maske jedes Mal neu rastern.
 *
 * Den Fixpunkt setzt `projekt.js` — er liegt an der dicksten Stelle eines
 * Buchstabens, damit man wirklich durch ihn hindurchfliegt.
 *
 * Die Weite rechnet ebenfalls das Skript: Sie wächst auf einer Potenz, damit
 * das Zoomen über die ganze Fahrt gleich schnell wirkt.
 */
.ld-projekt__stanze-lauf {
	scale: var(--ld-zoom-w, 1);
}

/*
 * **Die Löcher werden zum Schluss breiter, nicht nur größer.**
 *
 * Ein Buchstabe ist ein Schaft: Er deckt den Bildschirm senkrecht sofort und
 * seitlich fast nie — deshalb stand am Ende der Fahrt immer noch Papier links
 * und rechts. Eine Kontur an den Zeichen der Maske verbreitert jedes Loch.
 *
 * **Sie wächst erst im letzten Drittel**, wenn man ohnehin nur einen
 * Ausschnitt des Buchstabens sieht; dass er dabei fetter wird, ist dann nicht
 * mehr zu erkennen. Wie breit, rechnet `projekt.js` aus der gemessenen
 * Strichstärke.
 */
.ld-projekt__stanze-lauf text {
	stroke: #000;
	stroke-width: calc(
		clamp(0, (var(--ld-zoom-p, 0) - 0.55) / 0.35, 1) *
		var(--ld-stanze-fett, 0px)
	);
}

/*
 * **Das Papier löst sich in der Mitte der Fahrt auf, nicht am Ende.**
 *
 * Am Ende sah man es **nach unten wegwischen** — vom Kunden gemeldet, und der
 * Grund ist Geometrie: Der Fixpunkt liegt im D und damit etwas über der
 * Bildmitte. Alles läuft von ihm weg; die letzte Papierkante zieht deshalb
 * nach unten aus dem Bild. Je später die Blende, desto länger sieht man diese
 * Kante fahren.
 *
 * **Die eigentliche Antwort ist aber der Fixpunkt, nicht die Blende.** Seit
 * er auf der Bildmitte sitzt, läuft das Papier gleichmäßig nach allen Seiten
 * aus dem Bild — das liest sich als Aufgehen und nicht als Wischen. Eine
 * lange Blende war der erste Versuch und hatte einen eigenen Fehler: Auf
 * halber Deckung schimmerte der Film über den **ganzen** Bildschirm durch,
 * und aus der Papierfläche wurde ein Schleier.
 *
 * Es bleiben deshalb die letzten fünfzehn Prozent — genug, um den Rest in den
 * Ecken zu nehmen, zu kurz, um als Schleier aufzufallen.
 */
.ld-projekt__stanze-flaeche {
	opacity: clamp(0, calc((1 - var(--ld-zoom-p, 0)) / 0.06), 1);
}

/*
 * **Steht der Film, ist die Ausstanzung weg — ganz, nicht nur unsichtbar.**
 *
 * Eine Ebene mit `opacity: 0` wird weiterhin zusammengesetzt, und diese hier
 * ist eine bildschirmfüllende Maske über einem laufenden Video. `display:
 * none` nimmt sie aus dem Spiel; gebraucht wird sie ab hier nicht mehr.
 *
 * **Und der Film beginnt genau hier zu laufen** — `projekt.js` startet ihn an
 * derselben Marke. Die beiden gehören zusammen: Ein Video und eine
 * bildschirmfüllende Maske teilen sich denselben Weg auf den Schirm, und
 * gleichzeitig ging es nicht.
 */
.ld-projekt.is-frei .ld-projekt__stanze {
	display: none;
}

/* ------------------------------------------- Die Marke über dem Komplex */

/*
 * **Schriftzug auf einem kurzen Stiel, wie über den beiden Häusern auf der
 * Umgebungskarte.** Dort ist es dasselbe Bauteil und dieselbe Datei; man soll
 * die beiden Stellen als dieselbe Sache lesen.
 *
 * **Sie sitzt auf einem Punkt im Film, nicht auf einem Punkt des
 * Bildschirms.** Der Film liegt unter `object-fit: cover` und wird je nach
 * Fensterformat anders beschnitten — ein Prozentwert der Bühne zeigte auf
 * einem schmalen Fenster auf ein anderes Dach als auf einem breiten.
 * `projekt.js` rechnet den Beschnitt nach und setzt `--ld-pm-x` und
 * `--ld-pm-y` in Bildpunkten der Bühne.
 *
 * **Sie liegt über allem**, auch über der Ausstanzung — sie kommt ja erst,
 * wenn von der Ausstanzung ohnehin nichts mehr zu sehen ist.
 */
.ld-projekt__marke {
	/*
	 * **Seit dem 01.10.2026 steht hier „metz28"** statt des bloßen „metz" —
	 * dieselbe Marke wie in der Pille, vom Kunden so gewünscht.
	 *
	 * **Die Breite wächst dabei um den Faktor 1,414** (3,941 ÷ 2,787), und
	 * das ist Absicht: Auf der alten Breite wären die Buchstaben um fast ein
	 * Drittel kleiner geworden — und genau an dieser Marke hat der Kunde
	 * zweimal verlangt, sie möge **größer** werden. So bleibt die Schrift auf
	 * den Bildpunkt so groß wie bisher (62,7 hoch bei 2056), nur steht jetzt
	 * die Hausnummer daneben.
	 *
	 * Anders als auf der Umgebungskarte gibt es hier nichts, was die Marke
	 * nicht überragen darf: Sie schwebt über einem Dach in einem
	 * bildschirmfüllenden Film.
	 */
	--ld-pm-breite: clamp(7.78rem, 12.02vw, 15.55rem);
	--ld-pm-verhaeltnis: 3.941;
	--ld-pm-stiel: clamp(4rem, 12vh, 9.5rem);

	position: absolute;
	z-index: 3;
	inset-block-start: var(--ld-pm-y, 50%);
	inset-inline-start: var(--ld-pm-x, 50%);
	width: 0;
	height: 0;
	margin: 0;
	pointer-events: none;
}

/*
 * Der Stiel steht auf dem Baukörper und wächst nach oben. `transform-origin`
 * unten, damit er aus dem Dach herauskommt und nicht aus der Luft.
 */
.ld-projekt__marke::before {
	content: "";
	position: absolute;
	inset-block-end: 0;
	inset-inline-start: -1px;
	width: 2px;
	height: var(--ld-pm-stiel);
	transform-origin: 50% 100%;
	scale: 1 0;
	transition: scale 1.1s cubic-bezier(0.16, 1.02, 0.24, 1);
	background: var(--wp--preset--color--base);
}

/*
 * **Die Marke steht frei im Bild, ohne Kasten.**
 *
 * Hier lag einmal die Pille der Kopfzeile darunter — sie war messbar richtig
 * und sah falsch aus; **vom Kunden gestrichen.** Und sie war teuer: Ein
 * `backdrop-filter` über einem laufenden Video wird in **jedem Videobild** neu
 * gerechnet. Das war der Ruckler.
 *
 * **Stattdessen der Schriftzug in Kalkweiß mit einem engen Schatten.**
 * Nachgemessen reicht die Leuchtdichte unter der Marke von 0,043 bis 0,622 —
 * besonntes Dach neben Schatten. Eine Farbe zu wählen hieße, sich für eine der
 * beiden Hälften zu entscheiden; der Schatten dunkelt dagegen nur den Saum um
 * die Striche ab und lässt das Bild sonst in Ruhe. Dieselbe Regel wie bei
 * „Haidhausen": **eine Vignette, keine Fläche** — hier nur so klein wie die
 * Schrift selbst.
 *
 * Die helle Fassung der Marke liegt als eigene Datei daneben und kommt aus
 * `werkzeuge/marke-faerben.py`; ändert sich die Palette, wird sie dort neu
 * erzeugt.
 */
.ld-projekt__marke-bild {
	position: absolute;
	margin: 0;
	inset-inline-start: calc(-0.5 * var(--ld-pm-breite) + var(--ld-pm-versatz, 0px));
	inset-block-end: var(--ld-pm-stiel);
	width: var(--ld-pm-breite);
	height: calc(var(--ld-pm-breite) / var(--ld-pm-verhaeltnis));
	opacity: 0;
	/*
	 * **Der Auftritt ist ein Ploppen, kein Einblenden.** Die Kurve schießt
	 * über den Zielwert hinaus und fällt zurück — `cubic-bezier` mit einem
	 * zweiten Stützpunkt über 1. Vom Kunden so gewählt.
	 */
	/*
	 * **Ruhig und weit statt kurz und schnippisch.** Der erste Auftritt war
	 * ein halbsekündiges Ploppen mit deutlichem Überschwingen; **vom Kunden
	 * als zu hektisch gemeldet.** Jetzt steigt sie über anderthalb Sekunden
	 * aus doppelt so tiefer Lage, mit einem Auslaufen vierter Ordnung und
	 * einem Hauch Überschwingen — dieselbe Familie wie die Riesenwörter im
	 * Theme, nur langsamer.
	 */
	translate: 0 2rem;
	scale: 0.94;
	transition:
		opacity 0.9s cubic-bezier(0.22, 1, 0.36, 1) 0.1s,
		translate 1.5s cubic-bezier(0.16, 1.02, 0.24, 1) 0.1s,
		scale 1.5s cubic-bezier(0.16, 1.02, 0.24, 1) 0.1s;
}

.ld-projekt__marke-bild img {
	display: block;
	width: 100%;
	height: 100%;
	/*
	 * Zwei Schatten übereinander: ein enger, harter für die Kante und ein
	 * weiter, weicher für den Grund. Einer allein trägt nicht — der enge
	 * verliert sich auf unruhigen Dächern, der weite macht aus der Marke
	 * einen Fleck.
	 *
	 * **Seit dem 21.10.2026 ein dritter, noch weiterer**: Die Marke steht
	 * jetzt zwischen den beiden Häusern, und der Grund dort ist hell.
	 */
	filter:
		drop-shadow(0 1px 2px rgb(0 0 0 / 0.85))
		drop-shadow(0 0 0.9rem rgb(0 0 0 / 0.75))
		drop-shadow(0 0 2.4rem rgb(0 0 0 / 0.55));
}

/*
 * Der Stiel steht auf einem Foto und braucht dieselbe Hilfe wie die Marke —
 * bei zwei Bildpunkten Breite genügt ein weicher Schatten statt einer Fläche.
 */
.ld-projekt__marke::before {
	filter: drop-shadow(0 0 0.35rem rgb(0 0 0 / 0.7));
}

/*
 * **Sie steht nur, solange der Zoom ganz durch ist.**
 *
 * `is-marke` heißt „der Film ist durchgelaufen", `is-halt` heißt „die Fahrt
 * steht am Ende". Beim Zurückscrollen fällt `is-halt` als Erstes weg — die
 * Marke zieht sich also zurück, **bevor** sich an den Buchstaben etwas rührt.
 * **Vom Kunden so gewünscht.** Ohne das blieb sie liegen, während der
 * Bildschirm schon wieder aufging.
 */
.ld-projekt.is-marke.is-halt .ld-projekt__marke::before {
	scale: 1 1;
}

.ld-projekt.is-marke.is-halt .ld-projekt__marke-bild {
	opacity: 1;
	translate: 0 0;
	scale: 1;
}

/*
 * **Die Neigung mit der Maus**, sobald Film und Marke stehen — wie im
 * 3D-Wohnungsfinder, vom Kunden gewünscht. `projekt.js` setzt `--ld-pm-nx`
 * und `--ld-pm-ny` zwischen −1 und 1 an der Bühne, gedämpft.
 *
 * **Film und Marke kippen um denselben Punkt**, die Mitte der Bühne. Die
 * Marke ist ein Punkt ohne Fläche; ihr Drehpunkt wird deshalb in die
 * Bühnenmitte verlegt (`--ld-pm-dreh-x/-y`, vom Skript gerechnet), sonst
 * liefe sie beim Kippen vom Dach weg.
 *
 * **Höchstens 2,5 Grad zur Seite und 1,5 nach oben und unten.** Damit beim
 * Kippen kein Rand der Bühne aufreißt, wird vergrößert — aber nur so weit,
 * wie die Neigung gerade verlangt (`--ld-pm-s`, vom Skript gerechnet: bis 3
 * Prozent bei voller Seitenneigung, 1,5 bei voller Höhenneigung). Steht die
 * Maus mittig, ist das ganze Filmbild zu sehen. Bis zum 21.10.2026 waren es
 * feste 4 Prozent; **vom Kunden gemeldet:** „sehr, sehr reingezoomt".
 * `transform` statt `translate`: Es ist eine einzige Verwandlung, die der
 * Browser fertig zusammensetzt, ohne das Video neu zu zeichnen.
 *
 * **Gekippt wird der Film, nicht seine Hülle** — in der Hülle steht auch der
 * Vermerk „Visualisierung, KI-gestützt", und der rutschte mit der
 * Vergrößerung aus dem Bild.
 */
.ld-projekt__film,
.ld-projekt__marke {
	--ld-pm-neigung:
		perspective(80rem)
		rotateY(calc(var(--ld-pm-nx, 0) * 2.5deg))
		rotateX(calc(var(--ld-pm-ny, 0) * -1.5deg));
}

.ld-projekt.is-marke.is-halt .ld-projekt__film {
	transform: var(--ld-pm-neigung);
	scale: var(--ld-pm-s, 1);
}

.ld-projekt.is-marke.is-halt .ld-projekt__marke {
	transform-origin: var(--ld-pm-dreh-x, 0) var(--ld-pm-dreh-y, 0);
	transform: var(--ld-pm-neigung);
	scale: var(--ld-pm-s, 1);
}

@media (prefers-reduced-motion: reduce) {
	.ld-projekt.is-marke.is-halt .ld-projekt__film,
	.ld-projekt.is-marke.is-halt .ld-projekt__marke {
		transform: none;
		scale: 1;
	}
}

/*
 * Ohne Skript gibt es weder Film noch Zoom — dann hat die Marke auch keinen
 * Ort und hätte auf dem leeren Bildschirm nichts zu suchen.
 */
.ld-projekt:not(.is-zoombereit) .ld-projekt__marke {
	display: none;
}

/*
 * Schriftbild nach der Vorlage: dort läuft die Aussage in Halvar Breitschrift,
 * Schnitt Medium, mit deutlich negativer Laufweite (−0,08 em) und einer
 * Zeilenhöhe von 1,16. Halvar ist kostenpflichtig; hier steht als
 * Entsprechung die breite Schnittweite von Archivo, selbst gehostet.
 */
.ld-projekt__titel {
	/*
	 * Der Zeilenumbruch steht fest im Text. Die Begrenzung fängt nur sehr
	 * schmale Fenster ab, in denen die Zeilen sonst zusätzlich brechen würden.
	 */
	max-width: 22ch;
	margin-inline: auto;
	/*
	 * Regular — das hier ist eine große Überschrift, kein Aussagesatz. Der
	 * Schnitt richtet sich im ganzen Theme nach der Art des Textes und nicht
	 * nach der Helligkeit des Grundes: Überschriften Regular, Aussagesätze
	 * Light, kleine Fließtexte Work Sans.
	 */
	font-weight: 400;
	font-size: clamp(2.2rem, 7.2vw, 9.5rem);
	/*
	 * 1,12 statt 1,0 — dieselbe Ausnahme wie bei der Aussage in „Die Idee":
	 * Die Vorlage setzt ihre Versalien auf 1,0, im Englischen gibt es aber
	 * keine Umlaute. Bei einer deutschen Zeile mit Ä, Ö oder Ü stoßen die
	 * Punkte sonst an die Zeile darüber. Einzeilige Riesenwörter bleiben
	 * deshalb auf 1,0, mehrzeilige stehen auf 1,12.
	 */
	line-height: 1.12;
	/*
	 * Serifenschriften brauchen keine negative Laufweite — die stammte von der
	 * breiten Grotesk, die hier vorher stand. Die Vorlage setzt ihre
	 * Überschriften ebenfalls ohne Sperrung.
	 */
	letter-spacing: normal;
	text-transform: uppercase;
	text-wrap: balance;
}

/*
 * Einleitung unter der Aussage. Ein ruhiger Textblock in bequemer Zeilenbreite —
 * mehr als etwa 60 Zeichen pro Zeile liest sich schlecht.
 */
.ld-projekt__einleitung {
	max-width: 58ch;
	margin-block-start: clamp(1.75rem, 3.6vw, 4rem);
	margin-inline: auto;
	/*
	 * `md` statt `lg`. Auf `lg` stand die Einleitung bei 30 Bildpunkten und
	 * damit fast doppelt so groß wie derselbe Fließtext an anderer Stelle —
	 * der Textblock im Umgebungs-Abschnitt hat 16. Mit der luftigen Zeilenhöhe
	 * zusammen las sich das gesperrt, obwohl gar keine Sperrung gesetzt ist.
	 */
	font-size: var(--wp--preset--font-size--md);
	line-height: 1.55;
	text-wrap: pretty;
}

/* ------------------------------------------------------- Buchstaben */

/*
 * Jedes Wort ist ein Fenster mit unsichtbarer Kante. Der Innenabstand oben
 * schafft Platz für Umlautpunkte, ohne die untere Kante aufzuheben — dort
 * verschwinden die Buchstaben, bevor sie hochsteigen.
 */
.ld-buchstaben__wort {
	display: inline-block;
	overflow: hidden;
	padding-block-start: 0.18em;
	margin-block-start: -0.18em;
	vertical-align: bottom;
}

/*
 * **Der Satz baut sich auf, nachdem der Auftakt oben angedockt ist** — nicht
 * mehr, während man auf ihn zuscrollt.
 *
 * **Vorher hing er am Scrollweg:** `--ld-buchstaben-p` lief von 0 auf 1,
 * sobald der Abschnitt von unten hereinkam, und war durch, wenn seine
 * Oberkante noch bei **20 Prozent der Fensterhöhe** stand — also während der
 * Auftakt noch ein Fünftel des Bildschirms füllte. **Vom Kunden geändert:**
 * Erst andocken, dann aufbauen.
 *
 * **Damit muss er zeitgesteuert laufen, nicht scrollgesteuert.** Wer nach dem
 * Andocken stehen bleibt, scrollt ja nicht weiter — ein scrollgesteuerter Satz
 * bliebe auf halbem Weg stehen. Dieselbe Bauweise wie „Umgebung" und „Die zwei
 * Wohnwelten": **einmal ausgelöst, dann läuft es von allein durch.** Das
 * Skript setzt dafür nur `is-satz`; gerechnet wird hier.
 *
 * **Die Proportionen sind die alten.** Der Verzug je Buchstabe reicht bis 0,67
 * des Laufs, der Weg eines Buchstabens dauert ein Drittel — zusammen ergibt
 * das genau die Gesamtzeit. Nur die Bezugsgröße ist jetzt eine Zeit statt
 * einer Scrollstrecke.
 *
 * **Die Zahl kommt aus der Vorlage:** Ein Buchstabe soll dieselben 0,9
 * Sekunden brauchen wie überall sonst im Theme (siehe `.ld-steigen`). Ein
 * Drittel des Laufs sind 0,9 s, also läuft das Ganze **2,7 Sekunden**.
 *
 * Die durcheinandergewürfelte Reihenfolge bleibt, wie sie war — sie steckt in
 * `--ld-buchstaben-d`, das `projekt.js` je Buchstabe aus einer festen
 * Streuformel setzt.
 */
.ld-projekt {
	/*
	 * **1,8 statt 2,7 Sekunden** — seit dem 14.10.2026, vom Kunden: Der ganze
	 * Bildschirm brauchte 4,35 Sekunden, bis er stand. Ein Buchstabe läuft
	 * damit 0,6 s, der letzte fliegt bei 1,2 s los.
	 */
	--ld-satz-lauf: 1.8s;
	--ld-satz-kurve: cubic-bezier(0.215, 0.61, 0.355, 1);
}

.ld-buchstaben__zeichen {
	display: inline-block;
	--ld-satz-dauer: calc(var(--ld-satz-lauf) / 3);
	--ld-satz-verzug: calc(var(--ld-buchstaben-d, 0) * var(--ld-satz-lauf));
	/*
	 * `translate` statt `transform` — dieselbe Aufteilung wie beim Abschied
	 * klebender Abschnitte, und sie hält frei, falls hier je eine zweite
	 * Bewegung dazukommt.
	 */
	translate: 0 118%;
	transition: translate var(--ld-satz-dauer) var(--ld-satz-kurve)
		var(--ld-satz-verzug);
}

.ld-projekt.is-satz .ld-buchstaben__zeichen {
	translate: 0 0;
}

/*
 * **Die Einleitung folgt, Wort für Wort.**
 *
 * Aussehen und Takt sind die gemessenen Werte des Themes: aus 0,55 em von
 * unten, aus 7 Bildpunkten Unschärfe, 0,7 Sekunden je Wort und 0,05 Sekunden
 * von Wort zu Wort — dieselbe Erscheinung wie `.ld-wort` in `base.css`.
 *
 * **Der Verzug steht am Wort**, gesetzt von `projekt.js`: 1,8 Sekunden Vorlauf
 * plus 0,05 je Wort. Die 1,8 sind kein runder Wert, sondern der größte Verzug
 * der Überschrift — der Absatz setzt also genau dann ein, wenn deren letzter
 * Buchstabe losfliegt. So macht es die Vorlage im Abschnitt „Architektur".
 *
 * **Ohne Skript passiert hier gar nichts**, weil es die Wörter dann nicht
 * gibt: Der Absatz steht als gewöhnlicher Text da.
 */
.ld-projekt__wort {
	display: inline-block;
	opacity: 0;
	translate: 0 0.55em;
	filter: blur(7px);
	transition:
		opacity 0.55s var(--ld-satz-kurve) var(--ld-satz-wort-v, 0s),
		translate 0.55s var(--ld-satz-kurve) var(--ld-satz-wort-v, 0s),
		filter 0.55s var(--ld-satz-kurve) var(--ld-satz-wort-v, 0s);
}

.ld-projekt.is-satz .ld-projekt__wort {
	opacity: 1;
	translate: 0 0;
	filter: blur(0);
}

/*
 * Ohne JavaScript und bei reduzierter Bewegung steht der Satz einfach da.
 * Das Zerlegen findet dann gar nicht erst statt.
 */
@media (prefers-reduced-motion: reduce) {
	.ld-buchstaben__zeichen,
	.ld-projekt__wort {
		opacity: 1;
		translate: none;
		filter: none;
		transition: none;
	}
}


/* ------------------------------------------- Vom Entwurf zum Bau */

/*
 * Zwei deckungsgleiche Bilder übereinander. Beim Scrollen baut sich zuerst die
 * Zeichnung von oben nach unten auf; noch während sie wächst, schiebt sich das
 * Rendering von unten nach oben darüber. Beide mit weicher Kante, ohne Linie.
 *
 * Dazu treten beide Bilder ganz leicht zurück: Sie starten vier Prozent
 * herangezoomt und enden bei normaler Größe. Der Zoom liegt auf den Bildern
 * selbst, der Rahmen bleibt gleich groß — man sieht also am Anfang einen
 * knapperen Ausschnitt, nicht ein größeres Bild.
 *
 * assets/js/sections/projekt.js setzt dafür drei Werte am Abschnitt:
 * --ld-wandel-entwurf, --ld-wandel-rendering und --ld-wandel-zoom.
 */
.ld-projekt__wandel {
	/*
	 * Wie viel vom Rendering am Ende oben stehen bleibt. Das Bild fährt nicht
	 * ganz aus der Bühne — sonst stünden die Fakten am Schluss allein auf einer
	 * leeren Fläche. Es hält an, sobald seine Unterkante dieses Band erreicht:
	 * oben ein Streifen Bild, darunter die Fakten, beides als ein Bild.
	 */
	--ld-band: 25svh;
	/* Luft zwischen der Unterkante des Bandes und der ersten Faktenreihe. */
	--ld-fakten-luft: 11svh;
	margin-block-start: clamp(2rem, 4.5vw, 5.5rem);
}

.ld-projekt__wandel.is-bereit {
	height: 320svh;
}

.ld-projekt__wandel-buehne {
	position: relative;
	/* Bild und Fakten wandern hier hindurch — alles außerhalb bleibt verdeckt. */
	overflow: clip;
}

/*
 * Die Fakten liegen fest hinter dem Bild und bewegen sich nicht. Nur das Bild
 * fährt nach oben aus der Bühne und gibt sie dabei frei — wie eine Jalousie,
 * die aufgezogen wird. Weil das Bild nach oben geht, wird von unten nach oben
 * freigelegt: die untere Reihe erscheint zuerst.
 */
/*
 * Die Breite kommt aus `wideSize` in theme.json und ist dort flüssig — sie
 * stand einmal auf festen 1280 Pixeln, und genau das war der Fehler: Auf einem
 * 14-Zoll-Fenster sind 1280 noch 85 Prozent der Breite, auf einem 16-Zoll nur
 * noch 62. Das Bild wirkte dort verloren.
 *
 * Der Riegel hier fängt den umgekehrten Fall ab: ein breites, aber flaches
 * Fenster. Dort würde das Bild höher als die Bühne und oben wie unten
 * angeschnitten. `--ld-wandel-quer` ist das Seitenverhältnis der Aufnahme und
 * kommt aus dem Skript — fest hineinschreiben ließe es sich nicht, weil ein
 * neues Bildpaar ein anderes Verhältnis haben kann.
 */
.ld-projekt__wandel-lauf {
	position: relative;
	width: 100%;
	max-width: calc(var(--ld-wandel-quer, 1.78) * (100svh - 4rem));
	margin-inline: auto;
}


.ld-projekt__wandel-bilder {
	position: relative;
	z-index: 2;
	display: flex;
	flex-direction: column;
	justify-content: center;
	width: 100%;
}

.ld-projekt__wandel.is-bereit .ld-projekt__wandel-bilder {
	height: 100svh;
	/*
	 * --ld-hub kommt aus dem Skript: der gemessene Weg von der Unterkante des
	 * Renderings bis zum Band. In reiner CSS-Rechnung ginge das nicht, weil die
	 * Bildhöhe am Seitenverhältnis des jeweiligen Bildes hängt.
	 */
	transform: translateY(calc(var(--ld-schub, 0) * -1 * var(--ld-hub, 100svh)));
	will-change: transform;
	/*
	 * Der Kasten ist bildschirmhoch, das Bild darin nicht — es sitzt mittig,
	 * also bleibt über und unter ihm kalkweiße Fläche stehen. Die untere davon
	 * ist schädlich: Die Unterkante der Jalousie ist die Linie, an der
	 * freigegeben wird, und sie läge um diesen Totraum zu tief. Der Streifen
	 * Fakten direkt unter dem Bild bliebe damit über die ganze Fahrt verdeckt —
	 * am Ende genau die erste Faktenreihe.
	 *
	 * Sichtbar wird das erst bei hohen Fenstern: Der Totraum ist die halbe
	 * Differenz von Fenster- und Bildhöhe und wächst mit dem Fenster. Ab rund
	 * 1038 Pixeln Fensterhöhe verschluckt er die erste Reihe. Auf dem
	 * 14-Zoll-Rechner blieb es deshalb lange unbemerkt, auf dem 16-Zoll fiel es
	 * sofort auf.
	 *
	 * --ld-blende kommt aus dem Skript, weil die Bildhöhe am Seitenverhältnis
	 * des eingesetzten Bildes hängt. Oben wird nicht beschnitten: Dort deckt die
	 * Fläche nur den leeren Innenabstand der Faktenebene ab.
	 */
	clip-path: inset(0 0 var(--ld-blende, 0px) 0);
}

.ld-projekt__wandel.is-bereit .ld-projekt__wandel-buehne {
	position: sticky;
	inset-block-start: 0;
	height: 100svh;
}

/*
 * Der Abschied: Am Ende des Schachts hängt alles auf der Bühne zurück, während
 * der braune Abschnitt darunter mit voller Scrollgeschwindigkeit hochkommt und
 * darüberläuft. Die Bühne selbst bleibt stehen — sie ist nur der Rahmen, der
 * abschneidet. Erklärung und Maß stehen bei --ld-abschied-weg in base.css.
 *
 * translate statt transform, weil die Bildebene darin ihr eigenes transform
 * für den Hochschub braucht; die beiden Angaben stören einander nicht.
 */
.ld-projekt__wandel.is-bereit .ld-projekt__wandel-lauf {
	translate: 0 calc(var(--ld-abschied, 0) * var(--ld-abschied-weg));
}

.ld-projekt__wandel-bild {
	margin: 0;
	width: 100%;
	/* Der Rahmen bleibt fest, das Bild darin wird beschnitten statt größer. */
	overflow: clip;
}

.ld-projekt__wandel-bild img {
	display: block;
	width: 100%;
	height: auto;
}

/* Die Zeichnung gibt die Höhe vor, das Rendering liegt deckungsgleich darauf. */
.ld-projekt__wandel-bild--rendering {
	position: absolute;
	inset-block-start: 50%;
	inset-inline: 0;
	transform: translateY(-50%);
}

/* ------------------------------------------------------------ Zoom */

/*
 * --ld-wandel-zoom läuft von 1 auf 0. Beide Bilder bekommen denselben Wert,
 * damit sie deckungsgleich bleiben.
 */
.ld-projekt__wandel.is-bereit .ld-projekt__wandel-bild img {
	transform: scale(calc(1 + var(--ld-wandel-zoom, 0) * 0.04));
	transform-origin: center center;
	will-change: transform;
}

/* ---------------------------------------------------------- Kanten */

/* Die Zeichnung kommt von oben herunter. */
.ld-projekt__wandel.is-bereit .ld-projekt__wandel-bild--entwurf {
	-webkit-mask-image: linear-gradient(
		to bottom,
		#000 calc(var(--ld-wandel-entwurf, 0) * 128% - 28%),
		transparent calc(var(--ld-wandel-entwurf, 0) * 128%)
	);
	mask-image: linear-gradient(
		to bottom,
		#000 calc(var(--ld-wandel-entwurf, 0) * 128% - 28%),
		transparent calc(var(--ld-wandel-entwurf, 0) * 128%)
	);
}

/* Das Rendering kommt von unten herauf. */
.ld-projekt__wandel.is-bereit .ld-projekt__wandel-bild--rendering {
	-webkit-mask-image: linear-gradient(
		to top,
		#000 calc(var(--ld-wandel-rendering, 0) * 128% - 28%),
		transparent calc(var(--ld-wandel-rendering, 0) * 128%)
	);
	mask-image: linear-gradient(
		to top,
		#000 calc(var(--ld-wandel-rendering, 0) * 128% - 28%),
		transparent calc(var(--ld-wandel-rendering, 0) * 128%)
	);
}

/*
 * **Der Block für schmale Geräte steht ganz am Schluss.** Eine Medienabfrage
 * erhöht die Gewichtung nicht — bei gleichem Gewicht gewinnt die spätere
 * Regel, und „später" heißt schlicht: weiter unten in der Datei.
 */
@media ( max-width: 767px ) {
	.ld-projekt__wandel.is-bereit {
		height: 260svh;
	}

	.ld-projekt__wandel {
		--ld-band: 20svh;
		--ld-fakten-luft: 7svh;
	}

	/*
	 * **Telefon, vom Kunden festgelegt (27.10.2026).** Für diesen Abschnitt
	 * gibt es bei Arbor kein Gegenstück — der Zoom durch die Buchstaben ist
	 * unsere eigene Sequenz. Deshalb bleibt er mittig wie am Schreibtisch;
	 * nur der Grad der Aussage kommt von Arbors großer Aussage auf dem
	 * Telefon („A New Chapter"): 46 Bildpunkte, Zeilenhöhe 1,1.
	 *
	 * Der Absatz bleibt zentriert, hält aber Abstand zu den Rändern (rund
	 * 43 Bildpunkte je Seite statt 23), und zwischen Aussage und Absatz
	 * liegt mehr Luft. `padding` statt `margin`: Das Layout `constrained`
	 * setzt die seitlichen Ränder seiner Kinder mit `!important` auf auto.
	 */
	.ld-projekt .ld-projekt__inhalt {
		padding-inline: 1.4375rem;
	}

	.ld-projekt .ld-projekt__titel {
		width: 100%;
		max-width: none;
		font-size: 2.88rem;
		line-height: 1.1;
	}

	.ld-projekt .ld-projekt__einleitung {
		box-sizing: border-box;
		width: 100%;
		max-width: none;
		margin-block-start: 2.75rem;
		padding-inline: 1.25rem;
		font-size: 0.96rem;
		line-height: 1.5;
	}
}
