/* Typorama Site — shared page components (layered on ds/site.css) */

body { background: var(--tp-bg); margin: 0; }   /* 25.08.2026: s. ds/site.css */

/* image placeholder */
.ph {
  background: var(--tp-bg-soft); border: 1px solid var(--tp-border);
  display: flex; align-items: center; justify-content: center; position: relative;
  overflow: hidden;
}
.ph span {
  font-family: var(--tp-font-sans); font-size: var(--tp-t-label); text-transform: uppercase;
  /* ⚑⚑ `--tp-ink-muted` (#5f5d54), nicht #a9a69f — Percy-Entscheid 26.08.2026.
     Gemessen war der alte Ton 2,19 gegen den beigen Grund, bei 11 px: der schlechteste
     Kontrastwert der ganzen Website. Und anders als ein Platzhalter IN einem Feld ist
     «kein Bild erfasst» **echter Text**, der eine Auskunft gibt — er darf und soll
     gelesen werden. Die blassen Töne in den Eingabefeldern bleiben bewusst blass
     (Percy: ein zu dunkler Hinweis liest sich, als sei das Feld schon ausgefüllt). */
  letter-spacing: .18em; color: var(--tp-ink-muted); padding: 4px 10px; text-align: center;
}
.ph img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ph--contain img { object-fit: contain; background: var(--tp-white); }

/* page head */
.tps-crumb {
  font-family: var(--tp-font-sans); font-size: var(--tp-t-klein); letter-spacing: .08em;
  color: var(--tp-ink-muted); padding: 16px 56px 0;
}
.tps-crumb a { color: var(--tp-ink-muted); text-decoration: none; }
.tps-crumb b { color: var(--tp-ink); font-weight: 700; }
/* ⚑⚑ Die Brotkrume steht auf EINER Zeile, der Name am Ende kürzt sich mit «…» (Percy-Entscheid
   16.09.2026, Entscheidblatt «Rubriken nach dem Klammerfehler», Frage 4).
   Grund: im HTML steht ein Platzhaltername («Linotype Elektron», «Druckmaschine», «—»), den
   api.js durch den echten ersetzt. War der echte länger, brach die Brotkrume eine Zeile mehr
   um, und der ganze Inhalt darunter sprang — auf vorschau im echten Ladeverlauf gemessen:
   +23 px bei 375 px auf objekt.html?id=76 und maschine.html?id=202, bei 480 px auch auf
   maschine.html?id=31. Wie lang der Name wird, weiß vorher niemand; reservieren lässt es sich
   nicht. Auf einer Zeile ist die Höhe dagegen immer dieselbe. Der volle Name steht ohnehin als
   Titel direkt darunter, und Vorleseprogramme lesen ihn ganz — gekürzt wird nur das Bild.
   ⚑ Das Kürzen braucht `overflow: hidden`, und das schneidet sonst den gelben Fokusring der
     Links ab (3 px + 2 px Abstand, Hausordnung §3). Innenabstand und gleich großer negativer
     Außenabstand geben dem Ring 6 px Raum, ohne dass sich eine Zeile verschiebt.
   ⚑ `schrift.html` bekam dafür den `.tps-crumb__weg`, den objekt/maschine/schriften schon
     hatten: dort wird die Brotkrume ebenfalls aus der Schnittstelle gefüllt. */
/* ⚑ Geschnitten wird am ZEILENENDE — damit gibt zuerst der Name nach, der dort steht, und der Weg
     davor bleibt ganz. Eine Flex-Reihe mit `flex-shrink: 1000` für den Namen wurde am 16.09.2026
     probiert und verworfen: die Links schrumpften dabei um Bruchteile eines Pixels mit, und schon
     das löst das «…» aus — heraus kam «Sammlu… › Schrift… › Linoty… › Akzid…».
   ⚑ Grenze, gemessen: ist schon der WEG allein länger als die Zeile, wird er selbst gekürzt, und vom
     Namen bleibt nichts. Das trifft objekt.html unter rund 400 px («Sammlung › Setzmaschinen ›
     Maschinens…»). Der Name steht dann nur im Titel darunter. */
.tps-crumb__weg {
  display: block; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: 6px; margin: -6px;
}
.tps-pagehead { padding: 40px 56px 28px; }
.tps-pagehead .tp-rubric { color: var(--tp-ink-muted); margin-bottom: 14px; display: block; }
/* Rubrikzeile als Weg (Percy-Entscheid 03.09.2026, Vorschau auf bibliothek.html seit 20.08.2026).
   Ruhezustand ist der lesbare — die Haarlinie steht immer, nicht erst beim Überfahren
   (Hausordnung §3, dasselbe Muster wie `.post__adresse` auf so-gut-wie-alt.html). */
.tp-rubric a {
  color: inherit; text-decoration: none;
  border-bottom: 1px solid var(--tp-border); padding-bottom: 2px;
  transition: color 160ms linear, border-color 160ms linear;
}
.tp-rubric a:hover { color: var(--tp-ink); border-bottom-color: var(--tp-yellow); }
.tp-rubric a:focus-visible { outline: 3px solid var(--tp-yellow); outline-offset: 3px; }
.tps-pagehead h1 { margin: 0; font-size: var(--tp-t-2); }
.tps-pagehead .lead { font-family: var(--tp-font-serif); font-size: var(--tp-t-5); line-height: 1.5; color: var(--tp-ink); max-width: var(--tp-content-max); margin-top: 16px; }
/* ── Seitenkopf quer: Titel links, Anreißer rechts (Percy 14.09.2026) ─────────────────────
   Auf den Listenseiten (Verzeichnis, Sammlung, Clichés, Merkmalsvergleich) stand der Anreißer
   mit 62 Zeichen links unter dem Titel, rechts daneben blieb die halbe Breite leer, und der
   Kopf war 438 px hoch — die erste Zeile der Liste begann bei 896 px, am Bildschirmrand.
   Percy: «Man muss doch sehen, dass da unten noch eine Liste kommt.» Breit steht jetzt die
   Rubrik über die ganze Breite, darunter links der Titel (ein Drittel), rechts der Anreißer
   (zwei Drittel); alles Weitere im Kopf (die Themenkacheln der Sammlung) läuft wieder über
   die ganze Breite. Schmal bleibt es gestapelt.
   ⚑ Die Rubrik NICHT in die Titelspalte: auf `sammlung.html` füllte api.js sie bis 16.09.2026
     mit den Bestandszahlen, und dreizeilig über einem umbrechenden Titel sah der Kopf verschoben
     aus. Die Zahlen sind weg (Percy), die Regel bleibt: die Rubrik gehört über den ganzen Kopf.
   ⚑ Satzbreite 100ch statt 62ch: mit der ganzen Spalte liefen die Zeilen bei 1800 px auf rund
     130 Zeichen; 100ch ist die Breite, die für die Beschreibung unter dem Bild gilt.
   Nachtrag Percy 15.09.2026: «Titel auf eine Zeile. Der Text auf der rechten Seite wird an der
   Grundlinie des Titels ausgerichtet, beginnt also darunter.» Die Titelspalte ist darum so
   breit wie der Titel (`max-content`) statt ein Drittel — bei einem Drittel brachen drei der
   vier Titel zweizeilig um. Der Anreißer hängt mit der Versalhöhe seiner ersten Zeile an der
   Grundlinie des Titels.
   ⚑ Die zwei Faktoren sind gemessen (kopfloser Chrome, EB Garamond geladen), nicht geschätzt:
     Grundlinie des Titels 53 px unter der Oberkante bei 58 px Schrift (0.914 × Schriftgröße,
     Zeilenhöhe `--tp-leading-tight`); Versaloberkante des Anreißers 8.94 px unter seiner
     Oberkante bei 20 px Schrift und Zeilenhöhe 1.5 (0.447 × Schriftgröße). Ändert sich eine
     der beiden Zeilenhöhen, stimmen die Faktoren nicht mehr — dann neu messen.
   Nachtrag Percy 15.09.2026 (2): «unter die Titelzeile eine gelbe Unterlinie, die genau bis zur
   ersten Zeile geht» — vom Anfang des Titels bis dahin, wo die erste Zeile des Anreißers
   beginnt. Dazu kam derselbe Kopf auf alle Seiten mit Rubrik, Titel und Anreißer.
   Nach der Vorschau (Percy 16.09.2026): «6px-Linie direkt auf Grundlinie, also quasi so, als
   würde die Schrift darauf stehen. Unterlängen wie g oder y sind egal, die lappen drüber.»
   Die Oberkante der Linie liegt also auf der Grundlinie des Titels, und die Linie liegt HINTER
   der Schrift (`z-index: -1` im eigenen Stapel des Kopfs, `isolation: isolate`) — sonst
   schnitte sie die Unterlängen ab, statt dass sie darüberlappen.
   ⚑ Die erste Fassung (3 px, 20 px unter der Grundlinie, unter den Unterlängen) hat Percy
     damit verworfen.
   ⚑ Flex mit Umbruch statt Raster: «Impressum, Datenschutz & AGB» ist 767 px breit, bei 1240 px
     blieben rechts 305 px. Unter 440 px Satzbreite rutscht der Anreißer darum von selbst unter
     den Titel, ohne eine Klasse, die sich jemand merken müsste. Die −28 px unter dem Titel heben
     den Versatz dann wieder auf: 44 px darüber minus 28 px ergibt die 16 px des gestapelten
     Kopfs; nebeneinander bestimmt der Anreißer die Zeilenhöhe, und die −28 px wirken nicht.
   ⚑ `shop.html` NICHT: dort steht der Kopf in einer schmalen Spalte neben dem Schaufenster
     (Percy-Entscheid 04.09.2026), mit kleinerem Titel; einzeilig sprengte «Aus der Werkstatt —
     direkt zu dir.» die Spalte. `schriften.html` auch nicht: der Kopf hat keinen Anreißer.
   ⚑ Die Linie hängt per Ankerpositionierung an beiden Enden (Firefox 155 und Chrome am
     15.09.2026 im kopflosen Lauf nachgesehen, Safari kann es seit 26). Rutscht der Anreißer unter
     den Titel, beginnen beide an derselben Kante, die Linie wird null breit und verschwindet —
     eine Linie ins Leere gibt es nicht. Ein Browser ohne Anker liest `anchor()` nicht, und die
     Linie bleibt ebenfalls null breit. */
.tps-pagehead--quer .lead { max-width: 100ch; }
.tps-pagehead--quer .tps-pagehead__text > .lead:last-child { margin-bottom: 0; }
@media (min-width: 1240px) {
  .tps-pagehead--quer { position: relative; isolation: isolate; display: flex; flex-wrap: wrap; align-items: flex-start; column-gap: 56px; }
  .tps-pagehead--quer > * { flex: 1 0 100%; }
  .tps-pagehead--quer > h1 { flex: none; white-space: nowrap; margin-bottom: calc(16px - (var(--tp-t-2) * 0.914 - var(--tp-t-5) * 0.447)); anchor-name: --kopftitel; }
  .tps-pagehead--quer > .tps-pagehead__text { flex: 1 1 440px; margin-top: calc(var(--tp-t-2) * 0.914 - var(--tp-t-5) * 0.447); anchor-name: --kopftext; }
  .tps-pagehead--quer .tps-pagehead__text > .lead:first-child { margin-top: 0; }
  .tps-pagehead--quer::after {
    content: ""; position: absolute; z-index: -1; height: 6px; background: var(--tp-yellow);
    top: calc(anchor(--kopftitel top) + var(--tp-t-2) * 0.914);
    left: anchor(--kopftitel left); right: anchor(--kopftext left);
  }
}

.tps-section { padding: 0 56px 48px; }
.tps-section h2 { font-size: var(--tp-t-3); margin: 0 0 18px; }
.tps-sechead { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 16px; }
.tps-sechead .tp-rubric { color: var(--tp-ink-muted); }

.tps-link {
  font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); letter-spacing: .14em;
  text-transform: uppercase; color: var(--tp-ink); text-decoration: underline;
  text-decoration-color: var(--tp-yellow); text-underline-offset: 4px;
}

/* object / product cards */
.tps-obj { cursor: pointer; text-decoration: none; color: var(--tp-ink); display: block; }
/* ⚑ Die Karte, auf der man gerade steht, ist kein Ziel: sie wird als `<div>` gebaut (ohne
   `href`), und der geerbte Zeigefinger versprach einen Klick, den es nicht gibt. Gefunden
   am 08.09.2026 vom Tastatur-Prüfstand. Alle übrigen `.tps-obj` im Haus sind `<a>`. */
