/* Bilderwolke
   Millimeterpapier, darauf die Bilder — frei treibend oder in einer Spalte —,
   unten die grüne Box, darüber bei Bedarf die grüne Bubble. Verhalten in
   assets/js/wolke.js.

   Raster wie auf einer Schreibmaschine: Jedes Zeichen ist 10px breit, jede
   Zeile 20px hoch (--row). Maße stehen in px, nicht in ch — siehe --row.
   Fein liegt alle 10px eine Linie, kräftig alle 100px. */

@font-face {
	font-family: "OCR B";
	/* Eigene Fassung mit gleicher Ober- und Unterlänge (800/800) und ohne
	   Zeilenabstand: Die Grundlinie liegt genau in der Mitte jeder Zeile, also
	   auf einer Rasterlinie. In der Datei statt per ascent-override, weil
	   Safari das nicht kennt. Für alles auf dem Millimeterpapier. */
	src: url("../fonts/ocrb-raster.woff2") format("woff2");
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "Univers Raster";
	/* Univers LT Std 55 Roman mit gleicher Ober- und Unterlänge (800/800)
	   und ohne Zeilenabstand: Die Grundlinie liegt genau in der Mitte jeder
	   Zeile, also auf einer Rasterlinie. Die Versalhöhe ist 0,724em — bei
	   der Schriftgröße der Seite gut 10px, Großbuchstaben füllen das
	   Kästchen über der Linie. In der Datei statt per ascent-override, weil
	   Safari das nicht kennt.
	   ACHTUNG: Kommerzielle Schrift (Linotype/Monotype). Vor dem Livegang
	   braucht es eine Web-Lizenz. */
	src: url("../fonts/univers-55-raster.woff2") format("woff2");
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}

:root {
	/* Auf dem Millimeterpapier: OCR B. In den grünen Flächen (Box,
	   Bubble): Univers, sonst Helvetica oder Arial. */
	--schrift: "OCR B", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
	--schrift-gruen: "Univers Raster", "Univers LT Std", Helvetica, Arial, sans-serif;
	--gruen: rgb(110 245 150 / 0.82);
	--linie: rgb(0 0 0 / 0.04);
	--linie-kraeftig: rgb(0 0 0 / 0.08);

	/* Unregistriert, also erst am Element ausgerechnet: ch bezieht sich auf
	   dessen Schrift. */
	--row: 20px;

	/* Bildspalte auf dem Telefon, auf volle Zeichen abgerundet, damit die
	   Textspalte auf einer Rasterlinie beginnt. */
	--spalte: round(down, min(420px, 46vw), 10px);

	/* Bildhöhe in der Liste, zugeklappt: 19 Kästchen, dazu eines Abstand
	   (--abstand) — jedes Projekt beginnt so auf einer kräftigen Linie. */
	--bild-klein: 190px;
	--abstand: 10px;
	/* Breite der Bildspalte. Auf dem Desktop die linke Hälfte. */
	--platz: var(--spalte);
}

*,
*::before,
*::after {
	box-sizing: border-box;
}

html {
	/* Kein Scroll-Ankern des Browsers: Beim Auf- und Zuklappen führt das
	   Skript die Lage des Projekts selbst (rollenZu). */
	overflow-anchor: none;
	/* OCR B ist 0,723em breit: So wird jedes Zeichen genau 10px breit. Die
	   Univers in den grünen Flächen hat bei dieser Größe eine Versalhöhe
	   von 10px (0,724em) — beide Schriften passen ins selbe Raster. */
	font-size: calc(10px / 0.723);
	-webkit-text-size-adjust: 100%;
}

body {
	margin: 0;
	min-height: 100dvh;
	color: #000;
	font-family: var(--schrift);
	font-size: 1rem;
	line-height: var(--row);
	font-variant-numeric: tabular-nums;
	background-color: #fff;
	/* Gleitende Bilder dürfen seitlich hinausragen, ohne dass die Seite
	   waagerecht rollt. */
	overflow-x: clip;
}

a {
	color: inherit;
	text-decoration: none;
}