.tps-obj--hier { cursor: default; }
.tps-obj .ph { height: 170px; transition: border-color var(--tp-duration) var(--tp-ease); }
.tps-obj:hover .ph { border-color: var(--tp-black); }
.tps-obj__num { font-family: var(--tp-font-sans); font-weight: 900; font-size: var(--tp-t-label); letter-spacing: .06em; margin: 10px 0 4px; }
.tps-obj__num b { background: var(--tp-yellow); padding: 1px 5px; margin-right: 2px; }
.tps-obj__name { font-family: var(--tp-font-serif); font-weight: 600; font-size: var(--tp-t-5); line-height: 1.15; }
.tps-obj__meta { font-family: var(--tp-font-sans); font-size: var(--tp-t-label); color: var(--tp-ink-muted); margin-top: 3px; letter-spacing: .02em; }
.tps-obj__meta .ok { color: var(--tp-ink); border-bottom: 2px solid var(--tp-yellow); }
.tps-price { font-family: var(--tp-font-serif); font-weight: 600; font-size: var(--tp-t-5); }

/* stock badge */
.tps-stock {
  display: inline-block; padding: 1px 8px; font-size: var(--tp-t-label);
  font-family: var(--tp-font-sans); font-weight: 700; letter-spacing: .06em;
  border: 1px solid var(--tp-black); background: var(--tp-white); color: var(--tp-ink);
}
.tps-stock--low { background: var(--tp-yellow); }
.tps-stock--out { background: #e5e3df; border-color: #bbb; color: #888; }

/* chips */
.tps-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.tps-chip {
  font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); letter-spacing: .08em;
  border: 1px solid var(--tp-black); padding: 8px 14px; text-decoration: none; color: var(--tp-ink);
  background: var(--tp-white); transition: background var(--tp-duration) var(--tp-ease);
}
.tps-chip:hover { background: var(--tp-bg-soft); }
.tps-chip b { background: var(--tp-yellow); padding: 0 4px; margin-right: 4px; }

/* detail spec block */
.tps-spec { border-top: var(--tp-bar) solid var(--tp-yellow); padding-top: 22px; }
.tps-spec .tp-rubric { color: var(--tp-ink-muted); margin-bottom: 10px; display: block; }
.tps-spec h1 { font-size: var(--tp-t-2); margin: 0 0 6px; }
.tps-spec .sub { font-family: var(--tp-font-sans); font-size: var(--tp-t-klein); letter-spacing: .06em; color: var(--tp-ink-muted); margin-bottom: 24px; }
.tps-spec .sub .ok { color: var(--tp-ink); border-bottom: 2px solid var(--tp-yellow); font-weight: 700; }
/* ── Zählende Ziffern (Etappe 2.7 E) ────────────────────────────────────────────
   Am `body` steht `font-feature-settings: "onum"` — Mediävalziffern. Im Fließtext
   gehören sie dorthin: sie haben Ober- und Unterlängen und laufen mit der Schrift mit.
   In einer **Kolonne** springen sie darum. Jede Zahl, die gezählt oder verglichen wird,
   bekommt Versalziffern auf gleicher Höhe und gleicher Breite. Eine Klasse statt
   fünfzehn Einzelregeln — und ein Name, an dem man erkennt, warum sie da ist. */
.zahl-kolonne { font-variant-numeric: lining-nums tabular-nums; }

.tps-meta { border-top: 1px solid var(--tp-border); margin: 0; }
.tps-meta__row { display: grid; grid-template-columns: 160px 1fr; gap: 16px; padding: 9px 0; border-bottom: 1px solid var(--tp-border); }
.tps-meta__row dt { font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); text-transform: uppercase; letter-spacing: .18em; color: var(--tp-ink-muted); margin: 0; padding-top: 3px; }
.tps-meta__row dd { font-family: var(--tp-font-serif); font-size: var(--tp-t-klein); margin: 0; }
.tps-ctas { display: flex; gap: 10px; flex-wrap: wrap; margin: 20px 0; }

/* info card */
.tps-card { border: 1px solid var(--tp-border); padding: 22px 24px; background: var(--tp-white); }
.tps-card--soft { background: var(--tp-bg-soft); border: 0; }
.tps-card--yellow { background: var(--tp-yellow); border: 0; }
/* ⚑⚑ ZWEI Ebenen im selben Selektor, und das ist Absicht: wie ein Kartentitel AUSSIEHT,
   gehört der Karte; auf welcher EBENE er steht, hängt davon ab, wo die Karte sitzt. Auf
   druckerei.html stehen die drei Karten direkt unter der Seitenüberschrift, sind also h2;
   auf bibliothek, kontakt und kurse sitzen sie in einem Abschnitt und sind h3. Wer das an
   eine Ebene bindet, muss beim Verschieben die Größe nachziehen — und vergisst es. */
.tps-card h2, .tps-card h3 { font-size: var(--tp-t-4); margin: 0 0 8px; }
/* ⚑⚑ Percy-Entscheid 04.09.2026 (Bogen «Kachel-Anrisse»): Weg C, Grundschrift.
   Ein Absatz ist ein Absatz, auch wenn ein Rahmen darum steht — 20 px auf 1,55
   wie überall (§2). Vorgelegt waren drei Wege mit gemessenen Kosten; C kostet auf
   druckerei.html bei 375 px rund 687 px Höhe, und das hat er in Kauf genommen. */
.tps-card .tp-rubric { color: var(--tp-ink-muted); display: block; margin-bottom: 10px; }
/* ⚑⚑ Hier stand «Schwarz zu 55 % durchsichtig» — die EINZIGE Stelle der ganzen Website,
   die den Lesbarkeits-Maßstab verfehlte: 4,26 statt 4,5 (gemessen 26.08.2026 über 17
   Seiten). Auf Gelb gibt es kein gedämpftes Grau, das durchkommt: `--tp-ink-muted` liefert
   nur 4,37. Darum die volle Tinte, 11,42. Merksatz: eine Farbe, die aus Durchsichtigkeit
   entsteht, kennt ihren eigenen Kontrast nicht — sie hängt davon ab, worauf sie liegt. */
.tps-card--yellow .tp-rubric { color: var(--tp-ink); }

/* agenda rows */
.tps-agenda { border-top: 1px solid var(--tp-border); }
.tps-agenda__row {
  display: grid; grid-template-columns: 130px 1fr auto; gap: 20px; align-items: baseline;
  padding: 14px 0; border-bottom: 1px solid var(--tp-border); text-decoration: none; color: var(--tp-ink);
}
.tps-agenda__row:hover .t { text-decoration: underline; text-decoration-color: var(--tp-yellow); text-underline-offset: 3px; }
.tps-agenda__row .d { font-family: var(--tp-font-sans); font-weight: 900; font-size: var(--tp-t-label); letter-spacing: .1em; text-transform: uppercase; }
.tps-agenda__row .d em { font-style: normal; background: var(--tp-yellow); padding: 1px 5px; }
.tps-agenda__row .t { font-family: var(--tp-font-serif); font-weight: 600; font-size: var(--tp-t-5); }
.tps-agenda__row .m { font-family: var(--tp-font-sans); font-size: var(--tp-t-klein); color: var(--tp-ink-muted); letter-spacing: .04em; }

/* price table */
.tps-table { border: 1px solid var(--tp-black); }
.tps-table__row { display: flex; justify-content: space-between; align-items: baseline; padding: 13px 20px; border-bottom: 1px solid var(--tp-border); }
.tps-table__row:last-child { border-bottom: 0; }
.tps-table__row .k { font-family: var(--tp-font-serif); font-size: var(--tp-t-klein); }
.tps-table__row .k small { display: block; font-family: var(--tp-font-sans); font-size: var(--tp-t-label); color: var(--tp-ink-muted); letter-spacing: .04em; margin-top: 2px; }
.tps-table__row .v { font-family: var(--tp-font-serif); font-weight: 600; font-size: var(--tp-t-5); white-space: nowrap; }

/* footer shared */
.tps-support { display: flex; gap: 28px; align-items: center; }
.tps-support img { height: 40px; width: auto; background: var(--tp-white); }

/* ===== RESPONSIVE (mobil) — geteilte Seitenkomponenten =============== */
@media (max-width: 820px) {
  .tps-crumb { padding: 14px 20px 0; }
  .tps-pagehead { padding: 32px 20px 22px; }
  .tps-pagehead h1 { font-size: var(--tp-t-3); }
  .tps-pagehead .lead { font-size: var(--tp-t-5); }
  .tps-section { padding: 0 20px 40px; }
  .tps-section h2 { font-size: var(--tp-t-3); }
  .tps-sechead { flex-wrap: wrap; gap: 4px 16px; }
  .tps-spec h1 { font-size: var(--tp-t-3); }
  .tps-meta__row { grid-template-columns: 130px 1fr; gap: 10px; }
  .tps-agenda__row { grid-template-columns: 78px 1fr; gap: 6px 12px; }
  .tps-support { flex-wrap: wrap; gap: 16px; }
}
@media (max-width: 480px) {
  .tps-pagehead h1 { font-size: var(--tp-t-3); }
  .tps-meta__row { grid-template-columns: 1fr; gap: 2px; }
  .tps-meta__row dt { padding-top: 0; }
}
/* Kacheln, die `schaufenster.js` selbst füllt.
 *
 * ⚑ Bewusst eine eigene Klasse statt `.tps-obj` umzubauen: die von Hand gesetzten
 *    Kacheln quer über die Seite bleiben so, wie sie sind. Nur die von
 *    `schaufenster.js` gefüllten richten ihren Fuß bündig aus — Artikelnamen aus
 *    Dolibarr sind unterschiedlich lang, und ohne das steht der Preis in jeder
 *    Kachel auf einer anderen Höhe.
 */
.tps-obj--live { display: flex; flex-direction: column; height: 100%; }
.tps-obj--live .tps-obj__name { margin-top: 8px; }
.tps-obj--live .tps-obj__foot {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 8px; margin-top: auto; padding-top: 8px;
}

/* Quadratischer Bildplatz (Percy, 04.08.2026) — die Produktfotos sind quadratisch,
 * also darf der Platz nicht 140 oder 150 Pixel hoch sein und den Rest mit Weißraum
 * füllen. Gleiches Seitenverhältnis wie die Karten im Shop-Raster (`.karte__bild`),
 * damit Startseite, Shop-Kopf und Katalog dieselbe Form zeigen.
 *
 * ⚑ Drei Klassen im Wähler, weil `index.html` in seinem eigenen `<style>`-Block
 *    `.shop-grid .ph { height: 140px }` setzt — das steht **nach** dieser Datei und
 *    gewönne bei gleicher Spezifität.
 */
.tps-obj--live .ph.ph--contain {
  height: auto;
  aspect-ratio: 1 / 1;
}
.tps-obj--live .ph.ph--contain img {
  width: 100%; height: 100%; object-fit: contain;
}

/* ── Der leere Zustand einer Knotenseite (§3.12, Etappe 2.14) ──────────────────
 * Steht hier statt dreimal in den Seiten: schrift, objekt und maschine benutzen
 * dieselbe Funktion `knotenFehlt()`, also auch dieselbe Form. Kein Kasten, keine
 * Fläche — Weißraum und eine Haarlinie, wie überall sonst.
 */
.knoten-fehlt {
  max-width: var(--tp-content-max);
  margin: 0 56px;
  padding: 44px 0 72px;
  border-top: 1px solid var(--tp-border);
}
.knoten-fehlt h1 {
  font-family: var(--tp-font-serif);
  font-size: var(--tp-t-3); line-height: 1.15; font-weight: 600;
  margin: 26px 0 14px;
}
.knoten-fehlt p {
  font-family: var(--tp-font-serif);
  font-size: var(--tp-t-5); line-height: 1.6;
  margin: 0 0 18px;
}
@media (max-width: 820px) {
  .knoten-fehlt { margin: 0 20px; padding: 30px 0 52px; }
  .knoten-fehlt h1 { font-size: var(--tp-t-3); }
}

/* ── Der leere Zustand einer Trefferliste (§3.12, Etappe 2.14) ─────────────────
 * Fünf Listen, eine Form: `shop.html` hatte sie zuerst, `sammlung`, `cliches` und
 * `register` benutzen dieselbe Funktion — also gehören die Regeln hierher und nicht
 * dreimal in die Seiten.
 */
.nix { padding: 26px 0 8px; max-width: var(--tp-content-max); }
.nix h3 { font-family: var(--tp-font-serif); font-size: var(--tp-t-4); margin: 0 0 10px; }
.nix__wahl { font-family: var(--tp-font-sans); font-size: var(--tp-t-label); letter-spacing: .06em;
             color: var(--tp-ink-muted); margin: 0 0 14px; }
.nix__ohne { display: block; width: 100%; text-align: left; border: 0; background: none;
             cursor: pointer; padding: 7px 0; border-top: 1px solid var(--tp-border);
             font-family: var(--tp-font-serif); font-size: var(--tp-t-klein); color: var(--tp-ink); }
.nix__ohne:hover { background: var(--tp-paper-2); }
.nix__ohne b { font-weight: 700; font-variant-numeric: lining-nums tabular-nums; }
.nix__alle { margin-top: 16px; }

/* ═══════════════════════════════════════════════════════════════════════════════
   Das Such-Modal (suche.js) — die globale Suche, auf jeder Seite erreichbar.
   ⚑ Percy-Freigabe 13.08.2026 nach Vorschau (`_vorschau/suchmodal.html`).
   ⚑ Kein Dark Mode, Haarlinien statt Flächen, Fokus gelb und 3 px (Hausordnung §1–§4).
   ═══════════════════════════════════════════════════════════════════════════════ */
/* ⚑ Die Regeln des Such-Fensters stehen seit 25.08.2026 in `ds/suche.css`.
   `suche.js` hängt das Fenster in jede Seite ein, die es lädt — die Startseite
   lädt aber `shared.css` nicht, und dort stand das Fenster darum ungestylt im
   Textfluss. Nicht wieder hierher kopieren. */
@media (max-width: 700px) {
  .sm__kasten { margin: 0; max-width: none; height: 100%; max-height: none; border: 0; }
}
/* ⚑ Hausordnung §3: keine Bewegung, wo sie abbestellt ist. */
@media (prefers-reduced-motion: no-preference) {
  .sm.auf .sm__kasten { animation: smauf .18s ease-out; }
  @keyframes smauf { from { transform: translateY(-8px); opacity: .6; } }
}

/* ⚑⚑ Das Komplett-Zeichen des Teileverzeichnisses.
   Im gedruckten Band steht hinter manchen Teilenummern ein ■; der Fuß jeder Seite
   erklärt es: «■ = komplett» — der Eintrag meint die zusammengebaute Gruppe mit
   Zubehör, nicht das Einzelstück. Bis zum 13.08.2026 steckte das Zeichen in der
   Teilenummer selbst (428 von 6780). Seither ist es eine Eigenschaft und wird HIER
   wieder angehängt: die Nummer bleibt suchbar, die Auskunft des Katalogs bleibt
   vollständig. Percy: «Schließlich will ich so nah wie möglich am Teileverzeichnis
   bleiben.»
   ⚑ Ink, nicht Gelb: auf hellem Papier ist Gelb eine Fläche, kein Text (Hausordnung §1). */
.kompl {
  margin-left: .38em;
  font-size: .74em;
  line-height: 1;
  vertical-align: .12em;
  color: var(--tp-ink);
  border: 0;                 /* <abbr> bekommt sonst in manchen Browsern eine Punktlinie */
  text-decoration: none;
  cursor: help;
}

/* ── Weiterweg am Fuß einer Seite ────────────────────────────────────────────
   «Weiter» plus zwei bis drei Türen, jede mit Rubrik, Titel und einem Satz.
   ⚑ Stand bis zum 20.08.2026 dreimal wortgleich als lokales CSS in geschichte.html,
   stiftung.html und foerderverein.html. Mit Clichés und Rundgang wären es fünf Kopien
   geworden — darum hier, einmal. Die Klassennamen sind die alten (`gs…` von der
   Geschichtsseite). ⚑ Das nummerierte Band heißt seit 06.09.2026 `.bandnr` und ist
   aus dieser Familie heraus — siehe den Block darunter. */
/* ⚑⚑ EIN BAUTEIL, EIN NAME: `.bandnr` (06.09.2026).
   Das nummerierte Band — Zahl, dann eine Haarlinie bis zum Rand — hieß im Haus DREIMAL
   anders: `.gsnr` auf den Gönnerseiten, `.nr` auf besuch.html, `.nr` auf index.html.
   `driftmass.py` konnte das nie melden — es vergleicht Selektoren, und `.nr` und `.gsnr`
   sind verschiedene Selektoren. Gefunden nur, weil beim Zusammenlegen der Haarlinie
   auffiel, dass dieselbe Regel hier längst unter `.gsnr` stand.

   ⚑⚑ Und `.nr` musste weg, weil der Name schon vergeben war: auf register.html,
   schriften.html und sammlung.html ist `.nr` eine ZAHL IN EINER TABELLENZELLE
   (`<span class="nr"><b>8573</b></span>`, sans, Strichstärke 900) — ein völlig anderes
   Ding, und `api.js` erzeugt sie an fünf Stellen. Ein geteiltes `.nr { display:flex }`
   hätte diese Zellen zu Flex-Kästen gemacht; genau das war am 06.09. der erste Anlauf.
   Jetzt heißt das Band `.bandnr` und die Zelle `.nr`, und keiner der beiden Namen
   bedeutet noch zweierlei.

   ⚑ `gsnr` hieß es, weil die Regeln 2026 von der Geschichtsseite kamen («gs…») und man
   damals kein Markup anfassen wollte. Der Grund ist mit diesem Umzug entfallen — die
   Geschwister `.gstueren`/`.gstuer` tragen ihr Kürzel weiter, sie kollidieren mit nichts.
   Umbenannt an 20 Markup-Stellen auf den drei Gönnerseiten und an 8 auf besuch und index;
   gemessen mit `bauteilbild.py --name namen1`: eine Umbenennung muss NICHTS verändern. */
.bandnr { display: flex; align-items: baseline; gap: 16px; margin-bottom: 12px; }
/* ⚑⚑ PERCY-ENTSCHEID 06.09.2026: «Strichstärke 700 — die Fassung der Gönnerseiten.»
   Die Zahl stand in drei Strichstärken: 700 hier, 500 auf besuch.html, 400 (also gar
   keine Auszeichnung) auf der Startseite; dazu zwei Sperrungen (.16em / .2em). Genommen
   ist diese Fassung, wörtlich — sie stand schon an den meisten Stellen (6 von 18
   gezählten Bändern) und ist die einzige mit Ziffernbreite.
   ⚑ Ein Unterschied war KEINE Drift, sondern Inhalt: auf der Startseite steht in der
   Zahl nur «01», auf den anderen beiden «01 — Titel». `text-transform` und `white-space`
   tun an einer reinen Ziffer nichts — sie schaden dort auch nicht.
   ⚑ Der Selektor hieß bis zur Umbenennung am selben Tag `.gsnr .zahl, .nr .zahl`. */
.bandnr .zahl {
    font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label);
    letter-spacing: .16em; text-transform: uppercase; color: var(--tp-ink-muted);
    font-variant-numeric: tabular-nums;
  }
/* ⚑⚑ `white-space: nowrap` ist beim Zusammenlegen HERAUSGEFALLEN, und das ist kein
   Versehen. Es stand in der Gönner-Fassung und wäre als Beifang mitgewandert: auf der
   Startseite trägt eines der neun Bänder einen langen Text, der bei 375 px umbrechen
   MUSS. Mit `nowrap` wurde die Zahl dort 416 px breit in einem 375-px-Fenster — 41 px
   waagrechter Überlauf auf dem Telefon, gemessen mit `bauteilbild.py` am 06.09.2026
   (34 px hoch und umgebrochen vorher, 17 px hoch und hinausragend nachher).
   Entschieden hat Percy die STRICHSTÄRKE; `nowrap` war nie die Frage.
   ⚑ Gegengeprüft, ob es woanders gebraucht wird: die breiteste Bandzahl bei 375 px ist
   241 px auf foerderverein.html, 177 auf besuch.html — beide weit im Fenster. `nowrap`
   hat dort nie etwas bewirkt und fehlt jetzt niemandem. */
.bandnr .strich { flex: 1; height: 1px; background: var(--tp-border); }
.gstueren { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
              gap: 20px; max-width: 1000px; }
/* ⚑⚑ Nie eine Tür allein in der letzten Zeile (Percy 13.09.2026). `auto-fit` legt so viele
   Spalten an, wie hineinpassen — bei drei Türen zwischen etwa 560 und 900 px sind das zwei,
   die dritte stand dann allein darunter (Geschichte, Stiftung, Förderverein am Tablet); bei
   vier Türen passten in 1000 px nur drei (Trägerschaft, bei JEDER Breite). Die Spalten
   richten sich darum nach der ANZAHL, nicht nach dem Platz:
     2 Türen  → zwei nebeneinander oder untereinander (auto-fit genügt, keine Waise möglich)
     3 Türen  → drei nebeneinander oder untereinander, nie zwei und eine
     4 Türen  → vier in einer Reihe (je so groß wie drei in 1000 px: 4 × 320 + 3 × 20 =
                1340), schmaler zwei mal zwei, auf dem Handy untereinander (Weg A)
   Gezählt wird mit `:has()`, damit keine Seite eine Zusatzklasse pflegen muss — eine
   fünfte Tür auf einer Seite ändert die Anordnung von selbst mit, statt sie zu zerreißen.
   `minmax(0, 1fr)`, nicht `1fr` — sonst sprengt ein langes Wort die Spalte. */