a:focus-visible,
a[aria-current] {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

/* Hover nur mit echter Maus — auf Touch bliebe es nach einem Tipp kleben. */
@media (hover: hover) {
	a:hover {
		text-decoration: underline;
		text-underline-offset: 0.2em;
	}
}

h1,
h2 {
	font: inherit;
	margin: 0;
}

img {
	max-width: 100%;
	height: auto;
}

.visually-hidden,
.skip-link:not(:focus) {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

.skip-link:focus {
	position: fixed;
	z-index: 20;
	top: var(--row);
	left: 20px;
	padding: 0 10px;
	background: #fff;
}

/* --- Millimeterpapier ----------------------------------------------------
   Eine eigene Ebene unter allem, so hoch wie die Seite. In der Wolke liegt
   sie unterhalb des Fensters; in der Liste gleitet sie von unten herein
   (assets/js/wolke.js setzt --papier-y und --papier-clip). Der Rahmen
   schneidet ab, was dabei unter die Seite ragt — sonst entstünde dort
   Platz zum Rollen.
   ------------------------------------------------------------------------ */

body {
	/* Bezugsrahmen fürs Papier: so hoch wie die Seite. */
	position: relative;
}

.papier-rahmen {
	position: absolute;
	inset: 0;
	overflow: clip;
	pointer-events: none;
}

.papier-rahmen {
	z-index: -1;
}

.papier {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	transform: translateY(var(--papier-y, 0px));
	/* Beim Hereingleiten beginnt das Papier am oberen Fensterrand, nicht am
	   Anfang der Seite: --papier-clip ist dann die Scrollposition. */
	clip-path: inset(var(--papier-clip, 0px) 0 0 0);
}

.papier-gleitet .papier {
	transition: transform 0.9s cubic-bezier(.6, .05, .25, 1);
}

.papier {
	background-image:
		linear-gradient(to right, var(--linie-kraeftig) 1px, transparent 1px),
		linear-gradient(to bottom, var(--linie-kraeftig) 1px, transparent 1px),
		linear-gradient(to right, var(--linie) 1px, transparent 1px),
		linear-gradient(to bottom, var(--linie) 1px, transparent 1px);
	background-size:
		100px calc(5 * var(--row)),
		100px calc(5 * var(--row)),
		10px calc(var(--row) / 2),
		10px calc(var(--row) / 2);
}

/* --- Wolke und Liste ----------------------------------------------------- */

.wolke {
	list-style: none;
	margin: 0;
	padding: 0;
}

.wolke-bild {
	display: block;
}

.wolke-bild img,
.wolke-mehr img {
	display: block;
	width: 100%;
	height: auto;
}

/* Die Wolke: Das Fenster ist die Bühne. Lage, Größe und Drehung setzt das
   Skript. Zu sehen ist nur das erste Bild. */
.wolke[data-modus="wolke"] {
	position: fixed;
	inset: 0;
	overflow: hidden;
}

.wolke[data-modus="wolke"] .wolke-item {
	position: absolute;
	top: 0;
	left: 0;
	transform-origin: center;
	will-change: transform;
}

.wolke[data-modus="wolke"] .wolke-bild img {
	box-shadow: 0 1px 4px rgb(0 0 0 / 0.18);
}

/* Anfassen und verschieben (assets/js/wolke.js). Kein Scrollen und kein
   Bild-Ziehen des Browsers dazwischen. */
.wolke[data-modus="wolke"] .wolke-item {
	cursor: grab;
	touch-action: none;
	user-select: none;
	-webkit-user-select: none;
}

.wolke[data-modus="wolke"] .wolke-item.is-gezogen {
	cursor: grabbing;
}

.wolke[data-modus="wolke"] a,
.wolke[data-modus="wolke"] img {
	-webkit-user-drag: none;
}

.wolke[data-modus="wolke"] .wolke-text,
.wolke[data-modus="wolke"] .wolke-mehr {
	display: none;
}

/* Ohne Bild: schwarzes Hochformat, darin noch einmal der gekürzte Titel.
   Steht nur in der Liste, nie in der Wolke. */
.wolke-platzhalter {
	/* Der Titel in der schwarzen Fläche in Univers, wie in den grünen
	   Flächen. */
	font-family: var(--schrift-gruen);
	display: block;
	width: 100%;
	height: 100%;
	padding: 10px;
	overflow: hidden;
	background: #000;
	color: #fff;
	hyphens: auto;
	overflow-wrap: break-word;
}

/* Nicht in der Auswahl für die Wolke (75 zufällige, assets/js/wolke.js):
   ausgeblendet, ihre Bilder laden erst in der Liste. */
.wolke[data-modus="wolke"] .wolke-item.ausser-wolke {
	display: none;
}

/* Die Liste: Bild links, Jahr und Titel daneben. Aufgeklappt stehen die
   Metainfos unter dem Titel und weitere Bilder unter dem ersten. Alle Maße
   in Zeichen und Zeilen. Unten so viel Luft, dass auch die letzte Gruppe
   noch nach oben gerollt werden kann. */
.wolke[data-modus="liste"] {
	--bildhoehe: var(--bild-klein);
	display: grid;
	gap: var(--abstand);
	padding: calc(5 * var(--row)) 20px 0;
}

.wolke[data-modus="liste"] .wolke-item {
	display: grid;
	grid-template-columns: var(--spalte) minmax(0, 1fr);
	column-gap: 10px;
	/* Kein Zeilenabstand: Die Textspalte reicht über zwei Zeilen, ein
	   Abstand käme sonst auch bei zugeklappten Projekten dazu. Den Abstand
	   zu weiteren Bildern trägt .wolke-mehr selbst. */
	row-gap: 0;
	align-items: start;
	/* Mindesthöhe (aufgerundet aufs Raster) bleibt unten frei, statt sich
	   auf die Zeilen zu verteilen. */
	align-content: start;
}

/* Höhe: --bildhoehe, außer das Bild würde damit breiter als seine Spalte
   (--platz). Dann die größte Stufe, bei der es hineinpasst — in denselben
   Schritten wie oben (9, 19, 29 … Kästchen), damit das nächste Projekt
   weiter auf einer kräftigen Linie beginnt. Breite abgerundet auf volle
   Kästchen: Alle vier Kanten liegen auf Rasterlinien, und das Bild füllt
   den Rahmen mit weniger als einem Kästchen Verschnitt. */
.wolke[data-modus="liste"] {
	/* Für --platz auf dem Desktop: cqw misst die Breite der Liste. */
	container-type: inline-size;
}

.wolke[data-modus="liste"] .wolke-bild {
	/* Passt nicht einmal die kleinste Stufe (9 Kästchen), darf ein sehr
	   breites Bild auch dazwischen landen — lieber aus dem Takt als
	   beschnitten. */
	--passt: max(
		calc(round(down, calc(var(--platz) / var(--format, 1) + var(--abstand)), 100px) - var(--abstand)),
		min(80px, round(down, calc(var(--platz) / var(--format, 1)), 10px))
	);
	--hoehe: min(var(--bildhoehe), var(--passt));

	grid-column: 1;
	/* Auf das nächste Kästchen gerundet, nicht immer ab: Bei schmalen
	   Hochformaten wäre ein abgeschnittenes Kästchen viel. */
	width: min(round(nearest, calc(var(--hoehe) * var(--format, 1)), 10px), round(down, 100%, 10px));
	height: var(--hoehe);
	transform-origin: center;
}

.wolke[data-modus="liste"] .wolke-bild img {
	height: 100%;
	object-fit: cover;
}

.wolke[data-modus="liste"] .wolke-item.is-offen {
	/* Auf dem Telefon: so breit wie die Spalte, höchstens zwei Drittel des
	   Fensters hoch, abgerundet auf volle Zeilen. */
	--bildhoehe: round(down, min(65vh, calc(var(--spalte) / var(--format, 1))), 10px);
}

.wolke[data-modus="liste"] .wolke-text {
	grid-column: 2;
	grid-row: 1 / span 2;
	hyphens: auto;
	animation: einblenden 0.5s 0.5s both;
}

.wolke-kopf {
	display: grid;
}

/* Gruppe über dem Jahr: „Works“, „Architecture“. Leer, wenn nach
   Jahrzehnten sortiert ist — dann fällt die Zeile weg. */
.wolke-gruppe:empty {
	display: none;
}

.wolke-kopf:hover,
.wolke-kopf:focus-visible {
	text-decoration: none;
}

.wolke-kopf:focus-visible .wolke-titel,
.is-offen .wolke-titel {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

@media (hover: hover) {
	.wolke-kopf:hover .wolke-titel {
		text-decoration: underline;
		text-underline-offset: 0.2em;
	}
}

.wolke-mehr {
	grid-column: 1;
	display: grid;
	gap: var(--row);
	margin-top: var(--row);
}

.wolke[data-modus="liste"] .wolke-mehr img {
	width: auto;
	max-width: 100%;
	height: var(--bildhoehe);
	object-fit: contain;
}

.wolke-item:not(.is-offen) .wolke-angaben,
.wolke-item:not(.is-offen) .wolke-mehr,
.wolke-mehr:empty {
	display: none;
}

.wolke-item.is-offen .wolke-angaben {
	margin-top: var(--row);
	animation: einblenden 0.3s both;
}

@keyframes einblenden {
	from {
		opacity: 0;
	}
}

/* Metainfos tabellarisch: Bezeichnung, dann der Wert an einer gemeinsamen
   Kante zehn Kästchen weiter — Platz für „Kategorie“ und ein Leerzeichen.
   Längere Bezeichnungen („Ausgestellt in“) schieben nur ihren eigenen Wert
   weiter, ein Kästchen Abstand bleibt. */
.angaben {
	margin: 0;
	max-width: 600px;
}

.angaben div {
	display: flex;
	align-items: baseline;
}

.angaben dt {
	flex: none;
	min-width: 100px;
	padding-right: 10px;
}

.angaben dd {
	flex: 1 1 0;
	min-width: 0;
	margin: 0;
	overflow-wrap: break-word;
	hyphens: auto;
}

.angaben a {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

/* --- Unten: Bubble über der Box ------------------------------------------ */

.wolke-unten {
	position: fixed;
	z-index: 10;
	left: 50%;
	bottom: max(var(--row), env(safe-area-inset-bottom));
	transform: translateX(-50%);
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--row);
	width: max-content;
	max-width: calc(100vw - 40px);
	/* Die Bubble wächst, bis sie oben anstößt — dann rollt sie in sich. */
	max-height: calc(100dvh - 2 * var(--row));
}

.wolke-box,
.wolke-bubble {
	font-family: var(--schrift-gruen);
	border-radius: 25px;
	background: var(--gruen);
	-webkit-backdrop-filter: blur(14px) saturate(1.4);
	backdrop-filter: blur(14px) saturate(1.4);
}

.wolke-bubble {
	position: relative;
	flex: 0 1 auto;
	min-height: 0;
	width: min(600px, calc(100vw - 40px));
	display: flex;
	flex-direction: column;
	animation: bubble 0.3s cubic-bezier(.3, 1.4, .5, 1) both;
}

.wolke-bubble[hidden] {
	display: none;
}

.wolke-bubble.is-laedt {
	opacity: 0.6;
}

@keyframes bubble {
	from {
		opacity: 0;
		transform: translateY(var(--row)) scale(0.96);
	}
}

.wolke-bubble-inhalt {
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: var(--row) 30px;
}

.wolke-bubble-zu {
	/* Schließen: ein × im Kreis oben rechts. Gleich weit vom oberen und
	   rechten Rand, mittig auf der Höhe der Titelzeile (die erste Zeile
	   liegt 20–40px unter der Kante, ihre Mitte bei 30px). */
	position: absolute;
	z-index: 1;
	top: 16px;
	right: 16px;
	display: grid;
	place-items: center;
	width: 28px;
	height: 28px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: none;
	box-shadow: inset 0 0 0 1px currentColor;
	color: inherit;
	cursor: pointer;
	transition: background-color 0.15s, color 0.15s;
}

.wolke-bubble-zu svg {
	fill: none;
	stroke: currentColor;
	stroke-width: 1.5;
	stroke-linecap: round;
}

@media (hover: hover) {
	.wolke-bubble-zu:hover {
		background: #000;
		color: #fff;
		box-shadow: none;
	}
}

.bubble-titel {
	margin-bottom: var(--row);
	/* Platz für den Schließen-Kreis. */
	padding-right: 40px;
}

.bubble-text a {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.bubble-text p,
.bubble-text h2,
.bubble-text ul,
.bubble-text ol {
	margin: 0 0 var(--row);
}

.bubble-text > :last-child {
	margin-bottom: 0;
}

.bubble-liste {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: var(--row);
}

.bubble-liste li {
	display: grid;
	grid-template-columns: 40px minmax(0, 1fr);
	gap: 20px;
}

.bubble-klein {
	color: rgb(0 0 0 / 0.6);
}

/* News: neueste zuerst, Datum über dem Titel, verknüpfte Projekte darunter. */
.news-liste,
.news-projekte {
	list-style: none;
	margin: 0;
	padding: 0;
}

.news + .news {
	margin-top: calc(2 * var(--row));
}

.news-datum,
.news-titel {
	margin: 0 !important;
}

.news-text {
	margin-top: 10px;
}

.news-text a,
.news-projekte a {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.news-art {
	margin-left: 10px;
}

/* --- Die grüne Box ------------------------------------------------------- */

.wolke-box {
	flex: none;
	max-width: 100%;
	padding: var(--row) 30px;
	text-align: center;
}

.wolke-box-zeile {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: 6px 20px;
	margin: 0;
	min-height: calc(2 * var(--row));
}

/* Drei Ebenen: oben der Name (führt zurück in die Wolke), in der Mitte die
   Filter als Bubbles, unten die Seiten als schlichte Links, etwas
   abgesetzt. */
.wolke-box-filter {
	gap: 6px 10px;
}

.wolke-box-seiten {
	margin-top: 10px;
}

.wolke-box button {
	padding: 0;
	border: 0;
	background: none;
	color: inherit;
	font: inherit;
	line-height: inherit;
	cursor: pointer;
}

/* Ansichten als Symbole oben rechts in der Box: Liste, Kacheln, Galerie.
   Das aktive schwarz hinterlegt. */
.wolke-box {
	position: relative;
}

.wolke-ansichten {
	position: absolute;
	top: 10px;
	right: 14px;
	display: flex;
	gap: 2px;
}

.wolke-box .wolke-ansicht {
	display: grid;
	place-items: center;
	width: 28px;
	height: 28px;
	border-radius: 6px;
}

.wolke-ansicht svg {
	fill: none;
	stroke: currentColor;
	stroke-width: 1.2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.wolke-box .wolke-ansicht[aria-pressed="true"] {
	background: #000;
	color: #fff;
}

@media (hover: hover) {
	.wolke-box .wolke-ansicht:not([aria-pressed="true"]):hover {
		background: rgb(0 0 0 / 0.12);
	}
}

/* Filter-Bubbles: feiner schwarzer Rahmen. Aktiv schwarz mit weißer
   Schrift. Ausgegraut, wenn die Kombination mit der übrigen Auswahl kein
   Projekt hätte (assets/js/wolke.js, markieren). */
.wolke-box .wolke-filter {
	/* Die Schrift hat ihre Grundlinie in der Zeilenmitte (fürs Raster), die
	   Buchstaben stehen also in der oberen Hälfte. Oben mehr Innenabstand
	   als unten, damit sie mittig sitzen — gemittelt zwischen Versal- und
	   x-Höhe der Univers. */
	/* Links und rechts 1rem. Bräche eine Zeile damit um, setzt das Skript
	   .is-eng an die Zeile (boxLayout), dann wird es enger. */
	padding: 0.62em 1rem 0.02em;
	border-radius: 999px;
	box-shadow: inset 0 0 0 1px #000;
	line-height: 1em;
	transition: background-color 0.15s, color 0.15s, opacity 0.15s;
}

.wolke-box-filter.is-eng .wolke-filter {
	padding-inline: var(--innen, 0.9ch);
}

.wolke-box .wolke-filter[aria-pressed="true"] {
	background: #000;
	color: #fff;
}

.wolke-box .wolke-filter:disabled {
	opacity: 0.3;
	cursor: default;
}

/* Die Gruppe des Eintrags, der gerade oben steht, bekommt einen etwas
   kräftigeren Rahmen statt einer Unterstreichung. Er wandert beim
   Scrollen mit (assets/js/wolke.js, spaehen). */
.wolke-box .wolke-filter.is-hier {
	box-shadow: inset 0 0 0 2px #000;
}

/* Hover nur mit echter Maus: Auf dem iPhone bliebe er nach einem Tipp
   sonst kleben und sähe aus wie ein aktiver Filter. */
@media (hover: hover) {
	.wolke-box .wolke-filter:not(:disabled):not([aria-pressed="true"]):hover {
		background: rgb(0 0 0 / 0.12);
	}

	.wolke-box .wolke-name:hover {
		text-decoration: underline;
		text-underline-offset: 0.2em;
	}
}

/* --- Footer ----------------------------------------------------------------
   Anschrift, Kontakt, Steuer, Rechtliches. Steht am Ende der Seite; darunter
   nur so viel Platz, dass die grüne Box ihn nicht verdeckt (--box setzt das
   Skript). In der Wolke gibt es ihn nicht. Auf dem Desktop beginnt er an der
   Achse, wie der Text der Liste.
   ------------------------------------------------------------------------ */
body {
	display: flex;
	flex-direction: column;
}

#main {
	flex: 1 0 auto;
}

.fuss {
	display: flex;
	flex-wrap: wrap;
	gap: var(--row) 40px;
	padding: calc(5 * var(--row)) 20px calc(var(--box, 0px) + 2 * var(--row));
}

.fuss p {
	margin: 0;
}

.fuss a {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

body[data-papier="weg"] .fuss {
	display: none;
}

/* Galerie: Der Footer beginnt erst unterhalb des Fensters. */
.wolke[data-modus="liste"][data-ansicht="galerie"] {
	min-height: 100dvh;
}

@media (min-width: 50rem) {
	.fuss {
		padding-left: 50%;
	}
}

@media (max-width: 40rem) {
	/* Die längste Zeile („Works Exhibitions Curations Publications“) passt
	   auch auf dem Telefon in eine Zeile: Die Box verlässt dafür das Raster
	   und nimmt die Schrift an der Breite. Sie schwebt ohnehin darüber. */
	/* Box und Bubble fast über die ganze Breite, 2rem Abstand zum Rand. */
	.wolke-unten {
		width: calc(100vw - 4rem);
		max-width: none;
	}

	.wolke-box,
	.wolke-bubble {
		width: 100%;
	}

	.wolke-box {
		font-size: min(1rem, 3.2vw);
		padding: var(--row) 2ch;
	}

	.wolke-box-zeile {
		gap: 4px 1.5ch;
		min-height: calc(1.5 * var(--row));
	}

	.wolke-box-filter {
		gap: 4px 0.6ch;
	}

	.wolke-box-filter.is-eng .wolke-filter {
		padding-inline: var(--innen, 0.6ch);
	}

	.wolke-bubble-inhalt {
		padding: var(--row) 2ch;
	}
}

/* --- Desktop: Liste an der Mittelachse ----------------------------------
   Links die Bilder, rechtsbündig an der Achse; rechts Jahr, Titel und
   Metainfos, linksbündig. Die Achse liegt auf einer Rasterlinie: Das Papier
   wird dafür mittig ausgerichtet. Bild und Text halten je ein Zeichen
   Abstand zur Achse. */
@media (min-width: 50rem) {
	.papier {
		/* Prozentwerte richten die Mitte der Kachel an der Mitte aus; eine
		   halbe Kachel dazu, dann liegt eine Linie genau auf der Achse. */
		background-position:
			calc(50% + 50px) 0,
			calc(50% + 50px) 0,
			calc(50% + 5px) 0,
			calc(50% + 5px) 0;
	}

	.wolke[data-modus="liste"] {
		padding-inline: 0;
	}

	.wolke[data-modus="liste"] {
		--platz: calc(50cqw - 10px);
	}

	.wolke[data-modus="liste"] .wolke-item {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
		column-gap: 20px;
	}

	.wolke[data-modus="liste"] .wolke-bild,
	.wolke[data-modus="liste"] .wolke-mehr img {
		justify-self: end;
	}

	.wolke[data-modus="liste"] .wolke-item.is-offen {
		/* Aufgeklappt 29, 39, 49 oder 59 Kästchen hoch — so viel, wie in gut
		   zwei Drittel des Fensters passt. Mit dem Kästchen Abstand darunter
		   beginnt der nächste Eintrag direkt auf der nächsten kräftigen Linie,
		   genau wie bei den kleinen (19 + 1). Ist der Text länger, rundet das
		   Skript auf volle Hunderter auf (rasterHoehe). */
		--bildhoehe: calc(clamp(300px, round(down, 65vh, 100px), 600px) - var(--abstand));
	}

	.wolke[data-modus="liste"] .wolke-mehr {
		justify-items: end;
	}

	/* Der Text beginnt direkt auf der Achse, das Bild endet ein Kästchen
	   davor. */
	.wolke[data-modus="liste"] .wolke-text {
		margin-left: -10px;
	}
}

/* --- Telefon: Liste ---------------------------------------------------------
   Zugeklappt: Bild rechtsbündig an der Textspalte, jedes Projekt 19 Kästchen
   hoch plus eines Abstand — auch wenn das Bild flacher ist. Aufgeklappt
   einspaltig: das Bild über die ganze Breite, darunter Jahr, Titel und
   Metainfos.
   ------------------------------------------------------------------------ */
@media not (min-width: 50rem) {
	.wolke[data-modus="liste"] .wolke-bild {
		justify-self: end;
		/* Zugeklappt so hoch, wie die Spalte es zulässt, in Kästchen — nicht
		   in den Stufen 9/19 wie auf dem Desktop. Höchstens 19 Kästchen
		   (--bildhoehe); das Projekt hält ohnehin 19 frei. */
		--passt: round(down, calc(var(--platz) / var(--format, 1)), 10px);
	}

	.wolke[data-modus="liste"] .wolke-item {
		min-height: var(--bild-klein);
	}

	.wolke[data-modus="liste"] .wolke-item.is-offen {
		grid-template-columns: minmax(0, 1fr);
		/* Volle Breite der Liste (cqw: die Liste ist der Container), höchstens
		   gut vier Fünftel des Fensters hoch, abgerundet auf volle
		   Kästchen. */
		--bildhoehe: round(down, min(85vh, calc(100cqw / var(--format, 1))), 10px);
	}

	.wolke[data-modus="liste"] .wolke-item.is-offen .wolke-bild {
		--hoehe: var(--bildhoehe);
		grid-column: 1;
		/* Volle Breite — außer ein hohes Bild stößt an die Höhengrenze, dann
		   wird es schmaler statt beschnitten. Es steht dann links, bündig mit
		   dem Text darunter. */
		justify-self: start;
		width: min(100%, round(nearest, calc(var(--hoehe) * var(--format, 1)), 10px));
	}

	.wolke[data-modus="liste"] .wolke-item.is-offen .wolke-text {
		grid-column: 1;
		grid-row: 2;
		margin-top: var(--row);
	}

	.wolke[data-modus="liste"] .wolke-item.is-offen .wolke-mehr {
		grid-row: 3;
	}
}

/* --- Ansicht: Kacheln -------------------------------------------------------
   Gleich große Felder, 19 Kästchen breit und hoch, das Bild darin unten
   links; darunter Jahr und Titel. Ein Kästchen Abstand zwischen den Spalten:
   Feld plus Abstand sind zwei kräftige Kästchen. Auf dem Desktop mittig an
   der Achse, darum der halbe Abstand links.
   ------------------------------------------------------------------------ */

.wolke[data-modus="liste"][data-ansicht="kacheln"] {
	--kachel: 190px;
	grid-template-columns: repeat(auto-fill, var(--kachel));
	justify-content: center;
	column-gap: 10px;
	row-gap: var(--row);
	padding: calc(5 * var(--row)) 0 0 10px;
}

.wolke[data-modus="liste"][data-ansicht="kacheln"] .wolke-item {
	display: block;
	min-height: 0;
}

.wolke[data-modus="liste"][data-ansicht="kacheln"] .wolke-bild {
	width: 100%;
	height: var(--kachel);
}

.wolke[data-modus="liste"][data-ansicht="kacheln"] .wolke-bild img {
	width: 100%;
	height: 100%;
	object-fit: contain;
	object-position: left bottom;
}

.wolke[data-modus="liste"][data-ansicht="kacheln"] .wolke-platzhalter {
	width: 130px;
}

.wolke[data-modus="liste"][data-ansicht="kacheln"] .wolke-text {
	margin: 10px 0 0;
}

/* Titel höchstens drei Zeilen, damit die Reihen nicht ausfransen. */
.wolke[data-modus="liste"][data-ansicht="kacheln"] .wolke-titel {
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.wolke[data-modus="liste"][data-ansicht="kacheln"] .wolke-angaben,
.wolke[data-modus="liste"][data-ansicht="kacheln"] .wolke-mehr,
.wolke[data-modus="liste"][data-ansicht="galerie"] .wolke-angaben,
.wolke[data-modus="liste"][data-ansicht="galerie"] .wolke-mehr {
	display: none;
}

@media not (min-width: 50rem) {
	.wolke[data-modus="liste"][data-ansicht="kacheln"] {
		--kachel: 170px;
		justify-content: start;
		padding-left: 20px;
	}
}

/* --- Ansicht: Galerie -------------------------------------------------------
   Ein Projekt groß, mittig; darüber Jahr und Titel. Links und rechts die
   Pfeile, weiter auch mit den Pfeiltasten, Wischen oder einem Klick aufs
   Bild (assets/js/wolke.js).
   ------------------------------------------------------------------------ */

.wolke[data-modus="liste"][data-ansicht="galerie"] {
	padding: calc(2 * var(--row)) 70px 0;
}

.wolke[data-modus="liste"][data-ansicht="galerie"] .wolke-item {
	display: none;
}

.wolke[data-modus="liste"][data-ansicht="galerie"] .wolke-item.is-galerie {
	display: flex;
	flex-direction: column;
	align-items: center;
	min-height: 0;
	animation: einblenden 0.3s both;
}

/* Kopfzeile nur in der Galerie: Titel, Jahr, Kategorie als
   Abschnitte nebeneinander über dem Bild. Die Liste zeigt sie nicht. */
.wolke-galerie-kopf {
	display: none;
}

.wolke[data-modus="liste"][data-ansicht="galerie"] .is-galerie .wolke-text {
	display: none;
}

.wolke[data-modus="liste"][data-ansicht="galerie"] .is-galerie .wolke-galerie-kopf {
	order: -1;
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0 40px;
	max-width: min(100%, 1200px);
	margin: 0 0 var(--row);
}

.wolke-galerie-kopf > * {
	white-space: nowrap;
}

/* Der Titel darf umbrechen, die anderen Abschnitte nicht. */
.wolke-galerie-kopf .wolke-galerie-titel {
	white-space: normal;
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.wolke[data-modus="liste"][data-ansicht="galerie"] .is-galerie .wolke-bild {
	/* Fensterhöhe minus Box und was darüber steht: oben zwei Zeilen
	   Abstand, die Kopfzeile, eine Zeile Abstand (zusammen 80px); unten
	   der Abstand der Box zum Rand und zum Bild (40px). */
	--galerie-hoehe: calc(100dvh - var(--box, 0px) - 6 * var(--row));
	width: auto;
	height: auto;
	max-width: 100%;
	cursor: e-resize;
}

.wolke[data-modus="liste"][data-ansicht="galerie"] .is-galerie .wolke-bild img {
	width: auto;
	height: auto;
	max-width: 100%;
	max-height: max(200px, var(--galerie-hoehe));
	object-fit: contain;
}

.wolke[data-modus="liste"][data-ansicht="galerie"] .is-galerie .wolke-platzhalter {
	height: max(200px, var(--galerie-hoehe));
	width: auto;
	aspect-ratio: 0.7;
}

.galerie-nav[hidden] {
	display: none;
}

.galerie-pfeil {
	/* Nur der Pfeil, ohne Kreis. Die Fläche zum Antippen bleibt groß. */
	position: fixed;
	z-index: 9;
	top: calc(50% - var(--box, 0px) / 2);
	transform: translateY(-50%);
	width: 44px;
	height: 44px;
	padding: 0;
	border: 0;
	background: none;
	color: #000;
	font: inherit;
	font-size: 2rem;
	line-height: 44px;
	cursor: pointer;
}

.galerie-pfeil[data-schritt="-1"] {
	left: 10px;
}

.galerie-pfeil[data-schritt="1"] {
	right: 10px;
}

@media (hover: hover) {
	.galerie-pfeil {
		opacity: 0.6;
		transition: opacity 0.15s;
	}

	.galerie-pfeil:hover {
		opacity: 1;
	}
}

@media not (min-width: 50rem) {
	/* Telefon: das Bild über die ganze Breite. Die Pfeile liegen am Rand
	   darüber — weitergeblättert wird hier meist durch Wischen. */
	.wolke[data-modus="liste"][data-ansicht="galerie"] {
		padding-inline: 20px;
	}
}

@media (max-width: 40rem) {
	/* Telefon: Symbole etwas enger an den Rand, der Name rückt nicht drunter. */
	.wolke-ansichten {
		top: 6px;
		right: 8px;
	}

	.wolke-box .wolke-ansicht {
		width: 24px;
		height: 24px;
	}
}

/* --- Box: Aufbau je nach Platz ----------------------------------------------
   assets/js/wolke.js (boxLayout) probiert die Varianten der Reihe nach und
   nimmt die erste, in der nichts übersteht:
   eins     — alles in einer Zeile
   zwei     — Name, Seiten, Symbole; darunter die Bubbles in Gruppen
   drei     — Name und Symbole; Bubbles; Seiten
   standard — jede Gruppe in einer eigenen Zeile (wie bisher)
   ------------------------------------------------------------------------ */

.wolke-box-filtergruppen {
	display: contents;
}

.wolke-box:not([data-layout="standard"]) {
	display: grid;
	align-items: center;
	column-gap: 30px;
	padding-block: 10px;
}

.wolke-box:not([data-layout="standard"]) .wolke-ansichten {
	position: static;
	grid-area: symbole;
	justify-self: end;
}

.wolke-box:not([data-layout="standard"]) .wolke-box-filtergruppen {
	grid-area: filter;
	display: flex;
	justify-content: center;
	align-items: center;
	gap: 0 30px;
}

.wolke-box:not([data-layout="standard"]) .wolke-box-zeile {
	flex-wrap: nowrap;
	white-space: nowrap;
}

.wolke-box:not([data-layout="standard"]) .wolke-box-name {
	grid-area: name;
}

.wolke-box:not([data-layout="standard"]) .wolke-box-seiten {
	grid-area: seiten;
	margin: 0;
}

.wolke-box[data-layout="eins"] {
	grid-template-columns: auto auto auto auto;
	grid-template-areas: "name filter seiten symbole";
	/* An der Grundlinie statt an der Mitte: Der Text in den Bubbles steht
	   auf einer Linie mit dem Namen und den Seiten daneben. */
	align-items: baseline;
}

.wolke-box[data-layout="eins"] .wolke-box-filtergruppen,
.wolke-box[data-layout="eins"] .wolke-box-filter {
	align-items: baseline;
}

.wolke-box[data-layout="eins"] .wolke-ansichten {
	align-self: center;
}

/* Ohne Mindesthöhe der Zeilen: Die Zeile ist so hoch wie die Bubbles, und
   die sitzen mittig in der Box. */
.wolke-box[data-layout="eins"] .wolke-box-zeile {
	min-height: 0;
}

/* Die Symbole nicht höher als die Bubbles — sonst bestimmen sie die
   Zeilenhöhe, und die an der Grundlinie ausgerichteten Bubbles säßen oben. */
.wolke-box[data-layout="eins"] .wolke-ansicht {
	width: 24px;
	height: 24px;
}

.wolke-box[data-layout="eins"] {
	padding-block: 16px;
}

.wolke-box[data-layout="zwei"] {
	grid-template-columns: auto 1fr auto;
	grid-template-areas:
		"name seiten symbole"
		"filter filter filter";
}

.wolke-box[data-layout="drei"] {
	grid-template-columns: 1fr auto 1fr;
	grid-template-areas:
		". name symbole"
		"filter filter filter"
		"seiten seiten seiten";
}