.gstueren:has(> :nth-child(3):last-child) { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) {
  .gstueren:has(> :nth-child(3):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.gstueren:has(> :nth-child(4):last-child) { max-width: 1340px; grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1240px) {
  .gstueren:has(> :nth-child(4):last-child) { max-width: 1000px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 600px) {
  .gstueren:has(> :nth-child(4):last-child) { grid-template-columns: minmax(0, 1fr); }
}
.gstuer { border: 1px solid var(--tp-border); padding: 20px 22px; text-decoration: none;
            color: var(--tp-ink); display: block;
            transition: border-color 180ms ease, background-color 180ms ease; }
.gstuer:hover { border-color: var(--tp-black); background: var(--tp-bg-soft); }
.gstuer h2, .gstuer h3 { font-size: var(--tp-t-4); margin: 6px 0 6px; }
/* ⚑⚑ Percy-Entscheid 04.09.2026 (Bogen «Kachel-Anrisse»): Weg C, Grundschrift.
   Ein Absatz ist ein Absatz, auch wenn ein Rahmen darum steht — 20 px auf 1,55
   wie überall (§2). Vorgelegt waren drei Wege mit gemessenen Kosten; C kostet auf
   druckerei.html bei 375 px rund 687 px Höhe, und das hat er in Kauf genommen. */

/* ── Anmeldeknopf der Kursliste ───────────────────────────────────────────────────────
   ⚑⚑ Diese Regeln standen bis 25.08.2026 in `kursanmeldung.js` — in einem Stilblatt, das
   erst gebaut wird, WENN jemand den Anmelde-Dialog öffnet (`bauen()` läuft nur aus
   `oeffnen()`). Die Knöpfe «Anmelden» und «Termin anfragen» stehen aber von Anfang an auf
   der Seite: bis zum ersten Klick waren sie nackte Browser-Knöpfe — grau, klein, in
   Systemschrift, direkt neben dem Preis. Sie waren der Grund, warum die Zählung eine
   dritte Schriftart fand (Arial, 8 Stellen).
   ⚑ Ein Bauteil, das die Seite von Anfang an zeigt, braucht seine Regeln von Anfang an.
   Der Dialog selbst darf sein Stilblatt weiter nachladen — den sieht vorher niemand. */
.kanmknopf {
  font-family:var(--tp-font-sans);
  font-weight:700;
  font-size:var(--tp-t-label);
  letter-spacing:.1em;
  text-transform:uppercase;
  background:var(--tp-black);
  color:var(--tp-white);
  border:0;
  padding:9px 16px;
  cursor:pointer}
.kanmknopf--warte {
  background:var(--tp-white);
  color:var(--tp-ink);
  border:1px solid var(--tp-black)}

/* Ein Kurstermin, den man anklicken kann — öffnet das Anmeldefenster mit diesem Datum
   schon ausgewählt (kursanmeldung.js, `oeffnen(kurs, beginn)`). Auf kurse.html und
   kinder-drucken.html. Percy-Entscheid 13.09.2026, Variante A: ruhiger Pfeil statt
   ausgeschriebenem «Anmelden», die ganze Zeile reagiert.
   ⚑ Hier und nicht im Stilblatt von kursanmeldung.js — das entsteht erst beim ersten
   Öffnen des Fensters; die Termine stehen aber von Anfang an da (dieselbe Lehre wie
   `.kanmknopf` oben).
   ⚑⚑ NICHT `.terminwahl` nennen: der Name gehört besuch.html (Rahmen der Führungswahl),
   und eine Regel hier hätte dessen Layout still umgeworfen.
   ⚑ Negativer Rand + um 16 px verbreitert: der Text steht genau dort, wo er vorher als
   reiner Text stand, nur der Hover-Grund reicht links und rechts 8 px hinaus — innerhalb
   des Kartenrands, also ohne Überlauf. */
.kurstermin {
  display:flex;
  justify-content:space-between;
  align-items:baseline;
  gap:16px;
  box-sizing:border-box;
  width:calc(100% + 16px);
  margin:0 -8px;
  padding:7px 8px;
  background:none;
  border:0;
  border-radius:0;
  color:inherit;
  font:inherit;
  text-align:left;
  cursor:pointer}
.kurstermin:hover { background:var(--tp-paper-2) }
.kurstermin:focus-visible { outline:3px solid var(--tp-yellow); outline-offset:3px }
.kurstermin__rechts {
  display:inline-flex;
  align-items:baseline;
  gap:10px;
  flex-shrink:0}
.kurstermin__pfeil {
  font-family:var(--tp-font-sans);
  font-size:var(--tp-t-5);
  line-height:1;
  color:var(--tp-ink-muted)}
.kurstermin:hover .kurstermin__pfeil { color:var(--tp-ink) }

/* ── Die Filterspalte bleibt stehen ───────────────────────────────────────────────────
   ⚑⚑ Percy-Auftrag 25.08.2026: «die gibt es bis jetzt nur auf cliches.html» — auf allen
   fünf Listenseiten. Bei 2316 Clichés oder 5742 Schriften ist der Unterschied zwischen
   brauchbar und mühsam, ob man zum Filtern an den Seitenanfang zurückmuss.
   ⚑ Die Regel steht HIER und nicht in den Seiten: `cliches.html`, `sammlung.html`,
   `register.html` und `shop.html` tragen dieselbe `<aside class="facets">`. Eine Kopie je
   Seite wäre die vierte Fassung derselben Sache — genau das, was diese Woche schon bei
   Kopf- und Fußzeile aufgeräumt werden musste.
   ⚑ Drei Dinge gehören zwingend dazu:
   · `align-self: start` — ohne das dehnt das Raster die Spalte auf volle Höhe und nichts klebt
   · `max-height` + `overflow-y` — sonst hängt eine lange Filterliste unten aus dem Bild
   · `overscroll-behavior: contain` — sonst scrollt am Ende der Liste die Seite darunter weiter
   ⚑ Unter 900 px steht die Spalte wieder normal im Fluss: dort ist sie über der Liste und
   hätte nichts, woran sie kleben könnte. */
.facets {
  position: sticky; top: calc(var(--tp-kopf, 71px) + 16px); align-self: start;
  max-height: calc(100vh - var(--tp-kopf, 71px) - 32px);
  overflow-y: auto; overscroll-behavior: contain; padding-right: 8px;
  scrollbar-width: thin;
}
/* ⚑⚑ Die Filterüberschriften sind ETIKETTEN, keine Titel — «Schlagwort», «Material»,
   «Setzsystem». Sie sagen, wonach die Liste darunter sortiert, sie überschreiben nichts.
   Bis zum 25.08.2026 sahen sie so aus, weil die Grundeinstellung von `h4` zufällig ein
   Etikett war; als `h4` zum Titel wurde, wären daraus auf einen Schlag 10 Serifentitel in
   vier Seitenleisten geworden. Darum steht die Rolle jetzt hier, ausgeschrieben — und
   an EINER Stelle für alle vier Seiten (cliches, register, sammlung, shop). */
.facets h3 {
  font-family: var(--tp-font-sans);
  font-weight: 700;
  font-size: var(--tp-t-klein);
  text-transform: uppercase;
  letter-spacing: var(--tp-tracking-wide);
  line-height: var(--tp-leading-snug);
  /* ⚑ Der Abstand stand bis 25.08.2026 auf allen VIER Filterseiten einzeln und überall
     gleich. Eine Zahl an vier Orten ist drei Gelegenheiten, sie auseinanderlaufen zu
     lassen — jetzt steht sie hier. */
  margin: 0 0 12px;
}

/* ⚑⚑ Nur für Vorleseprogramme, nicht fürs Auge. `display:none` und `visibility:hidden`
   taugen dafür NICHT — die nehmen den Text auch dem Vorleseprogramm weg. Der Text bleibt
   im Dokument, steht aber auf einer Fläche von 1×1 px ohne Ausdehnung.
   ⚑ Die Klasse wurde schon seit Längerem benutzt (druckerei.html, das Suchfeld der
   Handsatzschriften) — DEFINIERT war sie nirgends. Sie wirkte dort allein über ein
   `style`-Attribut daneben; wer die Klasse anderswo verwendet hätte, hätte sichtbaren
   Text bekommen. */
.tp-visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}
.facets::-webkit-scrollbar { width: 6px; }
.facets::-webkit-scrollbar-thumb { background: var(--tp-border); }
@media (max-width: 900px) {
  .facets { position: static; max-height: none; overflow: visible; padding-right: 0; }
}

/* ⚑⚑ Die Kachel-Grundschrift — EINE Regel für alle sechs Kachelformen.
   Bis 04.09.2026 stand dieselbe Erklärung sechsmal: `.tps-card p` und `.gstuer p` hier,
   `.jcard p` in aktuelles, `.kcard__body p` in kurse, `.registerband p` in sammlung,
   `.tuer p` in so-gut-wie-alt. Sechs Orte für eine Sache — und darum drei verschiedene
   Zeilenabstände (1,45 · 1,5 · 1,55), obwohl es dieselbe Frage ist. Gemessen am
   04.09.2026 an 49 Anrissen: 5 Ausprägungen schon in Schrift/Grad/Zeilenabstand allein,
   9 wenn man Farbe und Satzbreite mitzählt, 11 mit dem Aussenabstand. (Eine Zahl ohne
   ihre Lupe ist keine Messung — darum stehen hier alle drei.)
   Percys Entscheid vom selben Tag (Bogen «Kachel-Anrisse», Weg C) gilt für alle:
   ein Absatz ist ein Absatz, auch wenn ein Rahmen darum steht — 20 px auf 1,55 (§2).

   ⚑ Zur Ladereihenfolge, weil sie oft falsch erinnert wird: ds/colors_and_type → ds/site →
   diese Datei → das <style> der Seite → und DANACH noch ds/kopfzeile, ds/fusszeile,
   ds/suche, die im <body> hängen. Das <style> der Seite ist also NICHT das Letzte.

   ⚑ `:where(...)` ist Absicht, nicht Ziererei: es zählt mit Spezifität NULL. Damit ist
   das hier ein BODEN, den jede Klassenregel (0-1-0) von sich aus schlägt — genau der
   Fehler, der am 04.09. drei Labels auf so-gut-wie-alt.html in Garamond 15 px rendern
   ließ, weil `.tuer p` (0-1-1) stärker war als `.tuer__preise` (0-1-0). Ein Boden kann
   das nicht mehr. Die `.tuer`-Labels bleiben trotzdem ausdrücklich ausgenommen: sie
   erklären ihren Aussenabstand nicht selbst und lebten bisher von `p { margin: 0 0 1em }`.

   ⚑ Was NICHT hierher gehört: Farbe, Satzbreite, Aussenabstand über 0 hinaus. Das sind
   die Stellen, an denen sich die Formen heute wirklich unterscheiden; sie stehen als
   Einzeiler bei der jeweiligen Kachel. Wer eine davon vereinheitlichen will, fragt Percy
   — nicht die Regel. */
:where(.tps-card, .gstuer, .jcard, .kcard__body, .registerband) p {
  font-family: var(--tp-font-serif);
  font-size: var(--tp-t-5);
  line-height: var(--tp-leading-normal);
  margin: 0;
}

/* ⚑ Absichtlich eine EIGENE Regel und nicht im Komma oben: eine Selektorliste ist nicht
   nachsichtig. Kennt ein Browser `:where()` nicht, verwirft er die GANZE Liste — und
   risse diese Hälfte mit, obwohl sie für sich einwandfrei liest. Getrennt überlebt sie.
   (Was beim Ausfall verloren ginge, ist nur `margin: 0`: Schrift, Grad und Zeilenabstand
   stehen identisch schon am `body` und kommen über die Vererbung an. Die Kacheln würden
   also nicht unformatiert, nur um je 20 px höher.)
   ⚑ Der Ausschluss gilt zwei Labels, aber aus EINEM Grund nur einem: `.tuer__preise`
   erklärt bloß `margin-top` und lebt für unten von `p { margin: 0 0 1em }` — gemessen
   `4px 0px 11px`. `.tuer__legende` erklärt `margin: 0` selbst und bräuchte den Ausschluss
   für den Abstand nicht; sie steht hier wegen der Satzbreite. Das Hausmass sind 62 ZEICHEN,
   und auf einem 11-px-Label ergibt das rund 328 px — die Legende bräche mitten in der
   Kachel um, während die Kachel selbst rund 440 px breit ist. */
.tuer p:not(.tuer__legende):not(.tuer__preise) {
  font-family: var(--tp-font-serif);
  font-size: var(--tp-t-5);
  line-height: var(--tp-leading-normal);
  margin: 0;
  max-width: var(--tp-content-max);
}

/* Die Objektbeschreibung auf objekt.html und maschine.html. Stand bis 04.09.2026
   zeichengleich in BEIDEN Seiten — eine Änderung war zwei Änderungen, und wer die zweite
   vergass, hatte zwei Detailseiten mit verschiedenem Satz. Die Satzbreite war bis zum
   04.09.2026 mit 66ch eine eigene Zahl; seit Percys Entscheid vom selben Tag holt sie
   sich das Hausmass wie alle anderen.
   ⚑⚑ Seit 13.09.2026 OHNE Satzbreite: die Beschreibung läuft über die ganze Breite des
   Bildes (Percy: «Die Beschreibung der Maschinen bitte über die gesamte Breite des Bildes.
   Kann sein, dass es mit einer Regel kollidiert, aber das muss anders.»). Das Hausmass
   62ch gilt hier bewusst nicht — der Text ist ein Block unter dem Bild, und eine kürzere
   Zeile ließ rechts daneben eine leere Fläche stehen. */
.od-desc {
  font-family: var(--tp-font-serif);
  font-size: var(--tp-t-5);
  line-height: var(--tp-leading-normal);
  margin: 20px 0 0;
}


/* ══ TREFFERLISTE ═══════════════════════════════════════════════════════════════════
   Suchzeile · Bereichsreiter · Facetten · Werkzeugleiste · Ergebnistabelle.

   ⚑⚑ Dasselbe Bauteil stand am 04.09.2026 auf FÜNF Seiten fünfmal: register, sammlung,
   cliches, schriften, vergleich (und in Teilen shop). Gemessen mit
   `_werkzeuge/driftmass.py`: `.toolbar` in 5 Fassungen, `.hits` in 3, `table.db td` in 3,
   `.facet-row` in 2 — Unterschiede von 2 px Abstand und .02em Sperrung, die nie jemand
   entschieden hat. Percy am 04.09.2026: «Dann mach die Trefferliste als nächstes — fang
   mit register.html an.»

   ⚑ Diese Fassung ist WÖRTLICH die von register.html, damit der Umzug beweisbar nichts
   verändert (Maßstab: `_werkzeuge/trefferbild.py`, 226 Bauteile auf sechs Seiten, bei
   1400 und 375 px). Die anderen Seiten tragen ihre eigenen Regeln noch — die stehen
   später in der Kaskade und gewinnen weiterhin. Sie kommen einzeln dran, nicht alle auf
   einmal, und jede mit eigener Messung.

   ⚑ NEBENWIRKUNG, gemessen und benannt: sammlung.html und shop.html haben eigene
   `.toolbar`-Regeln, die `gap` und `flex-wrap` NICHT erklären — die holen sie sich jetzt
   von hier (gap 20px, wrap). cliches.html ebenso `gap` bei `.sys-tabs`. Sichtbar ist das
   nirgends: alle vier Bauteile messen bei 1400 UND 375 px auf das Pixel dieselbe Größe
   wie vorher (`gap` wirkt bei `space-between` mit zwei Enden nicht, `wrap` nur beim
   Überlaufen). Es ist eine stille Verbesserung — diese Leisten können jetzt umbrechen
   statt hinauszulaufen. Aber es ist eine Änderung an Seiten, die nicht dran waren, und
   darum steht sie hier.

   ⚑ Warum hier und nicht in einem eigenen `ds/`-Blatt: `ds/kopfzeile.css`,
   `ds/fusszeile.css` und `ds/suche.css` hängen im <body> und laden damit NACH dem
   <style> der Seite. Ein Bauteil dort wäre von der Seite nicht mehr überschreibbar —
   genau das Gegenteil dessen, was ein geteiltes Bauteil braucht.
   ══════════════════════════════════════════════════════════════════════════════════ */

/* Suchzeile — Ein Kasten, ein Feld, ein Knopf. Der Rahmen gehört dem KASTEN, nicht dem
   Feld, damit der gelbe Fokusring einmal um das Ganze läuft und nicht um zwei Teile;
   `:has()` macht das ohne eine Zeile JavaScript.
   ⚑ `outline: none` am Feld ist darum KEINE Lücke, sondern die Bedingung dafür
   (Percy-Entscheid 10.08.2026: der Fokusring ist einfach, nicht doppelt). Dieser Hinweis
   stand bis 05.09.2026 in register.html und wäre dort nach dem Umzug verwaist —
   ein Kommentar ohne seine Regel schickt die nächste Session auf die falsche Seite. */
.search-bar { display: flex; margin: 0 56px; border: 1px solid var(--tp-black); }
.search-bar input { flex: 1; border: 0; padding: 18px 20px; font-family: var(--tp-font-serif); font-size: var(--tp-t-5); color: var(--tp-ink); outline: none; background: var(--tp-white); min-width: 0; }
/* ⚑ `min-width: 0` am Feld: ein Flex-Kind schrumpft von sich aus NICHT unter seinen
   Inhalt, und ein Eingabefeld bringt eine eingebaute Mindestbreite mit (rund 170 px).
   Ohne diese Angabe kann die Suchzeile in einem engen Kasten hinauslaufen. Nur
   shop.html hatte sie — dieselbe Sorte Fehler wie die neun Raster vom 05.09.2026,
   nur an einem Formularfeld. Jetzt haben sie alle vier Seiten. */
.search-bar input::placeholder { color: var(--tp-hint); }
.search-bar:has(input:focus-visible) { outline: 3px solid var(--tp-yellow); outline-offset: 2px; }
.search-bar .tp-btn { border: 0; border-left: 1px solid var(--tp-black); }

/* Bereichsreiter — Die Reiter über der Liste. Der aktive trägt seinen Balken als `box-shadow` innen, nicht als `border` — ein Rahmen würde die Zeile um seine Stärke verschieben, ein Schatten nicht. */
.sys-tabs { display: flex; gap: 0; margin: 20px 56px 0; border-bottom: 1px solid var(--tp-border); width: fit-content; }
.sys-tabs a { font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); letter-spacing: .12em; text-transform: uppercase; padding: 10px 18px; text-decoration: none; color: var(--tp-ink); cursor: pointer; }
/* ⚑ `cursor: pointer` ist hier keine Zierde: auf cliches.html sind die Reiter
   `<a data-bild="1">` OHNE href — ohne href zeigt der Browser den Textcursor, und
   ein Schalter, der nicht wie einer aussieht, wird nicht gedrückt. Auf register und
   sammlung tragen die Reiter ein `href="#"` und hätten den Zeiger ohnehin; die Regel
   ändert dort nichts und deckt den href-losen Fall gleich mit ab. (05.09.2026) */
.sys-tabs a.active { box-shadow: inset 0 -6px 0 var(--tp-yellow); }
.sys-tabs a small { font-weight: 400; color: var(--tp-ink-muted); margin-left: 5px; letter-spacing: 0; font-variant-numeric: lining-nums tabular-nums; }

/* Aufbau und Facetten — Zwei Spalten: links die Filter, rechts die Treffer. Unter dem Umbruchpunkt wird daraus eine Spalte. */
/* ⚑⚑ `minmax(0, 1fr)` statt `1fr` — keine Feinheit, sondern der Grund, warum vier Seiten
   am 05.09.2026 auf dem Telefon waagrecht scrollten (druckerei 67 px, shop 44, register 31,
   sammlung 21). Ein Raster- oder Flex-Kind hat von sich aus `min-width: auto` und schrumpft
   NICHT unter seinen Inhalt; `1fr` heißt darum in Wahrheit `minmax(auto, 1fr)`, und eine
   lange Facettenbeschriftung macht die ganze Spalte breiter als das Fenster.
   `minmax(0, …)` hebt diesen Boden auf. Gemessen mit `_werkzeuge/ueberlauf.py`. */
.layout { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 40px; padding: 28px 56px 56px; }
.facets .group { margin-bottom: 26px; }
/* Die Facettenzeile — Kästchen · Name · Punktführung · Zahl.
   ⚑⚑ PERCY-ENTSCHEID 06.09.2026, Weg A: «Die Punktlinie kommt auf alle drei Listen.»
   Vorgelegt war ein Bogen mit drei Wegen (`_werkzeuge/facetten_entscheid.py`), weil
   dasselbe Bauteil dreimal anders gebaut war: sammlung.html mit Punktführung und an der
   GRUNDLINIE ausgerichtetem Kästchen (25 Zeilen), register.html (44) und cliches.html (15)
   mittig und ohne. Die Führungspunkte sind der Griff des Inhaltsverzeichnisses: sie führen
   das Auge vom Namen zur Zahl, und bei zweizeiligen Namen — davon hat sammlung die
   meisten — steht das Kästchen mit `baseline` bei der ERSTEN Zeile statt in der Mitte des
   Blocks. Diese Fassung ist wörtlich die bisherige von sammlung.html; sie ist damit die
   Fassung des Hauses geworden, nicht eine neu erfundene.

   ⚑ Der Entscheid war nicht nur CSS: `api.js` baute die Zeile an FÜNF Stellen in drei
   Formen — sammlung mit `.wert` und `.fuehrung`, register mit namenlosem `<span>`,
   cliches mit nacktem Text ohne jedes Element. Alle fünf tragen jetzt dieselbe Form.
   Die Zeilennummern stehen hier bewusst NICHT: sie sind ein Zeitstempel, keine Adresse.
   Zu finden mit `grep -n 'facet-row' api.js`. */
/* ⚑⚑ **Die Filterzeile ist ein `<button>`, seit 08.09.2026.** Vorher war sie ein nacktes
   `<div>` mit Klick-Horcher: mit der Tastatur unerreichbar, ohne Fokusring, und ein
   Vorleseprogramm sagte weder «Knopf» noch «ausgewählt». Das ging jahrelang durch, weil
   kein Prüfstand des Hauses danach fragt — und wog mit der Mehrfachwahl schwerer: eine
   Spalte, in der man Häkchen zusammenstellt, verspricht sichtbar mehr Bedienung, als sie
   ohne Maus hergab.
   Ein echter Knopf bringt alles mit: Tab-Reihenfolge, Enter und Leertaste lösen dasselbe
   `click` aus wie die Maus, und der Hausring aus `:focus-visible` greift von selbst.
   Der Zustand steht in `aria-pressed`, nicht nur in der Klasse `.on` — eine Farbe ist
   keine Auskunft.
   ⚑ Diese Zurücksetzung stand bis dahin nur in `shop.html`, weil dort der Baum schon
     Knöpfe hatte. Sie gehört zur Zeile, nicht zu einer Seite. */
.facet-row { display: flex; align-items: baseline; gap: 8px; padding: 6px 0; font-family: var(--tp-font-serif); font-size: var(--tp-t-klein); cursor: pointer;
             width: 100%; text-align: left; border: 0; background: none; color: var(--tp-ink); font-weight: inherit; }
/* ⚑ Der Ring liegt INNEN (`outline-offset: -2px`). Aussen ragte er in der 250-px-Spalte
   über den Satzspiegel hinaus, weil die Zeile die volle Breite nimmt. Farbe und Stärke
   bleiben die des Hauses (Percy-Entscheid 10.08.2026: gelb, 3 px, einfach). */
.facet-row:focus-visible { outline: 3px solid var(--tp-yellow); outline-offset: -2px; }
.facet-row .box { width: 14px; height: 14px; border: 1px solid var(--tp-ink); flex: 0 0 14px; position: relative; top: 2px; background: var(--tp-white); }
.facet-row.on .box { background: var(--tp-yellow); box-shadow: inset 0 0 0 3px var(--tp-white); }
/* Die Punktführung nimmt den freien Platz — darum braucht die Zahl kein `margin-left:auto`
   mehr. ⚑ Es bleibt trotzdem stehen: fehlt einer Zeile die Führung (ein Erbauer, den
   jemand später ergänzt, ein Rückfall-Text wie «keine Angabe erfasst»), steht die Zahl
   sonst mitten in der Zeile statt rechts. Eine Regel, die den halben Fall abfängt. */
.facet-row .fuehrung { flex: 1; border-bottom: 1px dotted var(--tp-border); position: relative; top: -4px; }
.facet-row .count { margin-left: auto; font-family: var(--tp-font-sans); font-size: var(--tp-t-label); color: var(--tp-ink-muted); font-variant-numeric: lining-nums tabular-nums; }
/* Eine Facette, die niemand mehr wählen kann, ohne alles andere zu verlieren.
   ⚑ `#9b988f` ist eine rohe Zahl zwischen `--tp-ink-muted` (#5f5d54) und `--tp-hairline`
   (#dad8cf) — sie kam mit dieser Regel aus sammlung.html mit und hat keinen Hausnamen.
   Nicht im Vorbeigehen ersetzt: das wäre eine Farbänderung, keine Zusammenlegung. */
.facet-row.leer .wert, .facet-row.leer .count { color: #9b988f; }

/* Werkzeugleiste — Über der Liste: links wie viele Treffer, rechts die Wege hinaus.
   ⚑ `align-items: baseline`, nicht `center`. Der Rückweg-Link trägt einen 2-px-Balken
   und ist damit 21 px hoch, die Trefferzahl nur 17 — mittig ausgerichtet sitzen die
   beiden Texte NICHT auf einer Linie. Gemessen am 05.09.2026: die Trefferzahl steht
   mit `baseline` 2 px höher (register, sammlung), 1 px (cliches), 0 (shop, hat keinen
   Link daneben); die Höhe der Leiste bleibt überall gleich. vergleich.html hatte es
   als einzige Seite schon richtig — die anderen vier zogen nach, nicht umgekehrt.
   Genau diese Sorte Versatz meldet `musterpruefung.py` als «Grundlinie versetzt».
   ⚑⚑ `.zurueck` gehört NICHT hierher, obwohl er in der Werkzeugleiste steht. Am
   05.09.2026 stand er kurz in diesem Block — und veränderte damit drei Seiten, die gar
   nicht dran waren: auf artikel.html wurde aus «← zurück zum Shop» statt eines
   unterstrichenen Fließtext-Links ein fetter Versalien-Knopf mit gelbem Balken (sieben
   gerechnete Eigenschaften anders, Höhe 28 -> 19 px), auf journal-beitrag.html und
   cliche.php dasselbe in klein. Der Name klingt nach Trefferliste, meint aber überall im
   Haus «ein Weg zurück» — und wie der aussieht, ist eine eigene Frage, die Percy noch
   nicht gestellt bekommen hat. Die Messung hat es nicht gefangen, weil sie die sechs
   Trefferlisten-Seiten prüfte und diese drei nicht kennt (jetzt schon). */
.toolbar { display: flex; justify-content: space-between; align-items: baseline; gap: 20px; margin-bottom: 18px; flex-wrap: wrap; }
.toolbar__links { display: flex; gap: 22px; flex-wrap: wrap; }
.toolbar__links a[hidden] { display: none; }
.hits { font-family: var(--tp-font-sans); font-size: var(--tp-t-label); letter-spacing: .06em;
  font-variant-numeric: lining-nums tabular-nums; }
.hits b { font-weight: 900; }

/* Hinweiskasten — Der Satz unter der Liste, der sagt, woher die Daten kommen. */
.api-note { margin-top: 8px; padding: 14px 16px; background: var(--tp-bg-soft); font-family: var(--tp-font-sans); font-size: var(--tp-t-label); line-height: 1.5; color: var(--tp-ink-muted); }

/* Ergebnistabelle — Die Trefferliste selbst.
   ⚑ `font-variant-numeric: lining-nums tabular-nums` an Trefferzahl, Facettenzähler
   und Tabellenzelle: nur sammlung.html hatte es, die anderen nicht. In einer Liste
   von Zahlen gehören die Ziffern untereinander, sonst tanzt die Spalte. Gemessen am
   05.09.2026 auf register und cliches: Spaltenbreiten, Tabellenbreite und die Breite
   der Trefferzeile bleiben aufs Pixel gleich — es kostet nichts. */
/* ⚑⚑ Eine Datentabelle schrumpft NICHT. `table.db` hat zwar `min-width: 0`, aber eine
   Tabelle ist so breit wie ihre Spalten es verlangen — bei 375 px waren das auf
   register.html 386 px, also 31 px über dem Fenster, und die ganze Seite rutschte unter
   dem Daumen weg. Text kleiner zu setzen wäre die falsche Antwort; eine Liste mit sechs
   Spalten IST breit. Sie bekommt darum ihren eigenen Rollbalken, statt die Seite zu
   schieben — dasselbe Muster, das `api.js` für die Vergleichsmatrix längst benutzt
   (`<div class="vscroll"><table …>`, api.js:1512 und :3587).
   ⚑ Nur `overflow-x` steht hier. maschine.html und vergleich.html geben ihrem `.vscroll`
   zusätzlich einen schwarzen Rahmen; die Trefferliste will keinen, ihre Zellen haben
   schon Linien. */
.vscroll { overflow-x: auto; }

table.db { width: 100%; border-collapse: collapse; }
table.db th { font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); text-transform: uppercase; letter-spacing: .14em; text-align: left; padding: 0 14px 8px; color: var(--tp-ink-muted); white-space: nowrap; border-bottom: 2px solid var(--tp-ink); }
table.db td { font-family: var(--tp-font-serif); font-size: var(--tp-t-klein); padding: 11px 14px; border-bottom: 1px solid var(--tp-border); vertical-align: baseline; font-variant-numeric: lining-nums tabular-nums; }
table.db tr:hover td { background: var(--tp-bg-soft); }
table.db a { color: var(--tp-ink); text-decoration: none; font-weight: 600; }
table.db a:hover { text-decoration: underline; text-decoration-color: var(--tp-yellow); text-underline-offset: 3px; }

/* Nebenauskunft in einer Zelle — leiser und kleiner als der Rest der Zeile.
   Stand bis 06.09.2026 zeichengleich in cliches.html UND sammlung.html.
   ⚑ `table.db td.muted` ist spezifischer als `table.db td` und gewinnt darum
   unabhängig von der Reihenfolge; cliches.html darf sein `vertical-align: middle`
   weiterhin darüberlegen (dort stehen Bildchen in den Zellen, keine Textzeilen). */
table.db td.muted { color: var(--tp-ink-muted); font-size: var(--tp-t-klein); }

/* Bild-Marke in der Trefferliste — «Bild» oder ein Strich, wenn keines da ist.
   Stand bis 06.09.2026 zeichengleich in cliches.html UND sammlung.html; auf cliches
   setzt `api.js` sie, auf sammlung steht sie im Markup. Zwei Orte, ein Bauteil. */
.bild-badge { font-family: var(--tp-font-sans); font-size: var(--tp-t-label); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; border: 1px solid var(--tp-black); padding: 1px 6px; }
.bild-badge.none { border-color: #ccc; color: #aaa; }

/* Unter 820 px: eine Spalte, engere Ränder, und die Tabelle zeigt nur noch die drei
   ersten Spalten — auf einem Telefon ist eine sechsspaltige Tabelle keine Liste mehr,
   sondern ein Zerrbild. */
@media (max-width: 820px) {
  .search-bar { margin: 0 20px; }
  .sys-tabs { margin: 20px 20px 0; flex-wrap: wrap; width: auto; }
  .layout { grid-template-columns: minmax(0, 1fr); padding: 24px 20px 40px; gap: 24px; }
  table.db th:nth-child(n+4), table.db td:nth-child(n+4) { display: none; }
}


/* ══ GÖNNERSEITEN ═══════════════════════════════════════════════════════════════════
   Fließtext · nummeriertes Band · Angabenliste · Personenliste · Beitrittsformular.

   ⚑⚑ Diese 27 Regeln standen am 05.09.2026 ZEICHENGLEICH auf drei Seiten —
   foerderverein.html, geschichte.html und stiftung.html. 81 Regelplätze für 27 Regeln.
   Gefunden mit `_werkzeuge/driftmass.py --liste`, das Doppelungen nach Seitenpaaren
   bündelt; es war die größte zusammenhängende Doppelung im ganzen Bestand.

   ⚑ Alle Namen hängen an einem `gs`-Vorfahren, auch die allgemein klingenden:
   `.gsleute .name`, `.gsform__gitter .weit`, `.gsform button.tp-btn`. Das ist der Grund,
   warum sie geteilt werden dürfen — `.name` allein steht auch auf artikel.html und
   index.html, `.weit` auf artikel und warenkorb, `.tp-btn` auf sechzehn Seiten. Wer hier
   je einen Selektor entankert, trifft sie alle. (Nachgezählt am 05.09.2026.)
   ══════════════════════════════════════════════════════════════════════════════════ */

/* Fließtext der Seite */
.gstext { max-width: var(--tp-content-max); }
.gstext h2 { font-size: var(--tp-t-3); margin: 0 0 12px; }
.gstext h3 { font-size: var(--tp-t-4); margin: 26px 0 8px; }
.gstext p { margin: 0 0 16px; }
.gstext em.jahr { font-style: normal; font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); letter-spacing: .1em; }

/* Nummeriertes Band */
.gsband { border-top: 1px solid var(--tp-border); padding-top: 28px; margin-top: 38px; }
.gsband--erst { border-top: 0; padding-top: 0; margin-top: 0; }

/* Angabenliste */
.gsangaben { border-top: 1px solid var(--tp-border); max-width: 720px; }
.gsangaben__row { display: grid; grid-template-columns: 200px 1fr; gap: 8px 24px; padding: 11px 0; border-bottom: 1px solid var(--tp-border); align-items: baseline; }
.gsangaben__row dt { font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); letter-spacing: .12em; text-transform: uppercase; color: var(--tp-ink-muted); margin: 0; }
.gsangaben__row dd { margin: 0; font-family: var(--tp-font-serif); font-size: var(--tp-t-5); }

/* Personenliste */
.gsleute { list-style: none; padding: 0; margin: 0; max-width: 720px; border-top: 1px solid var(--tp-border); }
.gsleute li { display: grid; grid-template-columns: 1fr auto; gap: 4px 24px; padding: 10px 0; border-bottom: 1px solid var(--tp-border); align-items: baseline; }
.gsleute .name { font-family: var(--tp-font-serif); font-size: var(--tp-t-5); }
.gsleute .funktion { font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); letter-spacing: .12em; text-transform: uppercase; color: var(--tp-ink-muted); text-align: right; }

/* Formular */
.gsform label { display: block; font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); text-transform: uppercase; letter-spacing: .12em; color: var(--tp-ink-muted); margin-bottom: 5px; }
.gsform input, .gsform select, .gsform textarea { font-family: var(--tp-font-serif); font-size: var(--tp-t-klein); color: var(--tp-ink); border: 1px solid var(--tp-border); background: var(--tp-white); padding: 11px 12px; width: 100%; box-sizing: border-box; border-radius: 0; appearance: none; }
.gsform textarea { min-height: 110px; resize: vertical; }
.gsform__gitter { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.gsform__gitter .weit { grid-column: 1 / -1; }
.gsform .topf { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.gsform button.tp-btn { cursor: pointer; }
.gsform__meld { font-family: var(--tp-font-sans); font-size: var(--tp-t-klein); margin-left: 12px; }

@media (max-width: 760px) {
  .gsangaben__row { grid-template-columns: 1fr; gap: 2px; }
  .gsform__gitter { grid-template-columns: 1fr; }
  .gsleute .funktion { text-align: left; }
  .gsleute li { grid-template-columns: 1fr; }
}


/* ══ OBJEKT- UND MASCHINENSEITE ═════════════════════════════════════════════════════
   Aufbau · Ortszeile · Bestandsmarke · Blätterband · Andernorts · Viertes Band · Paten.

   ⚑⚑ 42 Regeln standen am 05.09.2026 ZEICHENGLEICH in objekt.html UND maschine.html.
   Die beiden Seiten sind dasselbe Bauteil mit verschiedenem Inhalt — Objekt bzw.
   Maschine —, und jede Änderung war zweimal zu machen. Wer die zweite vergass, hatte
   zwei Detailseiten mit verschiedenem Satz; genau das war am 04.09. bei `.od-desc` der
   Fall. Gefunden mit `driftmass.py --liste`.

   ⚑ Drei Namen darin klingen allgemein und sind es auch — `.haus` steht auch auf
   register.html, `.ph` auf sechs Seiten, `.tp-rubric` auf fünfundzwanzig. Sie stehen hier
   NUR in zusammengesetzten Selektoren mit eigenem Vorfahren (`.bestandsmarke .haus`,
   `.od-gallery .ph`, `.andernorts .tp-rubric`). Wer einen davon entankert, trifft sie
   alle. (Nachgezählt am 05.09.2026.)
   ══════════════════════════════════════════════════════════════════════════════════ */

/* Aufbau der Detailseite */
.od { display: grid; grid-template-columns: 1.25fr 1fr; gap: 48px; padding: 24px 56px 48px; }
.od-gallery .ph { height: 520px; }
.band { padding: 0 56px 48px; }
.tps-ctas[hidden] { display: none; }

/* Ortszeile */
#ortszeile { font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); letter-spacing: .14em; text-transform: uppercase; color: var(--tp-ink); border-bottom: 2px solid var(--tp-black); padding-bottom: 8px; margin: 0 0 18px; }
#ortszeile a { color: inherit; text-decoration: none; }
#ortszeile a:hover { background: linear-gradient(transparent 58%, var(--tp-yellow) 58%); }
.ortszeile__zusatz { font-weight: 400; color: var(--tp-ink-muted); letter-spacing: .08em; }

/* Bestandsmarke */
.bestandsmarke { margin: 6px 0 26px; }
.bestandsmarke span { font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); text-transform: uppercase; letter-spacing: .14em; padding: 4px 9px; display: inline-block; }
.bestandsmarke .haus { background: var(--tp-yellow); }
.bestandsmarke .doku { border: 1px solid var(--tp-border); color: var(--tp-ink-muted); }
.doku-note { background: var(--tp-bg-soft); padding: 14px 18px; margin-top: 18px; font-family: var(--tp-font-serif); font-size: var(--tp-t-klein); line-height: 1.5; }
.doku-note b { font-family: var(--tp-font-sans); font-size: var(--tp-t-label); font-weight: 700; text-transform: uppercase; letter-spacing: .16em; display: block; margin-bottom: 5px; color: var(--tp-ink-muted); }

/* Blätterband — oben in der Brotkrumenzeile (Percy 13.09.2026, vorher am Seitenfuß).
   Die Zeile wird zur Flex-Reihe, sobald ein Band darin steht: Weg links, Blättern rechts,
   in derselben Schrift wie die Brotkrume. Ist der Platz zu knapp, bricht das Band als
   eigene Zeile unter den Weg und verteilt sich über die ganze Breite.
   ⚑ Der Weg steckt in `.tps-crumb__weg`, weil eine Flex-Reihe lose Textstücke («›») als
     eigene Glieder behandeln und die Leerzeichen daneben verschlucken würde.
   ⚑ Das Band bleibt Kind der Brotkrume, nicht ihr Geschwister: `knotenFehlt()` in api.js
     räumt alles NACH der Brotkrume weg — ein Geschwister dazwischen hätte die Suche
     danach an der falschen Stelle beginnen lassen. */
.tps-crumb:has(> .blaettern) { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 32px; }
.tps-crumb:has(> .blaettern) > .tps-crumb__weg { flex: 1 1 auto; }
.blaettern { display: flex; align-items: baseline; gap: 18px; margin: 0; min-width: 0; }
/* ⚑⚑ Der Platz des Bandes wird von Anfang an freigehalten, statt ihn erst zu nehmen, wenn
   die Antwort da ist. `blaetterBand()` setzt `hidden = false`, sobald die Schnittstelle
   geliefert hat — bis dahin war das Band `display: none`, und der Inhalt darunter sprang
   dem Lesenden unter dem Finger weg. Gemessen am 16.09.2026 auf maschine.html und
   objekt.html: **31 px bei 375, 32 px bei 800** (bis 820 px liegt das Band in einer eigenen
   Zeile) und **2 px bei 1400** — die zwei sind die gelbe 2-px-Unterstreichung des Namens,
   die die Brotkrumenzeile höher macht.
   Darum bleibt das Band im Fluss und wird nur unsichtbar. Das `hidden`-Attribut bleibt
   gesetzt: Vorleseprogramme überspringen es weiterhin, es ist wirklich noch nicht da.
   Das geschützte Leerzeichen gibt der leeren Zeile ihre Höhe (ohne Inhalt wäre sie null
   hoch und reservierte nichts), der durchsichtige Rahmen die zwei Pixel der Unterstreichung.
   ⚑⚑ Der Rest zwischen 820 und 1240 px (Percy-Entscheid 16.09.2026, Frage 4): leer passt das
   Band neben die Brotkrume, gefüllt rutschte es in eine zweite Zeile. Auf vorschau im echten
   Ladeverlauf gemessen waren das +29 px bei 1000 px auf ALLEN vier Datensätzen — nicht nur
   «bei langen Namen», wie hier zuerst stand. Breit bricht die Reihe darum nicht mehr um
   (`nowrap` unten): Brotkrume und Band teilen sich die Zeile, und wird es eng, kürzen sich
   beide Namen mit «…». Schmal (bis 820 px) hat das Band ohnehin seine eigene Zeile. */
.blaettern[hidden] { display: flex; visibility: hidden; }
.blaettern[hidden]::before { content: "\00a0"; border-bottom: 2px solid transparent; }
.blaettern__seite { display: inline-flex; align-items: baseline; gap: 6px; flex: 0 1 auto; min-width: 0; color: var(--tp-ink); text-decoration: none; }
.tps-crumb .blaettern__seite { color: var(--tp-ink); }
/* Abschneidegrenze, keine Satzbreite: 30ch zeigen fast jeden Maschinennamen ganz. Wird es
   eng, schrumpft der Name mit «…» (flex + min-width 0), bevor das Band über den Rand geht. */
.blaettern__name { min-width: 0; max-width: 30ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border-bottom: 2px solid var(--tp-yellow); }
.blaettern__seite:hover .blaettern__name { background: var(--tp-yellow); }
.blaettern__pfeil { flex: none; color: var(--tp-ink-muted); }
.blaettern__pos { flex: none; color: var(--tp-ink-muted); white-space: nowrap; }
@media (max-width: 820px) {
  .blaettern { flex: 1 1 100%; justify-content: space-between; gap: 12px; }
}
@media (min-width: 821px) {
  .tps-crumb:has(> .blaettern) { flex-wrap: nowrap; }
  /* ⚑ Wird es eng, gibt die Brotkrume nach, nicht das Band: ihr Name steht als Titel direkt
     darunter, die Nachbarnamen stehen nur hier. Proportional geschrumpft blieb bei 900 px vom
     Nachbarn «Alden» nur «Al…». */
  .tps-crumb:has(> .blaettern) > .blaettern { flex-shrink: 0; }
}
/* Zwischen 821 und 1240 px teilen sich Brotkrume und Band eine schmale Zeile. 22ch statt 30ch
   hält das Band auch mit zwei langen Nachbarnamen unter der Satzbreite (gerechnet: 2 × 22ch +
   Pfeile + «126 von 500» ≈ 540 px bei 709 px Satzbreite ab 821 px). */
@media (min-width: 821px) and (max-width: 1240px) {
  .blaettern__name { max-width: 22ch; }
}
/* Auf dem Handy trägt der Name, nicht die Stelle: «126 von 500» weicht, damit beide Namen
   lesbar bleiben. */
@media (max-width: 480px) {
  .blaettern__pos { display: none; }
}

/* Andernorts */
.andernorts { margin: 22px 0 0; }
.andernorts .tp-rubric { color: var(--tp-ink-muted); display: block; margin-bottom: 6px; }
.andernorts__liste { list-style: none; margin: 0; padding: 0; }
.andernorts__liste li { padding: 7px 0; border-top: 1px solid var(--tp-border); }
/* Der kleine Zweitlink zur Website eines fremden Hauses — der Name selbst führt auf
   `standort.html` (18.09.2026); die Website ist eine Zugabe, kein zweiter Hauptweg. */
.andernorts__web { font-family: var(--tp-font-sans); font-size: var(--tp-t-label); color: var(--tp-ink-muted); text-transform: none; letter-spacing: 0; }
/* Die Kästen eines fremden Hauses: Grad und Schnitt links, Vollständigkeit rechts (`kaestenTabelleHtml`
   in api.js). Hier, nicht in den Seiten, weil schriften.html UND standort.html sie zeichnen (18.09.2026). */
.andernorts__grade { border-collapse: collapse; width: 100%; margin: 4px 0 6px; }
.andernorts__grade td { border-top: 1px solid var(--tp-border); padding: 7px 8px 7px 0; font-family: var(--tp-font-serif); font-size: var(--tp-t-klein); vertical-align: top; }
.andernorts__vollzelle { white-space: nowrap; text-align: right; padding-left: 12px; font-family: var(--tp-font-sans); font-size: var(--tp-t-label); color: var(--tp-ink-muted); }
.andernorts__voll { color: var(--tp-ink); }
.andernorts__quelle { font-family: var(--tp-font-sans); font-size: var(--tp-t-label); color: var(--tp-ink-muted); }
.andernorts__quelle a { color: var(--tp-ink-muted); }
@media (max-width: 640px) {
  .andernorts__grade td { display: block; padding: 7px 0 3px; }
  /* Auf dem Telefon steht die Vollständigkeit unter ihren Graden — ohne eigenen Strich, sonst
     las sie sich als eigene Zeile, und eine leere Zelle zog einen zweiten Strich. */
  .andernorts__grade td.andernorts__vollzelle { text-align: left; padding: 0 0 7px; border-top: 0; }
  .andernorts__vollzelle:empty { display: none; }
}
.feldlink { color: inherit; text-decoration: none; border-bottom: 2px solid var(--tp-yellow); }
.feldlink:hover { background: var(--tp-yellow); }

/* Stammbaum im Datenblatt (renderStammbaum in api.js, 11.09.2026). Die Reihe fliesst als
   Zeile und bricht um wie Text; die Pfeile sind Schmuck und haben darum einen leeren
   Alternativtext (`/ ""`) — sonst liest ein Vorleseprogramm «Pfeil nach rechts» zwischen
   jedem Namen. Die erste `content`-Zeile ist der Rückfall für Browser ohne diese Form.
   ⚑ Keine eigene Schriftgrösse: das Jahr ist leiser durch die FARBE, nicht kleiner
   (HAUSORDNUNG §2). */
.stamm { list-style: none; margin: 0; padding: 0; display: inline; }
.stamm li { display: inline; }
/* Links vom Pfeil steht das Leerzeichen zwischen den Gliedern (die Bruchstelle), darum
   nur rechts ein Abstand — sonst stünde der Pfeil nach links weiter weg als nach rechts. */
.stamm li + li::before { content: "\2192"; content: "\2192" / ""; margin: 0 .3em 0 0; color: var(--tp-ink-muted); }
.stamm strong { font-weight: 600; }
.stamm__jahr, .stamm__oder { color: var(--tp-ink-muted); }

/* Viertes Band */
#band4 { margin-top: 26px; padding-top: 16px; border-top: 2px solid var(--tp-black); }
.band4__titel { font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); text-transform: uppercase; letter-spacing: .14em; color: var(--tp-ink-muted); margin: 0 0 10px; }
.band4__liste { list-style: none; padding: 0; margin: 0; }
.band4__liste li { padding: 7px 0; border-bottom: 1px solid var(--tp-border); font-family: var(--tp-font-serif); font-size: var(--tp-t-5); }
.band4__liste li:last-child { border-bottom: 0; }
.band4__liste a { text-decoration: none; }
.band4__liste a:hover { background: linear-gradient(transparent 58%, var(--tp-yellow) 58%); }

/* Patenschaft */
.paten { background: var(--tp-bg-soft); padding: 18px 20px; margin-top: 4px; }
.paten h4 { margin: 0 0 6px; }
.paten h4 em { font-style: normal; background: var(--tp-yellow); padding: 1px 6px; }
.paten p { font-family: var(--tp-font-serif); font-size: var(--tp-t-klein); line-height: 1.5; margin: 0; }

@media (max-width: 820px) {
  /* ⚑⚑ Eine Spalte UND schmalerer Rand. Bis 06.09.2026 stand diese Regel zweimal
     verschieden: maschine.html senkte den Rand auf 20 px, objekt.html nur die Spaltenzahl
     — und behielt 56 px. Von 375 px blieben dort 263 übrig, und die Seite lief um 3 px
     hinaus. Zwei Seiten, die dasselbe Bauteil sind, mit zwei verschiedenen Antworten auf
     dieselbe Frage: genau die Drift, gegen die dieser Block gebaut ist.
     `minmax(0, …)` wie überall — sonst schrumpft das Rasterkind nicht unter seinen Inhalt. */
  .od { grid-template-columns: minmax(0, 1fr); padding: 24px 20px 32px; gap: 28px; }
  /* ⚑ `gap: 28px` stand nur auf maschine.html, objekt.html behielt 48. Untereinander
     gestapelt braucht es weniger Luft als nebeneinander — die 28 sind die überlegtere
     Fassung, und zwei Seiten desselben Bauteils sollen dieselbe Antwort geben. */
  .band { padding: 0 20px 40px; }
  /* `.blaettern` stand hier als Raster für das Band am Seitenfuß. Seit 13.09.2026 steht es oben
     in der Brotkrume, seine Regeln stehen beim Bauteil selbst («Blätterband»). */
}


/* ══ SHOP UND FORMULARE — die kleinen geteilten Bauteile ════════════════════════════
   Vier Regeln, die am 06.09.2026 zeichengleich auf je zwei Seiten standen. Gefunden
   mit `_werkzeuge/driftmass.py --liste`; jede war die Vorstufe zur Drift — beim
   nächsten Anfassen wäre eine davon weggelaufen, ohne dass etwas gemeldet hätte.

   ⚑ Vor dem Umzug über den GANZEN Bestand gegriffen, wer die Namen sonst noch trägt
   (Lehre vom 05.09.2026, als `.zurueck` drei ungemessene Seiten veränderte). Ergebnis:
   · `.weit` steht auch auf foerderverein.html und stiftung.html — dort aber OHNE `.feld`
     daneben, `.feld.weit` trifft sie also nicht.
   · `knopf` fand sich in `kopfzeile.js` — das ist `tp-nav__knopf`, ein anderer Name.
     Klassennamen sind ganze Wörter; ein Teilstrichtreffer ist kein Treffer.
   · `.honig` schreibt auch `kursanmeldung.js` (Kursanmeldung auf kurse.html) und bringt
     dort seine eigene, auf `.kanm` beschränkte Fassung mit — wortgleich mit dieser.
     Sie bleibt vorerst stehen: sie gehört zu einem Bauteil, das sich selbst mitbringt.
   ══════════════════════════════════════════════════════════════════════════════════ */

/* Lagerstand im Shop — die Farbe IST die Aussage, darum darf sie nicht driften.
   Bis 06.09.2026 zeichengleich in artikel.html und shop.html.
   ⚑ Die Farbe heisst `--tp-alert` (ds/colors_and_type.css). Bis zum 07.09.2026 stand
   hier ihre Zahl `#a8412b`,
   hier als Zahl geschrieben. Sie steht so an 14 Stellen im Bestand — das ist ein
   eigener Gang und nicht Teil dieses Umzugs; verändert würde sonst mehr als gemessen. */
.lager--knapp { color: var(--tp-alert); font-weight: 700; }
.lager--aus { color: var(--tp-ink-muted); }

/* Honigtopf — das Feld, das nur ein Roboter ausfüllt. Muss unsichtbar sein und trotzdem
   im Formular stehen; `display:none` nähme ihm die Wirkung, darum aus dem Bild geschoben.
   Bis 06.09.2026 zeichengleich in besuch.html und warenkorb.html. */
.honig { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Formularfeld über die ganze Zeile, und der abgeschaltete Knopf.
   Bis 06.09.2026 zeichengleich in artikel.html und warenkorb.html.
   ⚑⚑ `.knopf` gibt es seit dem 06.09.2026 nicht mehr. Hier stand bis dahin
   `.knopf:disabled`, und darüber der Satz, die Größenfrage «11/22 gegen 13/26» müsse
   jemand entscheiden. Percy hat sie entschieden, und zwar größer als die Frage war:
   «Auf den Hausknopf umstellen — .knopf fällt weg.» Beide Seiten tragen jetzt
   `.tp-btn tp-btn--yellow`, und der abgeschaltete Zustand steht in `ds/site.css`, wo
   der Hausknopf wohnt. */
.feld.weit { grid-column: 1 / -1; }

/* Zwei Spalten in einem Formular — Vorname/Name, PLZ/Ort.
   Stand bis 06.09.2026 auf artikel.html und warenkorb.html; artikel hängt einen unteren
   Abstand an, den warenkorb nicht will — der bleibt darum dort stehen.
   ⚑ `minmax(0, 1fr)` statt `1fr`: ein Rasterkind hat von sich aus `min-width: auto` und
   schrumpft nicht unter seinen Inhalt — bei Eingabefeldern mit ihrer eingebauten
   Mindestbreite von rund 170 px ist das die häufigste Überlaufursache im Haus. */
.feldgitter { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px 14px; }

/* Kopfzeile eines Bandes auf der Objekt- und der Maschinenseite — Titel links, Zusatz
   rechts. Stand bis 06.09.2026 zweimal; objekt.html fehlten `gap` und `flex-wrap`.

   ⚑⚑ DAS IST KEINE STILLE ÄNDERUNG — auf objekt.html bei 375 px sieht man sie, und sie
   ist eine Verbesserung. Nachgemessen am 06.09.2026, dieselbe Seite in beiden Zuständen:
     vorher (`nowrap`): die Rubrik «Matrizensätze · Linotype» steht in 130 px Breite und
       ist 62 px hoch — sie bricht dreizeilig; der Verweis daneben in 151 px, 34 px hoch.
       Zwei schmale Spalten aus zerbrochenem Text.
     nachher (`wrap`):  Rubrik 188 px breit, 31 px hoch; der Verweis darunter 217×17.
       Sauber gestapelt, 6 px höher insgesamt (62 → 68).
   maschine.html hatte beides schon; objekt.html war der Ausreißer. Sechs Pixel mehr Höhe
   für ungebrochene Zeilen auf dem Telefon.

   Der untere Abstand bleibt bei den Seiten (12 px · 14 px) — 2 px, die niemand entschieden
   hat, und die ich nicht im Vorbeigehen entscheide. */
/* ⚑ Der untere Abstand am 06.09.2026 dazu: er stand als 12 px (maschine) gegen 14
   (objekt) in den Seiten. Percy hat den Befund ohne Veto durchgehen lassen — 12 px,
   die Fassung von maschine.html, weil sie die jüngere ist und schon in der geteilten
   Regel stand. Zwei Pixel, die mit blossem Auge niemand sieht; sie kosteten nur die
   Frage, welcher gilt. */
.band__head { display: flex; justify-content: space-between; align-items: baseline; gap: 20px; flex-wrap: wrap; margin-bottom: 12px; }

/* Der leere Zustand einer Detailseite — «Diese Nummer gibt es nicht».
   ⚑⚑ PERCY-ENTSCHEID 06.09.2026: «56 px überall — die Flucht zur Kopfzeile stimmt.»
   Der Kasten stand dreimal: cliche.php und station.php mit 56 px Seitenrand, der ganze
   Ersatzteilkatalog (vier Seiten über katalog_rahmen.php) mit 46. Sichtbar war es an der
   Flucht: 56 px ist das Randmass des Hauses — Trefferliste, Abschnitt und Suchzeile stehen
   alle darauf, und das Logo der Kopfzeile ebenso. Mit 46 stand der Text zehn Pixel weiter
   links als das Logo darüber. Vorgelegt mit zwei Bildern (`_werkzeuge/drift_entscheid.py`).
   ⚑ Nicht zu verwechseln mit `.leer`/`.nix` — das sind die leeren TREFFERLISTEN weiter
   oben in diesem Blatt. Hier geht es um eine Detailseite, die es nicht gibt. */
.leerzustand { padding: 60px 56px 80px; max-width: 720px; }

/* Die Fehlermeldung im Formular.
   ⚑⚑ PERCY-ENTSCHEID 06.09.2026: «15 px überall — die Fehlermeldung ist ein Satz.»
   Sie stand zweimal: warenkorb.html mit 15 px und Zeilenabstand 1,5, artikel.html mit
   11 px. Im Stilblatt des Hauses heißt 11 px «Versal-Label in Nunito Sans» und 15 px
   «Tabellenzeilen, Nebenangaben, Legenden» — eine Meldung wie «Es fehlen noch: Anrede,
   E-Mail» ist ein Satz zum Lesen, kein Label. Genommen ist damit die Fassung von
   warenkorb.html, wörtlich.
   ⚑ Die Farbe heisst `--tp-alert` (ds/colors_and_type.css) — bis zum 07.09.2026 stand
   hier ihre Zahl.
   Sie steht so an 14 Stellen im Bestand; das ist ein eigener Gang, nicht Teil dieses
   Umzugs — hier würde sonst mehr verändert als gemessen.
   ⚑ In warenkorb.html stand an einer Stelle zusätzlich `style="font-size:var(--tp-t-klein)"`
   am Element. Das Attribut sagte dasselbe wie die Regel und ist mit weg — ein Attribut,
   das eine Regel wiederholt, überlebt jede spätere Änderung an der Regel. */
.fehlmeldung { font-family: var(--tp-font-sans); font-size: var(--tp-t-klein); color: var(--tp-alert); margin-top: 12px; line-height: 1.5; }

/* ══ DATENBLATT einer Artikel- oder Teileseite ══════════════════════════════════════
   Bezeichnung links, Wert rechts, Haarlinie darunter. Stand bis 06.09.2026 zeichengleich
   in artikel.html und teil.php.

   ⚑⚑ Es stand zweimal, weil es NICHT geteilt werden durfte: so-gut-wie-alt.html trug
   `.daten` mit zwanzig <div> darin und meinte damit etwas ganz anderes — ein geteiltes
   `.daten div` hätte die alle zu Flex-Zeilen mit Trennlinie und 11-px-Schrift gemacht.
   `driftmass.py` warnte an der Zeile. Seit der Umbenennung dort zu `.maschinendaten`
   (Namenskollision, Portion 2 am 06.09.2026) trägt den Namen nur noch, wer das Bauteil
   auch meint — nachgezählt über den ganzen Bestand: artikel.html und teil.php, sonst
   niemand. Erst DANN durfte es hierher.
   ⚑ Das ist der eigentliche Gewinn einer Umbenennung: nicht der schönere Name, sondern
   dass Teilen danach überhaupt möglich wird.

   ⚑ `gap: 20px` ist ein Sicherheitsnetz, kein Abstand, den man sieht: die kleinste
   gemessene Lücke zwischen Bezeichnung und Wert ist 101 px, auch bei 375 px. Er greift
   erst, wenn eine Angabe einmal lang wird.
   ⚑ `.daten dd { margin: 0; text-align: right }` bleibt in teil.php — artikel.html hat
   keine <dd>-Regel, und ob es eine bekommen soll, ist eine Frage und kein Umzug. */
.daten { margin-top: 26px; border-top: 1px solid var(--tp-border); }
.daten div { display: flex; justify-content: space-between; padding: 7px 0; border-bottom: 1px solid var(--tp-border); font-family: var(--tp-font-sans); font-size: var(--tp-t-label); gap: 20px; }
.daten dt { color: var(--tp-ink-muted); }

/* ══ DIE MARKE ══════════════════════════════════════════════════════════════════════
   Ein kleines Versal-Schild neben einem Eintrag: «im Typorama», «nur dokumentiert»,
   «vor Ort», «12 Teile».

   ⚑⚑ PERCY-ENTSCHEID 06.09.2026: «Eine Marke mit Varianten — registers System für alle.»
   Vorher stand sie viermal verschieden: register.html hatte als einzige ein System
   (Grundform ohne Grund, dazu gelb bzw. umrandet-grau), vergleich.html dieselbe Grundform
   mit FEST eingebautem Gelb, so-gut-wie-alt.html eine schwarz-weiße, der Ersatzteilkatalog
   eine nur umrandete ohne Fettung und ohne Versalien. Vier Schilder, ein Name.

   ⚑ Die Varianten heißen `--haus`/`--doku`/… und nicht `--gelb`/`--umrandet`, obwohl im
   Bogen Farbnamen als Beispiel standen: ein Name soll sagen, WAS die Marke behauptet,
   nicht welche Farbe sie gerade hat. Wer das Gelb einmal ändert, muss sonst auch den
   Namen ändern — oder er lügt.
   ⚑ Und sie tragen `--`, keine Einzelwörter. register hatte `.haus`, `.doku`, `.offen`
   als eigenständige Klassen; genau solche Allerweltsnamen waren heute den ganzen Tag das
   Problem (`.weg`, `.karte`, `.nr`). `api.js` ist an drei Stellen nachgezogen.

   ⚑ `--doku` und `--offen` sehen gleich aus und bleiben trotzdem zwei Namen: sie sagen
   Verschiedenes («nur dokumentiert» / «offen»), und was gleich AUSSIEHT, muss nicht
   gleich BLEIBEN. */
.marke { font-family: var(--tp-font-sans); font-weight: 700; font-size: var(--tp-t-label); text-transform: uppercase; letter-spacing: .12em; padding: 3px 7px; display: inline-block; white-space: nowrap; }
.marke--haus { background: var(--tp-yellow); }
.marke--doku, .marke--offen { border: 1px solid var(--tp-border); color: var(--tp-ink-muted); }
.marke--schwarz { background: var(--tp-black); color: var(--tp-white); }
.marke--leise { background: var(--tp-bg-soft); color: var(--tp-ink-muted); }

/* Das Formularfeld — Beschriftung über dem Eingabefeld.
   ⚑⚑ PERCY-ENTSCHEID 06.09.2026: 5 px, kein Veto gegen den Vorschlag.
   Es stand auf vier Seiten und war überall dieselbe Flex-Spalte; der einzige Unterschied
   war der Abstand — 4 px auf artikel und warenkorb, 5 px auf besuch und druckerei.
   Zwei gegen zwei, und niemand hatte es entschieden. Für die 5 sprach: besuch.html und
   druckerei.html tragen die längsten Formulare des Hauses, und wo viele Felder
   untereinander stehen, hilft ein Pixel mehr Luft beim Zuordnen.

   ⚑ vergleich.html trägt `class="feld"` und hatte KEINE Regel dafür — die zwei
   Auswahlfelder dort standen als Block. Mit dieser Regel werden sie zur Flex-Spalte wie
   überall sonst. Vorher genannt, gemessen und für richtig befunden; es ist eine sichtbare
   Änderung auf einer Seite, die nicht zur Frage gehörte.
   ⚑ typenschilder.php hat ein eigenes `.feld` mit ganz anderer Bedeutung und ist NICHT
   betroffen: es lädt shared.css gar nicht (Bildschirm-Werkzeug nach CI §09, siehe INSELN
   im Kopf von `_werkzeuge/driftmass.py`). */
.feld { display: flex; flex-direction: column; gap: 5px; }
