/* Das Buendel `liste` — ein Typ, alle Varianten.

   REGELN, DIE HIER GELTEN (CLAUDE.md, 3. 9.):
   - Nur Properties, nie Zahlen. Das Theme bringt Raster, Abstaende, Rollen und
     Flaechen mit; dieser Block erfindet nichts, er ordnet an.
   - Blockeigenes IMMER mit Rueckfall (--buehne-hoehe: var(--buehne-hoehe, 70svh)).
     Jede fehlende Groesse gehoert auf die Notizliste fuers Theme-Config.
   - Umbrueche intrinsisch, keine eigenen Breakpoints. Wo doch eine Schwelle
     noetig ist: @container, nie @media — ein Block kennt nicht die
     Seitenbreite, nur seinen Platz. */

/* REGISTRIERTE LAENGEN (17. 9., die Zunge stand schief): Eine Custom
   Property mit `em` wird erst DORT gerechnet, wo sie benutzt wird — die
   Fuellung (::after) rechnet mit der Schrift der Kachel, das Zungen-SVG mit
   seiner eigenen. Seit die Kachel ein Container ist, unterscheiden sich die
   beiden (gemessen: 39.6 zu 36.2 px), und Fuellung und Strich lagen
   auseinander. Als `<length>` registriert wird der Wert an der Kachel
   ausgerechnet und als PIXEL vererbt. Chrome 85, Safari 16.4, Firefox 128. */
@property --zunge-hoehe  { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --zunge-breite { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --blase-r      { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --zunge-abstand { syntax: '<length>'; inherits: true; initial-value: 0px; }

@layer versatile {

  /* == Gemeinsames ========================================================= */
  .liste {
    --spalten: 12;
    /* Die Rinne wird nie gesetzt, nur gestreckt: ein Faktor auf die geerbte,
       0 bis 1. Faktoren multiplizieren sich nicht — der Bezug ist immer die
       Rinne der Groessenleiter (ebenen.md, 30. 8.). */
    --rinne: calc(var(--gutter) * var(--rinne-faktor, 1));
    margin: 0;
    padding: 0;
    list-style: none;
    /* DIE BREITE AUSDRUECKLICH (Safari 17.6, gemessen 4. 9.): Jede Liste
       traegt `cq`, also `container-type: inline-size` — und die Rasterformen
       sind zugleich `display: grid`. Beides am selben Element laesst Safari
       die Inline-Groesse auf NULL fallen; Chrome nicht. Die Folge war still
       und gross: `minmax(min(var(--mindest), 100%), 1fr)` wurde damit zu
       `minmax(0, 1fr)`, jede Kachel war 0 px breit, und der Inhalt lag
       uebereinander. Gemessen an vier Vorstandskacheln: alle `0x193` an
       derselben Stelle.

       `100%` und nicht `auto`: Ein Container mit `contain: inline-size` darf
       seine Breite nicht mehr aus dem Inhalt nehmen — er braucht eine, die
       von aussen kommt. */
    inline-size: 100%;
  }
  /* EIN RASTER IST KEIN CONTAINER (Safari 17.6, gemessen 4. 9. am lebenden
     Objekt).

     Jede Liste traegt `cq` — `container-type: inline-size` —, damit die
     Schrift ihrer Eintraege am Platz gemessen wird. Traegt dasselbe Element
     zugleich `display: grid`, loest Safari die `1fr`-Tracks mit NULL auf:
     Die Spaltenzahl stimmt, jede Spalte ist 0 px breit, und alle Kacheln
     liegen uebereinander. Gemessen an der Vorstandsliste: Behaelter 694 px,
     `grid-template-columns: 0px 0px 0px`, Kacheln `0x193`.

     Zwei Auswege wurden gemessen und verworfen: `100cqi` statt `100%` im
     min() aendert nichts (das `1fr` bricht, nicht der Minimalwert), und ohne
     `min()` bleibt es ebenso. Es hilft nur, die beiden Rollen zu trennen.

     PREIS: Die Eintraege messen ihre Schrift jetzt am naechsthoeheren
     Container — dem Abschnittsband statt der Liste. Bei einer Liste, die die
     volle Breite des Bandes nimmt, ist das dasselbe; bei einer schmalen
     etwas groesser. Das ist ein sichtbarer, kleiner Unterschied gegen eine
     unbenutzbare Seite.

     RICHTIG waere ein eigener Behaelter INNEN — `<ul class="cq"><div
     class="raster">…` —, damit Container und Grid getrennte Elemente sind.
     Das ist eine Markup-Aenderung und gehoert in die Blockphase, wo das
     Buendel ohnehin neu geschnitten wird. */
  /* MIT `.cq` DAVOR, sonst greift die Regel nicht (gemessen 4. 9.):
     `.cq { container-type: inline-size }` steht in versatile.css, und die
     wird ZULETZT geladen (fallstricke.md). Bei gleicher Spezifitaet gewinnt
     die spaetere — diese Regel war ohne den zusaetzlichen Klassennamen
     wirkungslos, und der Fix sah aus, als haette er nichts geaendert. */
  .cq.liste--raster,
  .cq.liste--fakten,
  .cq.liste--masonry { container-type: normal; }

  .eintrag { min-inline-size: 0; }
  .eintrag__titel,
  .eintrag__untertitel,
  .eintrag__quelle { margin: 0; }
  .eintrag__text > * + * { margin-block-start: var(--flow, 1em); }
  .eintrag__text p { max-inline-size: min(var(--measure), 100%); }
  .eintrag__knopf { margin: 0; }

  /* Der Satz gilt fuer den GANZEN Eintrag — wer zentriert, zentriert Titel und
     Text zusammen. Logische Richtungen, keine Himmelsrichtungen. */
  .liste--satz-mitte .eintrag { text-align: center; justify-items: center; }
  .liste--satz-ende  .eintrag { text-align: end;    justify-items: end; }
  .liste--satz-mitte .eintrag__text p,
  .liste--satz-mitte .eintrag__text { margin-inline: auto; }

  /* == Das Medium und seine Formatleiter =================================== */
  /* Mobile first: xs ist die Grundstufe, jede groessere erbt, bis sie selbst
     etwas setzt. Die Kette steht in den var()-Rueckfaellen, nicht in Regeln —
     dadurch bleibt "leer heisst wie darunter" auch dann wahr, wenn eine Stufe
     uebersprungen wird. */
  /* DIE STUFE WIRD AN DER KACHEL AUFGELOEST (J. S., 17. 9.): Im Teppich ist
     die Kachel selbst ein Container (Schrift misst die Kachel). Eine
     @container-Regel am Medium saehe dann die KACHEL, nicht den Abschnitt —
     eine schmale Kachel fiele auf xs zurueck. Die Kachel misst ihren
     Vorfahren, also den Abschnitt, und reicht `--v-jetzt` hinunter. */
  .eintrag { --v-jetzt: var(--v-xs, auto); }
  @container (min-width: 480px)  { .eintrag { --v-jetzt: var(--v-s,  var(--v-xs, auto)); } }
  @container (min-width: 768px)  { .eintrag { --v-jetzt: var(--v-m,  var(--v-s,  var(--v-xs, auto))); } }
  @container (min-width: 1024px) { .eintrag { --v-jetzt: var(--v-l,  var(--v-m,  var(--v-s, var(--v-xs, auto)))); } }
  @container (min-width: 1440px) { .eintrag { --v-jetzt: var(--v-xl, var(--v-l,  var(--v-m, var(--v-s, var(--v-xs, auto))))); } }
  .eintrag__medium {
    position: relative;
    overflow: hidden;
    aspect-ratio: var(--v-jetzt, auto);
  }
  .eintrag__medium img {
    display: block;
    inline-size: 100%;
    /* OHNE DAS wird das height-Attribut zur festen Hoehe und verzerrt —
       gemessen 3. 9., Chrome 152. */
    block-size: auto;
  }
  /* Sobald ein Verhaeltnis erzwungen ist, fuellt das Bild den Kasten und der
     Fokuspunkt entscheidet, was beim Beschneiden stehenbleibt. */
  .eintrag__medium[style*="--v-"] img,
  .eintrag__medium[style*="--v-"] video {
    block-size: 100%;
    object-fit: cover;
    object-position: var(--fokus, 50% 50%);
  }
  /* EINE ZEICHNUNG WIRD NICHT BESCHNITTEN (J. S., 17. 9., der Schnitt in
     3:4): Ein SVG passt sich ins Format ein statt es zu fuellen — ein Plan,
     dem oben und unten fehlt, ist kein Plan mehr. Bei Fotos bleibt cover. */
  .eintrag__medium[style*="--v-"] img[src$=".svg"] { object-fit: contain; }
  /* EINGEBETTETE ZEICHNUNG (J. S., 17. 9.): fuellt den Kasten, wird
     eingepasst (viewBox, meet), nimmt die Schriftfarbe der Flaeche. Die
     Strichstaerke ist UNABHAENGIG von der Kachelgroesse — non-scaling-stroke
     misst in Bildschirmpixeln — und laeuft mit dem Fenster: 0.25 pt schmal,
     mit der Breite mehr, hoechstens 1 pt. Ein Wert fuer alle Plaene. */
  .medium__svg { inline-size: 100%; block-size: 100%; }
  .medium__svg svg {
    display: block;
    inline-size: 100%;
    block-size: 100%;
    color: inherit;
    /* KEINE FUELLUNG (gemessen 17. 9.): versatile.css gibt jedem SVG eine
       Fuellfarbe, und eine eingebettete Zeichnung erbt sie — der Schnitt
       lief schwarz voll. Ein Plan ist Strich, keine Flaeche; was in der
       Datei selbst eine Fuellung traegt (Illustrator-Export), behaelt sie. */
    fill: none;
    stroke: currentColor;
    /* FEST, NICHT MITLAUFEND (J. S., 24. 9.): Hier stand `clamp(0.25pt,
       0.05vw, 1pt)` — und `vw` heisst, dass der Strich mit dem Fenster
       dicker wird. `non-scaling-stroke` haelt ihn zwar unabhaengig von der
       KACHELGROESSE, aber nicht von dieser Rampe. Ein Plan soll seine
       Strichstaerke behalten, sonst wird aus der Zeichnung bei jeder Breite
       eine andere. 0.5pt ist die feinste Staerke, die auf einem normalen
       Bildschirm noch sauber steht; die Stufen darueber sind ihr Doppeltes
       und Dreifaches. */
    --plan-strich: 0.3pt;
  }
  /* Strichfarbe und -staerke der eingebetteten Plaene stehen AUSSERHALB der
     Schicht, ganz am Ende dieser Datei — siehe dort, warum. */
  .eintrag__medium[style*="--v-"] .medium__svg { position: absolute; inset: 0; }
  /* `mischung` am Eintrag (liste.html.twig): Bild und Video mischen sich mit
     der Flaeche dahinter. Ohne Angabe `normal`. */
  .eintrag__medium > img,
  .medium__video { mix-blend-mode: var(--medium-mischung, normal); }

  /* == Video im Medium ===================================================== */
  /* Ein Video verhaelt sich wie ein Bild: fuellt seinen Kasten, das Verhaeltnis
     kommt vom Eintrag, der Fokuspunkt entscheidet beim Beschneiden. */
  .medium__video {
    display: block;
    inline-size: 100%;
    block-size: auto;
    background: var(--surface, #000);
  }
  /* DER LEISE SCHALTER (J. S., 16. 9.): unsichtbar, bis man zeigt oder per
     Tastatur hinkommt — und sichtbar, solange das Video angehalten ist, sonst
     findet niemand den Weg zurueck. Ueber dem Kachel-Link (z-index), damit
     ein Klick darauf nicht die Seite wechselt. Bei Geraeten ohne Zeiger
     bleibt er immer da: Dort gibt es kein Hover. */
  .medium__schalter {
    position: absolute;
    inset-block-end: calc(var(--gutter) * .5);
    inset-inline-end: calc(var(--gutter) * .5);
    z-index: 3;
    display: grid;
    place-items: center;
    inline-size: 2.4em;
    block-size: 2.4em;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: color-mix(in oklab, var(--scrim, #000) 45%, transparent);
    color: var(--schleier-schrift, #fff);
    cursor: pointer;
    opacity: 0;
    transition: opacity var(--motion-base, .2s) var(--motion-ease, ease);
  }
  .medium__schalter .zeichen { inline-size: 1.1em; block-size: 1.1em; }
  .eintrag__medium:hover .medium__schalter,
  .medium__schalter:focus-visible,
  .medium__schalter[aria-pressed="true"] { opacity: 1; }
  @media (hover: none) { .medium__schalter { opacity: 1; } }
  .schalter--play { display: none; }
  .medium__schalter[aria-pressed="true"] .schalter--pause { display: none; }
  .medium__schalter[aria-pressed="true"] .schalter--play  { display: initial; }
  /* Eine Schleife ohne Bedienung zeigt keine Browser-Bedienelemente, auch
     nicht die, die WebKit bei Beruehrung einblendet. */
  .medium--stumm-schleife video::-webkit-media-controls,
  .medium--immer video::-webkit-media-controls { display: none !important; }

  /* == Das Format einer Kachel ohne Bild =================================== */
  /* `verhaeltnis` am Eintrag statt am Bild (J. S., 16. 9.): Dieselbe
     Erbkette wie beim Medium. Kein overflow:hidden — das Verhaeltnis ist ein
     Wunsch, zu viel Text laesst die Kachel wachsen statt abzuschneiden. */
  .eintrag--format { aspect-ratio: var(--v-jetzt, auto); }
  /* GEPOLSTERTE MEDIENKACHEL (J. S., 17. 9.): Das Verhaeltnis sitzt an der
     Kachel (Klasse oben), das Medium fuellt den Polsterkasten und laesst
     seine eigene Leiter fallen. Ein Plan passt sich darin ein (meet). */
  /* Eine Rasterzeile, die den Polsterkasten fuellt — kein `block-size: 100%`:
     das Prozent bleibt bei einer Hoehe aus aspect-ratio unbestimmt und wird
     null (gemessen 17. 9., headless Chrome: leere Kachel). */
  .eintrag--medienkachel { display: flex; flex-direction: column; }
  .eintrag--medienkachel .eintrag__medium { aspect-ratio: auto; block-size: auto; min-block-size: 0; flex: 1 1 auto; }
  .eintrag--medienkachel .eintrag__medium img,
  .eintrag--medienkachel .eintrag__medium video { block-size: 100%; object-fit: cover; object-position: var(--fokus, 50% 50%); }
  .eintrag--medienkachel .eintrag__medium img[src$=".svg"] { object-fit: contain; }
  .eintrag--medienkachel .eintrag__medium .medium__svg { position: absolute; inset: 0; }

  /* == Der Link am Eintrag ================================================= */
  /* EIN <a> im Markup, die ganze Kachel als Ziel — dasselbe Mittel wie beim
     Knopf im Teaser. `position: relative` am Eintrag, damit das
     Pseudoelement die Kachel meint und nicht die Liste. Der Fokusring liegt
     um die Kachel, nicht um den Titel: `:focus-within`, weil das
     Pseudoelement keinen Fokus tragen kann. */
  .eintrag--verlinkt { position: relative; }
  .eintrag__link { color: inherit; text-decoration: none; }
  .eintrag__link::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
  }
  /* DER RING NUR PER TASTATUR (J. S., 16. 9.): `:focus-within` zeichnete ihn
     auch nach einem Mausklick — ein dunkler Rand um jede angeklickte Kachel.
     `:focus-visible` greift nur bei Tastaturfokus. Kein `:has()` (Firefox
     113): Der Ring liegt deshalb auf dem Pseudoelement, das die Kachel
     ohnehin deckt — beim stummen Link auf dem Link selbst. */
  .eintrag__link:focus-visible { outline: none; }
  .eintrag__link:focus-visible::after,
  .eintrag__link--stumm:focus-visible {
    outline: var(--focus-width, 2px) solid currentColor;
    outline-offset: -2px;
  }
  /* DER STUMME LINK NIMMT KEINEN PLATZ (J. S., 16. 9., im Inspektor gesehen):
     Ein leeres Inline-<a> erzeugt im Raster des Eintrags eine eigene Zeile
     plus Luecke — unter einer Bildkachel stand ein Streifen von einer
     Zeilenhoehe. Absolut gesetzt nimmt es nicht am Raster teil, und die
     Kachel endet mit dem Bild. Das Pseudoelement braucht es dann nicht. */
  .eintrag__link--stumm { position: absolute; inset: 0; z-index: 2; }
  .eintrag__link--stumm::after { content: none; }
  /* Die Rueckmeldung beim Zeigen: der Titel unterstrichen, wie ein Link im
     Kopf — kein Zoom, kein Schatten. Das Bild bleibt, was es ist. */
  /* JEDE VERLINKTE KACHEL BEWEGT SICH BEIM ZEIGEN GLEICH (J. S., 17. 9.,
     nach drei Anlaeufen: erst Bild schrumpfen, dann Flaechen abdunkeln, dann
     Blasen drehen — „genau das ueberall"): die ganze Kachel schrumpft auf
     96 % und dreht anderthalb Grad. Bild, Farbe, Raster, Video, Blase — eine
     Rueckmeldung, ein Mass. transform faesst das Layout nicht an, der
     Teppich bleibt liegen. Mit Zeiger beim Zeigen (`hover: hover`, sonst
     klebt der Zustand auf dem Handy), ohne Zeiger beim Druecken, bei
     reduzierter Bewegung nichts. Masonry setzt `transition-property:
     opacity, transform` inline — das schliesst diesen Uebergang ein. */
  .eintrag--verlinkt, .eintrag--klickbar {
    transition: transform var(--motion-base, .2s) var(--motion-ease, ease);
    transform-origin: center;
  }
  /* Alle schrumpfen, NUR DIE BLASE dreht dazu (J. S., 17. 9., „zurueck"):
     `--kachel-drehung` ist 0, die Blase setzt sie auf anderthalb Grad. */
  /* Kacheln schrumpfen auf 97.5 %, Blasen auf 96 % (J. S., 17. 9.). */
  .eintrag--verlinkt, .eintrag--klickbar { --kachel-drehung: 0deg; --kachel-hover: .975; }
  .eintrag--blase.eintrag--verlinkt, .eintrag--blase.eintrag--klickbar { --kachel-hover: .96; }
  /* Die Blase kippt zur Zunge hin (J. S., 17. 9.): Zunge links dreht gegen
     den Uhrzeiger, die linke Seite senkt sich; Zunge rechts umgekehrt;
     Zunge in der Mitte bleibt gerade. */
  .blase--zunge-links.eintrag--verlinkt,  .blase--zunge-links.eintrag--klickbar  { --kachel-drehung: calc(-1 * var(--blase-drehung, 1.5deg)); }
  .blase--zunge-rechts.eintrag--verlinkt, .blase--zunge-rechts.eintrag--klickbar { --kachel-drehung: var(--blase-drehung, 1.5deg); }
  .blase--zunge-mitte.eintrag--verlinkt,  .blase--zunge-mitte.eintrag--klickbar  { --kachel-drehung: 0deg; }
  @media (hover: hover) {
    .eintrag--verlinkt:hover, .eintrag--klickbar:hover {
      transform: scale(var(--kachel-hover, .96)) rotate(var(--kachel-drehung));
    }
  }
  .eintrag--verlinkt:active, .eintrag--klickbar:active {
    transform: scale(var(--kachel-hover, .96)) rotate(var(--kachel-drehung));
  }
  @media (prefers-reduced-motion: reduce) {
    .eintrag--verlinkt, .eintrag--klickbar { transition: none; }
    .eintrag--verlinkt:hover, .eintrag--klickbar:hover { transform: none; }
  }

  .eintrag--verlinkt:hover .eintrag__link {
    text-decoration-line: underline;
    /* DIESELBEN WERTE WIE JEDER LINK DER SEITE (J. S., 16. 9.): Strich,
       Abstand und Farbe kommen aus dem Theme-Formular (Tokens.php,
       `type.link.*`), nicht aus einer eigenen Zahl. Hier stand kurz
       `from-font` — eine Angabe am Formular vorbei. */
    text-decoration-color: var(--underline, currentColor);
    text-decoration-thickness: var(--link-thickness, 1px);
    text-underline-offset: var(--link-offset, 0.15em);
    text-decoration-skip-ink: auto;
  }

  /* == Anordnungen ========================================================= */
  .liste--gestapelt {
    display: flex;
    flex-direction: column;
    gap: var(--rinne);
  }
  /* Gleich breite Zellen, die EINZELN umbrechen. Intrinsisch geloest: Der
     Block kennt seine Breite nicht, nur seinen Platz — das traegt spaeter
     auch im Behaelter. */
  .liste--raster {
    display: grid;
    gap: var(--rinne);
    /* `zeilenrinne:` am Block (J. S., 29. 9.) vervielfacht nur den Abstand
       ZWISCHEN DEN ZEILEN — etwa Portraets, die untereinander stehen und
       mehr Luft brauchen als nebeneinander. */
    row-gap: calc(var(--rinne) * var(--zeilenrinne, 1));
    /* KEIN `100%` IM min() (Safari 17.6, gemessen 4. 9.).
       Hier stand `minmax(min(var(--mindest), 100%), 1fr)`. Das `100%` sollte
       verhindern, dass eine Kachel breiter wird als ihr Behaelter. In Safari
       kostet es genau das Gegenteil: Die Liste traegt `cq`, also
       `container-type: inline-size`, und einen Prozentwert INNERHALB der
       eigenen Spaltendefinition loest Safari dort mit NULL auf — es sieht
       eine Zirkularitaet, weil die Containergroesse und der Inhalt
       voneinander abhingen. Aus `min(11rem, 100%)` wird 0, aus dem Track
       `minmax(0, 1fr)`, und jede Kachel ist 0 px breit. Gemessen an den vier
       Vorstandskacheln: Behaelter 694 px, Kacheln `0x193`, alle an derselben
       Stelle. Chrome rechnet mit der Containerbreite und zeigt nichts davon.

       GRENZE: Ohne das `min()` laeuft eine Kachel ueber, wenn `--mindest`
       groesser ist als der Behaelter — bei 18rem also unter 288 px. Das
       faengt die Zeile darunter ab, ohne Prozentwert. */
    grid-template-columns: repeat(auto-fit, minmax(var(--mindest, 18rem), 1fr));
    /* Der Ueberlaufschutz, den das `100%` leisten sollte — als eigene Regel
       und ohne Prozent im Track. */
    grid-template-columns: repeat(auto-fit, minmax(min(var(--mindest, 18rem), 100cqi), 1fr));
  }
  /* Gleich breit, aber ALLE AUF EINMAL: 3 -> 1+1+1, nie 2+1. */
  .liste--einzeilig {
    display: flex;
    flex-wrap: wrap;
    gap: var(--rinne);
  }
  .liste--einzeilig > .eintrag {
    flex-grow: 1;
    flex-basis: calc((var(--schwelle, 32rem) - 100%) * 999);
  }
  /* Eigene Breiten — Logos, Marken, Schlagworte. */
  .liste--umbrechend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(var(--rinne) * .6) var(--rinne);
  }

  /* == Plan: Fliesstext ==================================================== */
  .liste--fliesstext .eintrag__text { max-inline-size: min(var(--measure), 100%); }
  .liste--fliesstext .eintrag__titel { margin-block-end: var(--flow-tight, .5em); }

  /* == Plan: Aussage — kurzer Text, gross gesetzt ========================== */
  .liste--aussage { --rinne: calc(var(--gutter) * 2); }
  /* Die Schrift kommt aus der Rollenklasse (rolle--display, im Template).
     Hier steht nur, was zur ANORDNUNG gehoert. */
  .liste--aussage .eintrag__text {
    max-inline-size: min(calc(var(--measure) * 1.1), 100%);
    text-wrap: balance;
  }
  .liste--aussage .eintrag__text p { max-inline-size: none; }

  /* == Plan: Teaser ======================================================== */
  .liste--teaser .eintrag {
    display: grid;
    align-content: start;
    gap: var(--flow-tight, .6em);
    position: relative;
  }
  /* DIE LEGENDE HAENGT AM BILD (J. S., 3. 9. abends): rund 10 px darunter,
     nicht im Abstand eines eigenen Blocks. Zum Bild gehoert weniger Luft als
     zwischen zwei Textteilen — sonst schwimmt die Zeile und koennte ebenso
     zum naechsten Bild gehoeren.

     `calc(...)` und kein fester Wert: Das gap des Eintrags steht ueber allen
     Teilen; hier wird nur SEIN Anteil zurueckgenommen, bis rund 10 px uebrig
     bleiben. Aendert sich das gap, bleibt der Abstand unter dem Bild. */
  .liste--teaser .eintrag__medium { margin-block-end: calc(10px - var(--flow-tight, .6em)); }
  /* NUR, WENN EINE LEGENDE FOLGT (J. S., 16. 9., im Inspektor gesehen): Der
     Anschluss ist negativ, sobald .6em groesser als 10 px ist — und ohne
     etwas darunter ragte das Bild um diesen Betrag aus dem Eintrag. Eine
     reine Bild- oder Videokachel endet mit dem Medium. */
  .liste--teaser .eintrag__medium:last-child { margin-block-end: 0; }
  /* Die ganze Flaeche klickbar, OHNE den Link zu verdoppeln: ein <a> im
     Markup, ein Pseudoelement darueber.
     NUR MIT `link` (J. S., 17. 9., „Bad und Kueche ist noch ganz klickbar"):
     Bis heute deckte der Knopf im Teaser IMMER die ganze Kachel. Die Regel
     ist jetzt eine: `link` am Eintrag macht die Kachel klickbar — ohne
     Knopf ueber den Titel oder den stummen Link, mit Knopf ueber dessen
     Pseudoelement (der Knopf traegt das Ziel). Nur Knopf heisst nur Knopf. */
  .liste--teaser .eintrag--klickbar .eintrag__knopf a::after {
    content: "";
    position: absolute;
    inset: 0;
  }

  /* == Titel ueber dem Bild, je Eintrag =================================== */
  /* `titel_oben: true` (J. S., 29. 9.): Titel, dann das Medium, dann der Text
     als Legende — etwa eine Videokachel mit Rahmen aus Polster. `order` wie
     bei `legende: oben`; das Markup bleibt gleich. Nur wo der Eintrag eine
     Flex-Spalte ist (Masonry). */
  .eintrag--titel-oben > .eintrag__titel {
    order: -1;
    margin-block-end: var(--flow-tight, .6em);
  }

  /* == Legende ueber dem Bild ============================================== */
  /* `legende: oben` (J. S., 3. 9. abends). Dasselbe Mittel wie bei der Buehne:
     `order` schiebt allein das Bild ans Ende, das Markup bleibt fuer alle
     anderen Varianten gleich.

     Der enge Anschluss dreht mit — er sitzt jetzt OBEN am Bild, damit die
     Legende weiterhin an ihm haengt und nicht am Eintrag darueber. */
  .liste--legende-oben .eintrag__medium {
    order: 9;
    margin-block-end: 0;
    margin-block-start: calc(10px - var(--flow-tight, .6em));
  }

  /* == Knopf direkt unter dem Bild ========================================= */
  /* `knopflage: oben` (J. S., 3. 9. abends). Dasselbe Mittel wie sonst auch:
     `order` sortiert um, das Markup bleibt gleich. Das Bild behaelt seine
     Stelle (0), der Knopf rueckt dahinter (1), alles Uebrige folgt (2).

     `order` auf die uebrigen Teile MUSS gesetzt werden: Ohne das stuenden sie
     auf 0 und damit vor dem Knopf — genau die Reihenfolge, die hier nicht
     gemeint ist. */
  .liste--knopf-oben .eintrag > * { order: 2; }
  .liste--knopf-oben .eintrag__medium { order: 0; }
  .liste--knopf-oben .eintrag__knopf {
    order: 1;
    /* MEHR LUFT ZUM BILD ALS ZWISCHEN DEN TEXTTEILEN (J. S., 3. 9. abends):
       Ein Knopf ist keine Bildlegende — er darf nicht am Bild kleben, sonst
       liest er sich wie dessen Beschriftung. Die Rinne ist dasselbe Mass, das
       auch sonst zwei Dinge trennt, die nicht zusammengehoeren.

       Das enge `margin-block-end` des Bildes (10 px, fuer die Legende) wird
       hier mit aufgehoben — es gilt nur, wo wirklich eine Legende folgt. */
    margin-block-start: var(--gutter);
  }
  .liste--knopf-oben .eintrag__medium { margin-block-end: 0; }

  /* == Plan: Person ======================================================== */
  /* ZENTRIERT UNTER DEM PORTRAET (J. S., 3. 9. abends): Ein runder Ausschnitt
     hat keine linke Kante, an der Text ausrichten koennte — mittig ist hier
     die einzige Ausrichtung, die stimmt. Deshalb `justify-items` UND
     `text-align`: das eine ruecht die Kaesten, das andere die Zeilen darin. */
  .liste--person .eintrag {
    display: grid;
    align-content: start;
    justify-items: center;
    text-align: center;
    /* Enger als sonst: Name und Funktion sind EINE Angabe in zwei Zeilen,
       nicht zwei Dinge. Der Abstand zum Bild bleibt der der Liste. */
    gap: .15em;
  }
  /* ERREICHBARKEIT IN DER KARTE (J. S., 29. 9.): Ein Text unter Name und
     Funktion — Mail, Telefon — steht eine Zeile tiefer; `1lh` misst an der
     eigenen Schrift, bei `rolle: label` also an der kleinen. */
  .liste--person .eintrag__text { margin-block-start: 1lh; }
  /* Das Bild behaelt seinen eigenen, groesseren Abstand zum Namen. */
  .liste--person .eintrag__medium { margin-block-end: var(--flow-tight, .5em); }
  /* RUNDE PORTRAETS (J. S., 3. 9. abends). Der Kreis erzwingt das Quadrat:
     Ein Bild mit 3/4 wuerde als Ellipse ausgegeben, und der Fokuspunkt
     entscheidet dann, welcher Teil des Gesichts bleibt. `aspect-ratio` steht
     deshalb hier und nicht im Frontmatter — es gehoert zur Form, nicht zum
     Bild.

     Die Breite ist begrenzt: Ein Kreis ueber die ganze Spaltenbreite wird auf
     einem breiten Bildschirm zur Scheibe. `min()` haelt ihn klein genug, ohne
     eine feste Zahl zu setzen. */
  .liste--person .eintrag__medium {
    inline-size: min(var(--portraet-groesse, 12rem), 100%);
    aspect-ratio: 1;
    border-radius: 50%;
    overflow: hidden;
  }
  .liste--person .eintrag__medium img {
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
    /* `zoom:` am Block (J. S., 29. 9.), verankert unten in der Mitte: Ein
       Portraet ist unten angeschnitten, oben hat es Luft — so waechst es in
       die Luft, nicht aus dem Bild. Der Kreis schneidet (overflow oben). */
    transform: scale(var(--portraet-zoom, 1));
    transform-origin: 50% 100%;
  }

  /* == Plan: Buehne ======================================================== */
  /* Der Titel liegt IM Bild — das Bild ist selbst ein Behaelter. Text darf nie
     hinein (Lesebreite ueber einem Foto ergibt keinen lesbaren Satz).

     DER SELEKTOR IST ZWEITEILIG, und das ist kein Schoenheitsfehler: Bei einem
     einzelnen Eintrag rendert das Template KEIN <ul> — Liste und Eintrag sind
     dann dasselbe Element (`.liste--buehne.eintrag`). Steht die Buehne dagegen
     in einem Raster, ist der Eintrag ein Kind. Zielten diese Regeln auf die
     Liste, laegen im Raster alle Bilder uebereinander (gemessen 3. 9.). */
  :is(.liste--buehne.eintrag, .liste--buehne > .eintrag) {
    position: relative;
    display: grid;
    /* Das Verhaeltnis ist ein WUNSCH, kein Gesetz: Reicht der Platz fuer den
       Text nicht, bricht der Kasten auf, statt ihn zu beschneiden. Deshalb
       steht hier KEIN overflow:hidden — das setzt die automatische
       Mindesthoehe auf null und schaltet genau das ab (gemessen 3. 9.). */
    align-content: end;
    gap: var(--flow-tight, .5em);
    padding: var(--buehne-polster, var(--page-margin));
    /* UNTEN MEHR LUFT ALS AN DEN SEITEN (J. S., 3. 9. abends): Der Text sass
       zu tief — an der unteren Kante gemessen so weit weg wie links, und das
       wirkt gedrueckt. Ein Textblock, der unten in einem Bild steht, braucht
       darunter mehr Raum als daneben, sonst liest er sich wie abgeschnitten.
       Der Faktor haengt am selben Polster, damit nur EINE Zahl zu pflegen
       ist — das Verhaeltnis bleibt, wenn das Polster sich aendert.

       DIE REGEL GILT NUR FUER BUEHNEN: Der Selektor oben ist
       `.liste--buehne.eintrag` bzw. `.liste--buehne > .eintrag`. Ein Teaser,
       eine Person oder ein Fakteneintrag bekommt davon nichts. */
    padding-block-end: calc(var(--buehne-polster, var(--page-margin)) * 3.6);
    isolation: isolate;
    /* Die Schrift folgt dem SCHLEIER, nicht der Seite — und dreht deshalb
       nicht mit dem Nachtmodus. Ein Foto wird nachts nicht dunkler. */
    --on-surface: var(--schleier-schrift, #fff);
    color: var(--on-surface);
  }
  /* Nur die SEITENbuehne hat eine Mindesthoehe. Eine Buehne im Raster bekommt
     ihre Hoehe aus dem Verhaeltnis des Bildes.

     DER GANZE ERSTE BILDSCHIRM (J. S., 3. 9. abends): `100svh` minus dem Kopf.
     `--kopf-hoehe` misst der Kopf selbst und schreibt sie auf :root
     (partials/kopf.html.twig) — eine nachgerechnete Formel waere still falsch,
     sobald sich im Kopf etwas aendert. `svh` und nicht `vh`: Auf dem Handy
     zaehlt die KLEINE Fensterhoehe, sonst schiebt die einfahrende Adressleiste
     die Buehne unter den Rand. Der Rueckfall gilt, solange kein Skript lief.

     GRENZE: Das gilt nur fuer die Buehne AM SEITENANFANG. Eine zweite Buehne
     weiter unten bekaeme sonst ebenfalls den ganzen Bildschirm — dort ist die
     Kopfhoehe aber schon weggescrollt. Solange eine Seite genau eine Buehne
     ganz oben hat, faellt das nicht auf; sobald das nicht mehr stimmt, gehoert
     die Hoehe an den Abschnitt, nicht an den Block. */
  .liste--gestapelt.liste--buehne.eintrag {
    min-block-size: var(--buehne-hoehe, calc(100svh - var(--kopf-hoehe, 4.6rem)));
  }

  .liste--buehne .eintrag__medium {
    position: absolute;
    inset: 0;
    z-index: -2;
    aspect-ratio: auto;
  }
  .liste--buehne .eintrag__medium img {
    block-size: 100%;
    object-fit: cover;
    object-position: var(--fokus, 50% 50%);
  }
  /* == Buehne mit dem Titel OBEN IM Bild =================================== */
  /* `titellage: oben` (J. S., 3. 9. abends). Der Text liegt im Bild, aber an
     der oberen Kante — mit demselben Abstand, den er unten haette. Die beiden
     Polster tauschen einfach die Seite; die Zahl steht nur einmal. */
  .liste--buehne.liste--titel-oben.eintrag {
    align-content: start;
    padding-block-start: calc(var(--buehne-polster, var(--page-margin)) * 2.8);
    padding-block-end: var(--buehne-polster, var(--page-margin));
  }

  /* == Buehne mit dem Titel UEBER dem Bild ================================= */
  /* `titellage: ueber` (J. S., 3. 9. abends). Der Text steht auf der Flaeche,
     das Bild fuellt darunter den Rest des Bildschirms.

     WARUM `order` UND KEIN EIGENER WRAPPER: Das Macro `teile()` gibt immer
     erst das Medium aus, dann Titel, Untertitel, Knopf. Ein Wrapper um den
     Text haette dieselbe Reihenfolge in JEDER Variante geaendert. `order: 9`
     schiebt allein das Bild ans Ende — eine Zeile, und das Markup bleibt fuer
     alle anderen Plaene gleich.

     Die Schriftfarbe kommt hier NICHT vom Schleier: Der Text steht auf der
     Flaeche der Seite und dreht deshalb mit dem Nachtmodus mit — anders als
     ein Titel im Bild. `--on-surface` wird darum zurueckgenommen. */
  .liste--buehne.liste--titel-ueber.eintrag {
    display: flex;
    flex-direction: column;
    align-content: stretch;
    padding: 0;
    --on-surface: initial;
    color: inherit;
  }
  /* Der Text traegt sein Polster selbst — das Bild soll bis an die Kante. */
  .liste--titel-ueber > :is(.eintrag__titel, .eintrag__untertitel,
                            .eintrag__text, .eintrag__knopf) {
    padding-inline: var(--buehne-polster, var(--page-margin));
  }
  .liste--titel-ueber > .eintrag__titel { padding-block-start: var(--band, 2rem); }
  .liste--titel-ueber > .eintrag__knopf { padding-block-end: var(--gutter); }
  .liste--titel-ueber .eintrag__medium {
    order: 9;
    position: relative;
    inset: auto;
    z-index: auto;
    /* `min-block-size: 0` ist nicht Kosmetik: Ein Flex-Kind hat als
       Mindesthoehe seinen Inhalt, und ein Bild waere damit nie kleiner als
       seine natuerliche Hoehe — das Bild wuerde die Buehne aufblaehen statt
       den Rest zu fuellen. */
    flex: 1 1 0;
    min-block-size: 0;
  }

  .liste--buehne .eintrag__titel {
    /* Text ueber einem Bild braucht eine eigene Obergrenze: --measure misst an
       der Laufschrift, ein Titel ist ein Vielfaches davon und liefe sonst
       ueber die ganze Breite. In ch, damit es mit der Schrift mitwaechst.
       Die Schrift selbst kommt aus rolle--display. */
    max-inline-size: min(var(--buehne-textbreite, 18ch), 100%);
    text-wrap: balance;
  }
  .liste--buehne .eintrag__untertitel { max-inline-size: min(var(--buehne-untertitelbreite, 40ch), 100%); }
  .liste--buehne .eintrag__knopf { margin-block-start: var(--flow-tight, .6em); }
  .liste--buehne .knopf {
    border-color: currentColor;
    backdrop-filter: blur(2px);
  }

  /* == Der Schleier ======================================================== */
  /* Er gehoert zum BILD (J. S., 3. 9.) — alle vier Werte kommen als Custom
     Property vom Eintrag, das Theme traegt hoechstens die Vorgabe der Farbe.

     Der Schleier ist ein Kind des Mediums, nicht der Buehne: Ein absolut
     positioniertes Element mit z-index bildet einen eigenen Stapelkontext,
     und ein negativer Wert im ::after landete DAHINTER statt darueber. */
  .medium--schleier::after {
    z-index: 1;
    /* Verlauf ist der Normalfall: Er verdunkelt dort, wo Text steht, und
       laesst das Bild dort frei, wo keiner ist. Winkel 0 heisst von unten. */
    background: linear-gradient(
      var(--schleier-winkel, 0deg),
      color-mix(in oklab, var(--schleier-farbe, var(--scrim, #000))
                var(--schleier-staerke, var(--scrim-strength, 60%)), transparent),
      color-mix(in oklab, var(--schleier-farbe, var(--scrim, #000))
                calc(var(--schleier-staerke, var(--scrim-strength, 60%)) * .55), transparent)
        var(--schleier-mitte, 45%),
      transparent var(--schleier-reicht, 92%));
  }
  /* Flaechig: ein gleichmaessiger Wert ueber das ganze Bild. Fuer Motive, die
     ueberall gleich unruhig sind — dort hilft ein Verlauf nichts. */
  .medium--schleier.schleier--flaeche::after {
    background: color-mix(in oklab, var(--schleier-farbe, var(--scrim, #000))
                          var(--schleier-staerke, var(--scrim-strength, 60%)), transparent);
  }
  .liste--buehne .eintrag__titel {
    /* Text ueber einem Bild braucht eine eigene Obergrenze: --measure misst an
       der Laufschrift, ein Titel ist ein Vielfaches davon und liefe sonst
       ueber die ganze Breite. In ch, damit es mit der Schrift mitwaechst.
       Die Schrift selbst kommt aus rolle--display. */
    max-inline-size: min(var(--buehne-textbreite, 18ch), 100%);
    text-wrap: balance;
  }
  .liste--buehne .eintrag__untertitel { max-inline-size: min(var(--buehne-untertitelbreite, 40ch), 100%); }
  .liste--buehne .eintrag__knopf { margin-block-start: var(--flow-tight, .6em); }
  .liste--buehne .knopf {
    border-color: currentColor;
    backdrop-filter: blur(2px);
  }

  /* == Form: Fakten ======================================================== */
  .liste--fakten {
    display: grid;
    gap: var(--rinne);
    /* `100cqi` statt `100%` — siehe .liste--raster: Ein Prozentwert im
       eigenen Track wird in Safari zu 0, sobald das Element ein Container ist. */
    grid-template-columns: repeat(auto-fit, minmax(var(--mindest, 10.5rem), 1fr));
    grid-template-columns: repeat(auto-fit, minmax(min(var(--mindest, 10.5rem), 100cqi), 1fr));
  }
  /* KEIN UEBERSTRICH MEHR (J. S., 3. 9. abends): Jede Kennzahl traegt jetzt
     ein eigenes Aussehen (`flaeche:` am Eintrag) — eine Kachel mit Rahmen.
     Ein Strich obendrueber waere die zweite Trennung derselben Sache.
     Das Polster kommt dann von der Flaeche (.eintrag--gepolstert). */
  /* DIE BESCHRIFTUNG GANZ NACH UNTEN (J. S., 3. 9. abends): Zeichen und Zahl
     oben, das Wort am Fuss der Kachel. Damit stehen die Zahlen aller fuenf
     Kacheln auf einer Linie und die Woerter auch — auch wenn eine Zahl
     zweizeilig wird.

     `1fr` in der Mitte statt `margin-block-start: auto` an der Beschriftung:
     So dehnt sich die LUECKE, nicht ein Aussenabstand, und die Zeile bleibt
     unten auch dann stehen, wenn ein Eintrag gar keine Zahl hat. */
  /* ALLE ZAHLEN AUF EINER GRUNDLINIE (J. S., 3. 9. abends), auch wenn eine
     davon zweizeilig ist.

     Der Trick ist die DEHNBARE MITTE: Die Zeile der Zahl bekommt `1fr`, die
     Zahl darin steht mit `align-self: end` an deren Fuss. Weil Grid alle
     Kacheln einer Reihe gleich hoch macht und die Beschriftung unten fest
     sitzt, endet die gedehnte Zeile ueberall auf derselben Hoehe — eine
     zweite Zeile waechst dann nach OBEN statt nach unten.

     KEIN `subgrid`, obwohl es dafuer gemacht ist: Chrome kann es erst ab 117,
     Safari ab 16. Die Untergrenze des Themes ist Chrome 111 / Safari 15.4 —
     dort faellt subgrid still auf `none` zurueck. */
  .liste--fakten .eintrag {
    display: grid;
    grid-template-rows: auto 1fr auto;
    gap: .15em;
  }
  /* Das Zeichen mittig ueber Zahl und Beschriftung (J. S., 3. 9. abends):
     Eine Form hat keine Leserichtung und sitzt darum in der Mitte der
     Kachel; der Text bleibt links, wo man ihn liest. */
  .liste--fakten .eintrag__icon { grid-row: 1; justify-self: center; }
  .liste--fakten .eintrag__wert { grid-row: 2; align-self: end; }
  .liste--fakten .eintrag__mass { grid-row: 3; }
  /* Ohne eigene Flaeche steht die Kennzahl frei — dann braucht sie oben etwas
     Luft, damit sie nicht an dem klebt, was darueber steht. */
  .liste--fakten .eintrag:not([class*="flaeche--"]) {
    padding-block-start: var(--flow-tight, .6em);
  }
  /* HALB SO BREIT WIE DIE KACHEL (J. S., 3. 9. abends): Das Mass kommt aus dem
     PLATZ, nicht aus der Schrift — eine Form neben einer Zahl ist ein Bild und
     kein Zeichen im Satz. Damit waechst sie mit der Spaltenbreite mit und
     steht auf jeder Stufe im selben Verhaeltnis zur Kachel.

     `aspect-ratio` statt einer zweiten Laenge: Sonst muesste die Hoehe
     mitgepflegt werden, und bei einer Aenderung driftet eine der beiden. */
  .liste--fakten .eintrag__icon {
    inline-size: var(--fakten-icon, 50%);
    block-size: auto;
    aspect-ratio: 1;
    color: var(--icon, currentColor);
    margin-block-end: .35em;
  }
  .liste--fakten .eintrag__mass { /* Grossschreibung gehoert an die Rolle, nicht hierher — admin-felder.md 13d */ }
  .liste--fakten .eintrag__wert {
    margin: 0;
    /* KEINE GATTUNG, KEINE EIGENE GROESSE (J. S., 3. 9.). Hier stand
       var(--font-serif) und eine erfundene clamp()-Groesse — damit griff der
       Block an den Rollen vorbei direkt auf eine Gattung zu und brachte seine
       eigene Skala mit. Beides ist verboten:

         Rollen sind die Schnittstelle, Gattungen ihre Innerei. Wer eine
         Gattung anspricht, umgeht jede Einstellung, die ueber die Rolle
         laeuft — Gewicht, Laufweite, Neigung, Groesse.

       Die Kennzahl traegt jetzt eine Rolle (Klasse im Template). Hier bleibt
       nur, was WIRKLICH zu dieser Darstellung gehoert: dass Ziffern
       untereinander fluchten. */
    font-variant-numeric: tabular-nums;
    text-wrap: balance;
  }

  /* == Anordnung: Akkordeon ================================================ */
  /* DIE TRENNLINIEN (J. S., 3. 9. abends, nachgemessen am 4. 9.)

     Eine Fragenliste besteht fast nur aus Linien — bei zehn Fragen sind es
     elf. Sie sollen trennen, nicht zeichnen. Deshalb stand hier zuerst eine
     duennere FARBE statt einer duenneren Linie: 1 px, aber nur 30 % Deckkraft.

     DER RUECKFALL GREIFT NIE (gemessen 4. 9.). Sobald der Abschnitt eine
     Flaechenklasse traegt — und das tut er auf jeder gestalteten Seite —,
     setzt das Theme `--rule` immer, bei leerem Strichfeld auf 18 %. Die 30 %
     hier sind also nur der Wert fuer eine Liste voellig ohne Flaeche, und
     genau deshalb war die Linie im Prototyp zu leise.

     Die Antwort darauf ist die STAERKE, nicht die Farbe: An der Farbe kann
     das Buendel nichts drehen, ohne der Flaeche ins Handwerk zu pfuschen —
     wer eine kraeftigere Linie will, waehlt im Formular unter „Striche darin"
     eine Farbe. Die Staerke gehoert dagegen zur Anordnung.

     IMMER 1 PX (J. S., 29. 9.): Seit „Striche darin" eine Farbe traegt, ist
     die Linie klar genug; 2 px zeichneten. Die Staerke bleibt beim Buendel,
     die Farbe bei der Flaeche.

     Kein 0.5px und kein 1.5px: Beides rendert je nach Bildschirm als 1 px
     oder gar nicht. `--akkordeon-strich` bleibt eine Property, damit ein
     Projekt sie zurueckdrehen kann, ohne dass jede Stelle sie kennt. */
  .liste--akkordeon .eintrag {
    border-block-end: var(--akkordeon-strich, 1px) solid
      var(--rule, color-mix(in oklab, currentColor 30%, transparent));
  }
  .liste--akkordeon .eintrag:first-of-type {
    border-block-start: var(--akkordeon-strich, 1px) solid
      var(--rule, color-mix(in oklab, currentColor 30%, transparent));
  }
  .liste--akkordeon .eintrag__frage {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--gutter);
    /* ENGER (J. S., 3. 9. abends): .9em oben und unten waren fuer eine Frage
       zu viel Luft — die Liste zerfiel in Zeilen mit Abstand statt zu einer
       Liste. Der Wert bleibt eine Property, damit er einstellbar ist. */
    padding-block: var(--akkordeon-polster, .55em);
    cursor: pointer;
    list-style: none;
  }
  .liste--akkordeon .eintrag__frage::-webkit-details-marker { display: none; }
  .liste--akkordeon .zeichen {
    /* Wie jedes Icon im Block: „Icons darin“ aus dem Aussehen, sonst die
       Schriftfarbe (J. S., 29. 9.). */
    color: var(--icon, currentColor);
    flex: 0 0 auto;
    inline-size: 1.1em;
    block-size: 1.1em;
    transition: transform var(--akkordeon-dauer, var(--motion-base, .2s))
                var(--akkordeon-kurve, var(--motion-ease, ease));
  }
  .liste--akkordeon .eintrag[open] .zeichen { transform: rotate(180deg); }
  .liste--akkordeon .eintrag__antwort {
    /* OBEN EINFACH, UNTEN DREIFACH (J. S., 4. 9., im Browser eingestellt).
       Die Antwort gehoert zu IHRER Frage — oben genuegt derselbe Abstand, den
       die Frage selbst traegt. Unten steht die naechste Frage, und die ist ein
       neuer ANFANG, kein Fortsetzungstext; dort braucht es mehr.
       Beide als calc() auf dieselbe Property, nicht als feste Zahlen: Wer das
       Polster einstellt, verstellt sonst nur die eine Haelfte, und das
       Verhaeltnis kippt still. */
    padding-block: calc(var(--akkordeon-polster, .55em) * 1)
                   calc(var(--akkordeon-polster, .55em) * 3);
    /* DER DECKEL GILT DEM TEXT, NICHT DEN BILDERN (J. S., 4. 9.).
       `--measure` ist die Lesebreite: Ab einer gewissen Zeilenlaenge findet
       das Auge den Zeilenanfang nicht mehr. Fuer ein Bild ist Breite dagegen
       kein Leseproblem, sondern Erkennbarkeit — ein Grundriss auf Lesebreite
       gestaucht zeigt weniger, als er koennte.

       RICHTIG WAERE: Text auf `--measure`, ein `antwort__raster` ueber die
       volle Breite. Im Prototyp bewusst NICHT gemacht (es stoert dort nicht);
       beim Bau des BEHAELTERS gehoert es hinein — dort ist der Fall ohnehin
       zu entscheiden, weil ein Behaelter sein eigenes Raster mitbringt.
       → docs/bauanleitung/blocktypen.md, "Behaelter" */
    max-inline-size: min(var(--measure), 100%);
  }
  .liste--akkordeon .eintrag__antwort > * + * { margin-block-start: var(--flow, 1em); }

  /* HALBFETT STATT FETT (J. S., 4. 9.): Die Zwischentitel im Baubeschrieb
     stehen als `**Text**`, und `strong` ist im Browser 700 — neben der
     Laufschrift (400) ein zu grosser Sprung. Der naechsthoehere EINGEBUNDENE
     Schnitt ist 500 (gemessen an `type.weights.sans`: 200 · 400 · 500 · 700).

     Als Property mit Rueckfall: Fuehrt eine Quelle die 500 nicht, rueckt der
     Browser von selbst auf den naechsten vorhandenen — und ein Projekt mit
     anderer Schnittliste kann `--halbfett` setzen, ohne diese Regel zu kennen.

     Nur in der ANTWORT, nicht global: Was `strong` im uebrigen Fliesstext
     wiegt, ist eine Entscheidung des Themes. */
  .liste--akkordeon .eintrag__antwort strong { font-weight: var(--halbfett, 500); }

  /* EIN ABSATZ VOR EINER AUFZAEHLUNG LEITET SIE EIN (J. S., 4. 9.) — er
     gehoert zu ihr, nicht zum Text davor. Deshalb ein Viertel Abstand statt
     eines vollen: So liest man "Ueberschrift plus Liste" als einen Block.

     `p + ul` und nicht `:has(> strong)`: Die Untergrenze des Themes ist
     Firefox 113, und `:has()` kann Firefox erst ab 121. Die Regel greift damit
     bei JEDEM Absatz direkt vor einer Liste — und das ist richtig so, denn
     genau dort leitet er sie ein. */
  .liste--akkordeon .eintrag__antwort > p + ul,
  .liste--akkordeon .eintrag__antwort > p + ol {
    margin-block-start: calc(var(--flow, 1em) * .25);
  }

  /* AUFZAEHLUNG MIT STRICH STATT PUNKT (J. S., 4. 9.). Ein Halbgeviertstrich
     (–), nicht ein Binde- und nicht ein Geviertstrich: Der Bindestrich ist zu
     kurz, um als Marke zu lesen, der Geviertstrich zieht die Zeile
     auseinander.

     Als `::marker`-Inhalt und nicht als `list-style-type: "–"`: Beides geht,
     aber nur ueber den Marker laesst sich die FARBE getrennt setzen — der
     Strich soll die Zeile gliedern, nicht mitlesen. `list-style-position`
     bleibt aussen, damit die Textkante fluchtet.

     `.eintrag__antwort` und nicht global: Was eine Aufzaehlung im Fliesstext
     traegt, ist eine Entscheidung des Themes; hier geht es um die Listen im
     Baubeschrieb, die fast nur aus kurzen Zeilen bestehen. */
  /* GILT JETZT FUER JEDEN FLIESSTEXT (J. S., 24. 9.): Der Satz oben hat die
     Entscheidung offengelassen — sie ist gefallen, ein Strich in jeder
     Aufzaehlung. Dieselbe Regel traegt beide Orte, statt dass zwei
     Schreibweisen nebeneinander stehen.

     Das Mass ist `em` und deshalb dynamisch: Der Einzug misst sich an der
     Schrift der Liste, nicht an einer festen Zahl — eine Aufzaehlung in der
     Rolle `label` rueckt entsprechend weniger ein als eine in der
     Laufschrift, und beide fluchten unter ihrer eigenen Textkante. */

  .eintrag__text ul,
  .liste--akkordeon .eintrag__antwort ul { padding-inline-start: 1.4em; }
  .eintrag__text ul > li::marker,
  .liste--akkordeon .eintrag__antwort ul > li::marker {
    content: "–  ";
    color: var(--rule, color-mix(in oklab, currentColor 45%, transparent));
  }

  /* DAS RASTER IN EINER ANTWORT (J. S., 4. 9.): Grundrisse stehen
     nebeneinander, nicht untereinander. `--antwort-spalten` setzt der Eintrag;
     ohne Angabe fuellt sich die Zeile mit allem, was mindestens 12rem hat.
     Dieselbe Bauart wie der Teppich, nur ohne Skript — hier stehen wenige
     Bilder gleicher Groesse, da lohnt kein Masonry. */
  .liste--akkordeon .antwort__raster {
    display: grid;
    gap: var(--rinne, var(--gutter));
    grid-template-columns: repeat(var(--antwort-spalten, auto-fit),
      minmax(12rem, 1fr));
    grid-template-columns: repeat(var(--antwort-spalten, auto-fit),
      minmax(min(12rem, 100cqi), 1fr));
    margin-block-start: var(--flow, 1em);
  }
  .liste--akkordeon .antwort__bild { margin: 0; }
  .liste--akkordeon .antwort__bild img {
    display: block; inline-size: 100%; block-size: auto;
    border-radius: var(--card-radius, 0);
    /* KEIN FILTER AUF DEN PLAENEN (J. S., 4. 9., verworfen): Die Grundrisse
       sind schwarze Linien auf Weiss, und ihr Blatt verschwand auf weissem
       Grund. Kurz stand hier ein `brightness(.97)` mit einer Ausnahme fuer den
       Nachtmodus — die bessere Antwort ist die FLAECHE: Der Abschnitt traegt
       das Aussehen "Leise", und damit hat das Blatt seine Kante in beiden
       Modi, ohne dass das Bild angefasst wird. */
  }
  /* Die Legende steht LINKSBUENDIG unter ihrem Bild (J. S., 4. 9.): zentriert
     ausprobiert und verworfen — bei mehreren Plaenen nebeneinander tanzen die
     Beschriftungen dann, waehrend die linke Kante sie zu einer Reihe bindet.

     KEINE eigene Farbe (J. S., 4. 9.): Sie stand kurz auf 48 % der
     Schriftfarbe. Das war doppelt gemoppelt — die Rolle `label` ist ohnehin
     kleiner und leichter, und eine zweite Abschwaechung macht daraus einen
     Text, den man sucht statt liest. Ohne `color` erbt die Legende die
     Schriftfarbe ihrer Flaeche, wie alles andere auch. */
  .liste--akkordeon .antwort__bild figcaption {
    margin-block-start: calc(var(--flow, 1em) * .35);
  }

  /* == Plan: Kontakt ======================================================= */
  /* Ein <address> ist in jedem Browser kursiv — hier soll es das nicht sein:
     Die Schrift kommt aus der Rolle, nicht aus dem Element. */
  .liste--kontakt { font-style: normal; }
  .liste--kontakt > * { margin: 0; }
  .liste--kontakt > * + * { margin-block-start: calc(var(--flow, 1em) * .5); }
  /* Halbfett wie im Fuss und in der Akkordeon-Antwort (`--halbfett`, oben). */
  .liste--kontakt .kontakt__name { font-weight: var(--halbfett, 500); }

  /* == Anordnung: Masonry ================================================== */
  /* GRUNDZUSTAND ist das Raster — masonry-layout haengt sich darueber. Nicht
     als Bekenntnis zu JavaScript-loser Bedienung, sondern gegen das Flackern
     in der Sekunde, bevor das Skript laeuft. Es kostet keine Zeile extra. */
  .liste--masonry {
    display: grid;
    gap: var(--rinne);
    /* NIE EINSPALTIG (J. S., 4. 9.): Die Mindestbreite wird zusaetzlich auf
       die halbe Spur gedeckelt — passt `--mindest` nicht zweimal nebeneinander,
       gilt eben die Haelfte. Damit stehen auf dem Telefon zwei Bilder statt
       eines, und breiter aendert sich nichts: Dort ist `--mindest` ohnehin
       der kleinere der beiden Werte.
       Der Deckel steht auch hier und nicht nur im Skript, damit der
       Grundzustand dasselbe zeigt — sonst springt das Layout in der Sekunde,
       bevor das Skript laeuft. */
    grid-template-columns: repeat(auto-fit,
      minmax(var(--mindest, 15rem), 1fr));
    grid-template-columns: repeat(auto-fit,
      minmax(min(var(--mindest, 15rem), calc(50cqi - var(--rinne) / 2)), 1fr));
  }
  .liste--masonry > .eintrag { grid-column: span min(var(--spannt, 1), 2); }
  /* FESTES RASTER MIT STUFEN (J. S., 16. 9.): `spalten: { xs: 4, m: 8, l: 12 }`
     am Block. Dieselbe Erbkette wie ueberall — jede Stufe faellt auf die
     naechstkleinere zurueck. `--spalten-jetzt` ist die eine Zahl, die Grid
     UND Skript lesen; das Skript nimmt sie statt Breite ÷ mindest. `spannt`
     zaehlt in diesen Spalten und ist auf ihre Zahl gedeckelt.
     AUCH FUERS RASTER (J. S., 29. 9.): Dort ersetzt `spalten:` das
     Platzrechnen (auto-fit mit `--mindest`) — etwa Portraets, die bis `xs`
     untereinander und ab `s` zu dritt stehen. */
  :is(.liste--masonry, .liste--raster).liste--spalten-fest {
    --spalten-jetzt: var(--spalten-xs, 2);
    grid-template-columns: repeat(var(--spalten-jetzt), minmax(0, 1fr));
  }
  @container (min-width: 480px)  { :is(.liste--masonry, .liste--raster).liste--spalten-fest { --spalten-jetzt: var(--spalten-s,  var(--spalten-xs, 2)); } }
  @container (min-width: 768px)  { :is(.liste--masonry, .liste--raster).liste--spalten-fest { --spalten-jetzt: var(--spalten-m,  var(--spalten-s,  var(--spalten-xs, 2))); } }
  @container (min-width: 1024px) { :is(.liste--masonry, .liste--raster).liste--spalten-fest { --spalten-jetzt: var(--spalten-l,  var(--spalten-m,  var(--spalten-s, var(--spalten-xs, 2)))); } }
  @container (min-width: 1440px) { :is(.liste--masonry, .liste--raster).liste--spalten-fest { --spalten-jetzt: var(--spalten-xl, var(--spalten-l,  var(--spalten-m, var(--spalten-s, var(--spalten-xs, 2))))); } }
  :is(.liste--masonry, .liste--raster).liste--spalten-fest > .eintrag { grid-column: span min(var(--spannt, 1), var(--spalten-jetzt)); }
  /* `spannt` MIT STUFEN: Die Zahl im style-Attribut (`--spannt:3`) schlaegt
     diese Regeln, weil Inline gewinnt; steht dort eine Leiter
     (`--spannt-xs`, `--spannt-l`), loest sich `--spannt` hier je Stufe auf. */
  /* DIE KACHEL IST EIN CONTAINER (J. S., 17. 9.): Die Schrift misst die
     Kachel, nicht den Abschnitt — eine 3-Spalten-Kachel setzt kleiner als
     eine 6-Spalten-Kachel (`cq` am li, die Tokens-Regeln `.cq > *` greifen).
     Deshalb FLEX, nicht Grid: `container-type` und `display: grid` am selben
     Element brechen in Safari (fallstricke.md). Der Teaser-Plan setzt Grid,
     hier wird es zur Flex-Spalte; `gap` gilt in beiden. */
  .liste--masonry > .eintrag { display: flex; flex-direction: column; }
  .liste--masonry > .eintrag { --spannt: var(--spannt-xs, 1); }
  @container (min-width: 480px)  { .liste--masonry > .eintrag { --spannt: var(--spannt-s,  var(--spannt-xs, 1)); } }
  @container (min-width: 768px)  { .liste--masonry > .eintrag { --spannt: var(--spannt-m,  var(--spannt-s,  var(--spannt-xs, 1))); } }
  @container (min-width: 1024px) { .liste--masonry > .eintrag { --spannt: var(--spannt-l,  var(--spannt-m,  var(--spannt-s, var(--spannt-xs, 1)))); } }
  @container (min-width: 1440px) { .liste--masonry > .eintrag { --spannt: var(--spannt-xl, var(--spannt-l,  var(--spannt-m, var(--spannt-s, var(--spannt-xs, 1))))); } }
  /* Der Massgeber ist im Grundzustand nicht da — dort rechnet Grid selbst. */
  .teppich__mass { display: none; }
  /* Sobald das Skript uebernimmt, rechnet es die Positionen — Grid wuerde
     dann dagegenarbeiten. */
  .liste--masonry.ist-teppich {
    display: block;
    --spaltenbreite: calc((100% - (var(--teppich-spalten, 3) - 1) * var(--rinne))
                          / var(--teppich-spalten, 3));
    /* EIN SCHRITT IM RASTER (J. S., 23. 9.): `--sp` ist eine Spalte SAMT der
       Rinne dahinter — das Mass, um das eine Kachel weiterrueckt, wenn sie
       eine Spalte spaeter beginnt. `--spaltenbreite` allein ist die Kachel
       ohne Rinne und damit um eine Rinne zu klein fuer einen Versatz.
       Beides laeuft mit: die Spaltenbreite ist ein Prozentwert, die Rinne
       ein clamp() am Theme. So heisst `calc(var(--sp) * 2)` immer „zwei
       Spalten", auf jeder Stufe und in jeder Abschnittsbreite. */
    --sp: calc(var(--spaltenbreite) + var(--rinne));
  }

  /* LUFT JE STUFE (J. S., 23. 9.): dieselbe Leiter wie `spannt` — xs ist die
     Grundstufe, jede groessere erbt, bis sie selbst etwas setzt. So bleibt
     der Einzug auf den schmalen Stufen bei null, wo der Teppich nur zwei
     Spalten hat und eine Kachel die ganze Breite nimmt. */
  .liste--masonry > .eintrag { --luft-links: var(--luft-links-xs, 0px); --luft-rechts: var(--luft-rechts-xs, 0px); --luft-unten: var(--luft-unten-xs, var(--rinne)); }
  @container (min-width: 480px)  { .liste--masonry > .eintrag {
    --luft-links: var(--luft-links-s,  var(--luft-links-xs, 0px));
    --luft-rechts: var(--luft-rechts-s,  var(--luft-rechts-xs, 0px));
    --luft-unten: var(--luft-unten-s,  var(--luft-unten-xs, var(--rinne))); } }
  @container (min-width: 768px)  { .liste--masonry > .eintrag {
    --luft-links: var(--luft-links-m,  var(--luft-links-s,  var(--luft-links-xs, 0px)));
    --luft-rechts: var(--luft-rechts-m,  var(--luft-rechts-s,  var(--luft-rechts-xs, 0px)));
    --luft-unten: var(--luft-unten-m,  var(--luft-unten-s,  var(--luft-unten-xs, var(--rinne)))); } }
  @container (min-width: 1024px) { .liste--masonry > .eintrag {
    --luft-links: var(--luft-links-l,  var(--luft-links-m,  var(--luft-links-s, var(--luft-links-xs, 0px))));
    --luft-rechts: var(--luft-rechts-l,  var(--luft-rechts-m,  var(--luft-rechts-s, var(--luft-rechts-xs, 0px))));
    --luft-unten: var(--luft-unten-l,  var(--luft-unten-m,  var(--luft-unten-s, var(--luft-unten-xs, var(--rinne))))); } }
  @container (min-width: 1440px) { .liste--masonry > .eintrag {
    --luft-links: var(--luft-links-xl, var(--luft-links-l,  var(--luft-links-m, var(--luft-links-s, var(--luft-links-xs, 0px)))));
    --luft-rechts: var(--luft-rechts-xl, var(--luft-rechts-l,  var(--luft-rechts-m, var(--luft-rechts-s, var(--luft-rechts-xs, 0px)))));
    --luft-unten: var(--luft-unten-xl, var(--luft-unten-l,  var(--luft-unten-m, var(--luft-unten-s, var(--luft-unten-xs, var(--rinne)))))); } }

  .liste--masonry.ist-teppich > .eintrag[style*="--luft-links"] {
    margin-inline-start: var(--luft-links, 0px);
  }
  /* LUFT RECHTS (J. S., 23. 9.): anders als links schiebt sie die Kachel nicht,
     sie haelt nur Platz neben ihr frei — masonry-layout misst die Breite samt
     Margin, die Nachbarin rueckt also nach. Zusammen mit `luft_links` laesst
     sich eine schmale Kachel so auf eine breitere Spannweite stellen, ohne
     dass ihr Inhalt mitwaechst. */
  .liste--masonry.ist-teppich > .eintrag[style*="--luft-rechts"] {
    margin-inline-end: var(--luft-rechts, 0px);
  }
  /* Die Rinne bleibt der Grundabstand; `luft_unten` tritt an ihre Stelle. */
  .liste--masonry.ist-teppich > .eintrag[style*="--luft-unten"] {
    margin-block-end: var(--luft-unten, var(--rinne));
  }
  .liste--masonry.ist-teppich > .eintrag {
    inline-size: var(--spaltenbreite);
    margin-block-end: var(--rinne);
    /* KEINE GROESSENUEBERGAENGE (J. S., 16. 9., dritter Anlauf): Breite und
       Format springen an der Stufe, nur die POSITION gleitet — das macht
       masonry-layout per transform (liste.js). Zwei Anlaeufe mit
       animierter Breite und animiertem `aspect-ratio` flackerten, weil sie
       einen Neuaufbau je Frame erzwangen. So laufen die bekannten Teppiche. */
  }

  .liste--masonry.ist-teppich .teppich__mass {
    display: block;
    inline-size: var(--spaltenbreite);
    block-size: 0;
  }
  /* NIE BREITER ALS DER TEPPICH (gemessen 3. 9. abends): Auf schmalen
     Breiten rechnet das Skript EINE Spalte — ein Eintrag mit `spannt: 2`
     nahm dann trotzdem die doppelte Breite und lief aus der Seite. Gemessen
     bei 390 px: 703 px breit, und die ganze Seite bekam einen waagrechten
     Ueberlauf. `min()` deckelt das, ohne dass das Skript etwas wissen muss.

     `overflow-x: hidden` am body verbirgt so etwas uebrigens nur — der
     Eintrag waere weiterhin zu breit und der Text darin abgeschnitten. */
  /* Seit dem 16. 9. fuer JEDE Spannweite, nicht nur 2: Breite mal Spannweite
     plus die Rinnen dazwischen, gedeckelt auf die Spaltenzahl. `min()` mit
     Zahlen ohne Einheit ist erlaubt. */
  .liste--masonry.ist-teppich > .eintrag[style*="--spannt"] {
    --spannt-jetzt: min(var(--spannt, 1), var(--teppich-spalten, 3));
    inline-size: min(calc(var(--spaltenbreite) * var(--spannt-jetzt)
                          + var(--rinne) * (var(--spannt-jetzt) - 1)), 100%);
  }
  /* SPANNEN GILT AUF JEDER STUFE (J. S., 4. 9., nach einem Zwischenschritt):
     Kurzzeitig sperrte eine Regel das Spannen unter drei Spalten — gemeint
     war das fuer EIN Bild, das zu dominant stand, und das ist jetzt anders
     geloest (es spannt gar nicht mehr). Ein hervorgehobenes Bild soll in
     JEDER Rasteranordnung hervorstechen, auch wenn zwei Spalten heisst, dass
     es die Zeile fuellt. Wer eines kleiner will, nimmt ihm `spannt`. */

  /* BESCHNITTEN, SOLANGE GEZOGEN WIRD (J. S., 16. 9.): Waehrend Fenster und
     Uebergaenge laufen, ragt ein umbrechender Text sonst in die Nachbarkachel.
     NICHT dauerhaft: `overflow: hidden` schaltet bei einer Kachel mit festem
     Format das Mitwachsen ab (min-height: auto wird 0), langer Text waere
     dann immer abgeschnitten. liste.js setzt die Klasse und nimmt sie nach
     dem letzten Uebergang wieder weg. */
  .liste--masonry.teppich--zieht > .eintrag { overflow: hidden; }

  /* Der Zeilenumbruch: keine Hoehe, kein Abstand — nur ein Strich im Raster,
     der alle Spalten wieder gleich hoch macht (liste.html.twig, `umbruch`). */
  .eintrag--umbruch { block-size: 0 !important; min-block-size: 0; padding: 0; margin: 0 !important; overflow: hidden; }
  .liste--masonry.ist-teppich > .eintrag--umbruch { inline-size: 100%; margin-block-end: 0 !important; }

  /* VOLLE BREITE (J. S., 4. 9.): `spannt: 3` im Frontmatter — nicht "drei
     Spalten", sondern die ganze Zeile, wie viele es auch sind. */
  .liste--masonry > .eintrag--voll { grid-column: 1 / -1; }
  .liste--masonry.ist-teppich > .eintrag--voll { inline-size: 100%; }

  @media (prefers-reduced-motion: reduce) {
    .liste--akkordeon .zeichen { transition: none; }
  }
}

@layer versatile {
  /* == Plan: Rollen — nur fuer die Musterseite ============================= */
  /* Jede Zeile steht in DER Rolle, die sie benennt. Damit zeigt die Seite die
     Einstellung selbst, statt sie zu beschreiben. */
  .liste--rollen { --rinne: calc(var(--gutter) * 1.4); }
  .liste--rollen .eintrag {
    display: grid;
    gap: .2em;
    padding-block-start: calc(var(--gutter) * .7);
    border-block-start: 1px solid var(--rule, currentColor);
  }
  .liste--rollen .eintrag__text p { max-inline-size: none; }
  /* `rolle--body` GELOESCHT (J. S., 3. 9. abends). Sie stand hier im Buendel,
     nicht im Theme — und war damit auf einer Seite ohne `liste`-Block gar
     nicht vorhanden. Vor allem aber war sie ueberfluessig: `body` in
     versatile.css setzt die Laufschrift, und jedes Element ohne Rollenklasse
     erbt sie. Wer die Laufschrift will, schreibt <p> und laesst die Klasse
     weg. */
}

@layer versatile {
  /* Eine Flaeche am Eintrag braucht Polster — sonst klebt die Schrift an der
     Kante ihres eigenen Grundes. Das Polster kommt von der Flaeche selbst,
     mit der Rinne als Rueckfall. */
  /* UNTEN DIE HAELFTE MEHR (J. S., 4. 9.): derselbe optische Ausgleich wie
     beim Abschnitt (seite.css, --band-unten), nur staerker — ein Kasten ist
     kleiner als ein Abschnitt, und die fehlende Luft faellt darin schneller
     auf. Ueber eine eigene Property, damit ein Projekt sie zurueckdrehen kann;
     `--polster-unten: 1` gibt wieder rundum denselben Wert. */
  .eintrag--gepolstert {
    /* DIE ECKE KOMMT AUS DER FLAECHE (Befund 4. 9., J. S. im Browser):
       surfaceRules() setzt `--card-radius` an der Flaechenklasse, verbraucht
       wurde sie aber nur von `.flaeche` in versatile.css — und diese Klasse
       traegt im Buendel niemand. Die Bloecke heissen `.eintrag.flaeche--<key>`.
       Die Ecke stand also im Formular und auf der Seite nirgends.

       Hier und nicht in surfaceRules(): Die Ecke wirkt nur im BLOCK. Ein
       Abschnitt laeuft randlos ueber die volle Breite, dort gibt es nichts zu
       runden — genau deshalb setzt die Flaechenklasse nur die Variable und
       ueberlaesst das Verbrauchen dem, der einen Kasten zeichnet. */
    border-radius: var(--card-radius, 0);
    --polster: calc(var(--card-padding, var(--gutter)) * var(--polster-faktor, 1));
    padding: var(--polster) var(--polster)
             calc(var(--polster) * var(--polster-unten, 1.5));
  }
}

@layer versatile {
  /* Eine Buehne im Raster hat kein `min-block-size`, also braucht sie eine
     Hoehe von innen: Ein leerer Halter mit dem Verhaeltnis des Bildes gibt
     sie, ohne dass das Bild aus seiner absoluten Lage muss. */
  .liste--raster.liste--buehne > .eintrag::before {
    content: "";
    grid-area: 1 / 1;
    aspect-ratio: var(--v-xs, 4/3);
    pointer-events: none;
  }
  .liste--raster.liste--buehne > .eintrag > * { grid-area: 2 / 1; }
  .liste--raster.liste--buehne > .eintrag > .eintrag__medium { grid-area: auto; }
}

@layer versatile {
  /* == Die Sprechblase (J. S., 17. 9.) ===================================== */
  /* `blase: sprech` am Eintrag. DIE KACHEL IST DURCHSICHTIG, der Kasten ein
     Pseudoelement, das oben beginnt und ueber der Zungenzone endet; die
     Zunge (SVG, Template) haengt in dieser Zone — alles bleibt im Mass der
     Kachel, der Teppich misst richtig. Grund und Rahmen kommen aus dem
     Aussehen: --surface und die drei --flaeche-rahmen-* (Tokens.php, 17. 9.).
     `.eintrag.eintrag--blase` DOPPELT (gemessen): Die Flaechenklasse setzt
     background und border, liegt in versatile.css und laedt zuletzt; die
     Kartenregel setzt border-radius. Zwei Klassen schlagen beide. */
  .eintrag.eintrag--blase {
    position: relative;
    isolation: isolate;
    background: transparent;
    border: 0;
    border-radius: 0;
    aspect-ratio: auto;
    --zunge-hoehe: var(--blase-zunge-hoehe, 1.8em);
    --zunge-breite: calc(var(--zunge-hoehe) * 1.5);
    /* DIE ECKE KOMMT AUS DEM AUSSEHEN (J. S., 17. 9.): `--card-radius` der
       Flaeche — eckig oder rund entscheidet das Formular, wie bei jeder
       Kachel. Die Zunge setzt hinter der Rundung an, mindestens aber eine
       Zungenbreite vom Rand weg, damit sie bei eckigen Blasen nicht in der
       Ecke klebt. */
    --blase-r: var(--card-radius, 0px);
    --zunge-abstand: max(var(--blase-r), var(--zunge-hoehe));
    padding-block-end: calc(var(--polster, var(--card-padding, var(--gutter))) * var(--polster-unten, 1.5) + var(--zunge-hoehe));
  }
  .eintrag.eintrag--blase::before {
    content: "";
    position: absolute;
    inset: 0 0 var(--zunge-hoehe) 0;
    z-index: -1;
    background: var(--surface);
    border: var(--flaeche-rahmen-breite, 0) var(--flaeche-rahmen-stil, none) var(--flaeche-rahmen-farbe, transparent);
    border-radius: var(--blase-r);
  }
  .blase__zunge {
    position: absolute;
    /* AUF DER MITTELLINIE DES RAHMENS (J. S., 17. 9., „ein Pixel nach
       oben"): Der Kastenrahmen liegt INNERHALB des Kastens, sein Strich
       hat seine Mitte eine halbe Rahmenbreite ueber der Kastenkante. Das
       SVG endete an der Kastenkante, seine Linien begannen also eine halbe
       Rahmenbreite zu tief — eine Kerbe am Ansatz. */
    inset-block-end: calc(var(--flaeche-rahmen-breite, 0px) / 2);
    inline-size: var(--zunge-breite);
    block-size: var(--zunge-hoehe);
    overflow: visible;
  }
  /* `stroke: none` ausdruecklich: versatile.css gibt jedem SVG
     `stroke: currentColor`, und der Balken bekaeme einen Rand. */
  /* MIT PUNKTRASTER (J. S., 17. 9.): Die Kachel ist durchsichtig, also darf
     das Muster nicht auf ihr liegen — es gehoert auf den KASTEN
     (::before), die Zunge bleibt in der Grundfarbe; ein CSS-Verlauf laesst
     sich nicht in ein SVG fuellen. Die Musterregel in seite.css setzt
     background-image an der Kachel mit zwei Attributselektoren; hier
     dieselbe Staerke, damit die Kachel leer bleibt. */
  [class*="muster--punkte"][class*="muster--punkte"].eintrag--blase { background: transparent; }
  /* KASTEN UND ZUNGE TRAGEN DASSELBE MUSTER (J. S., 17. 9.): Der Kasten
     (::before) und die Zungenfuellung (::after) haben dieselbe Kachelgroesse;
     muster.js passt sie so an, dass sie im Kasten exakt aufgeht
     (--muster-groesse), ohne Skript rundet `round`. Das Muster liegt auf dem
     Polsterkasten (background-clip: padding-box), damit der Rahmen frei
     bleibt und der Ansatzbalken der Zunge nur den Rahmen deckt. Die Zunge
     verschiebt ihr Muster um ihre eigene Lage zurueck, so laufen die Punkte
     aus dem Kasten ohne Sprung weiter. */
  [class*="muster--punkte"].eintrag.eintrag--blase::before,
  [class*="muster--punkte"].eintrag.eintrag--blase::after {
    background-color: var(--surface);
    background-image:
      radial-gradient(circle closest-side at 25% 25%, var(--muster-farbe) var(--muster-punkt), transparent calc(var(--muster-punkt) + 2%)),
      radial-gradient(circle closest-side at 75% 75%, var(--muster-farbe) var(--muster-punkt), transparent calc(var(--muster-punkt) + 2%));
    background-size: var(--muster-groesse, var(--muster-teilung) var(--muster-teilung));
    background-repeat: var(--muster-wdh, round);
  }
  .eintrag.eintrag--blase.muster--gerechnet { --muster-wdh: repeat; }
  [class*="muster--punkte"].eintrag.eintrag--blase::before {
    background-clip: padding-box;
    background-origin: padding-box;
    background-position: 0 100%;
  }
  /* Die Zungenfuellung: gleiche Lage und Groesse wie das SVG, zur Form
     geschnitten (dieselben Punkte wie der SVG-Pfad, in Prozent). Der
     Versatz: waagrecht um die Lage der Zunge im Polsterkasten, senkrecht um
     die Rahmenbreite, weil das Kastenmuster am Polsterrand endet. */
  /* Die Fuellung reicht um die Rahmenbreite in den Kasten hinein und deckt
     dort die Rahmenlinie zwischen den Ansatzpunkten — ohne Rahmen ist das
     null, und keine Punktreihe wird verdeckt (J. S., 17. 9., „ueberlappt
     noch"). Ihre Oberkante liegt damit genau am Polsterrand des Kastens,
     wo dessen Muster endet: Versatz senkrecht null. */
  .eintrag.eintrag--blase::after {
    content: "";
    position: absolute;
    inset-block-end: 0;
    inline-size: var(--zunge-breite);
    block-size: calc(var(--zunge-hoehe) + var(--flaeche-rahmen-breite, 0px));
    z-index: -1;
    background: var(--surface);
    clip-path: polygon(10% 0, 6.7% 95%, 76.7% 0);
  }
  /* `--zunge-x` setzt muster.js (Lage der Zunge in px, auch fuer die Mitte —
     Prozent in background-position meint den Restraum, nicht die Breite);
     ohne Skript gilt der Abstand aus dem CSS. */
  .blase--zunge-links.eintrag--blase::after,
  .blase--zunge-mitte.eintrag--blase::after {
    background-position: calc(-1 * (var(--zunge-x, var(--zunge-abstand)) - var(--flaeche-rahmen-breite, 0px))) 0;
  }
  .blase--zunge-links.eintrag--blase::after { inset-inline-start: var(--zunge-abstand); }
  .blase--zunge-mitte.eintrag--blase::after { inset-inline-start: 50%; transform: translateX(-50%); }
  /* Rechts: vom rechten Kastenrand her gerechnet — der Kasten geht dort
     exakt auf, also liegt eine Kachelkante am Polsterrand. Die Zunge endet
     `abstand` davor; `right X` legt die rechte Kachelkante X vor den rechten
     Zungenrand, also muss X = -(abstand - rahmen) sein, damit die Kante
     wieder am Polsterrand liegt. NEGATIV (17. 9., einmal falsch gedreht:
     eine halbe Teilung Versatz, von J. S. im Screenshot gesehen). */
  .blase--zunge-rechts.eintrag--blase::after {
    inset-inline-end: var(--zunge-abstand);
    clip-path: polygon(90% 0, 93.3% 95%, 23.3% 0);
    background-position: right calc(-1 * (var(--zunge-abstand) - var(--flaeche-rahmen-breite, 0px))) top 0;
  }
  /* Das SVG zeichnet nur noch den Strich; die Fuellung kommt vom ::after. */
  .blase__zunge .zunge__form { fill: none; }
  .blase__zunge .zunge__form {
    fill: none;
    stroke: var(--flaeche-rahmen-farbe, transparent);
    stroke-width: var(--flaeche-rahmen-breite, 0);
    /* Der Strich misst im viewBox-Mass; damit er so dick ist wie der
       Kastenrahmen in Pixeln, bleibt das SVG unskaliert (non-scaling). */
    vector-effect: non-scaling-stroke;
  }
  /* Die Zunge setzt hinter der Rundung an, ueberlappt den Kastenrahmen um
     seine Breite, damit der Balken ihn verdeckt. */
  .blase--zunge-links  .blase__zunge { inset-inline-start: var(--zunge-abstand); }
  .blase--zunge-mitte  .blase__zunge { inset-inline-start: 50%; transform: translateX(-50%); }
  .blase--zunge-rechts .blase__zunge { inset-inline-end: var(--zunge-abstand); transform: scaleX(-1); }
  .eintrag.eintrag--blase .eintrag__titel { text-wrap: balance; }
  /* MITTIG, waagrecht und senkrecht (J. S., 17. 9.): Eine Blase ist eine
     Aussage, kein Absatz — der Text steht in der Mitte des Kastens. Die
     Zungenzone liegt im Polster unten und zaehlt nicht mit. */
  .eintrag.eintrag--blase {
    align-content: center;
    justify-items: center;
    text-align: center;
  }
  .eintrag.eintrag--blase .eintrag__text p { margin-inline: auto; }
}

/* AUSSERHALB VON @layer, MIT ABSICHT (gemessen 17. 9. im Nachtmodus): Ein
   Illustrator-Export bringt ein <style> mit `stroke: #000` in einer eigenen
   Klasse mit. Das steht UNGESCHICHTET im Dokument, und ungeschichtete Regeln
   gewinnen gegen jede Kaskadenschicht — Spezifitaet hin oder her. Die Regel
   fuer Farbe und Staerke der Plaene muss deshalb selbst ungeschichtet sein:
   Klasse plus Element schlaegt dann die Klasse aus der Datei. Jede Linie
   eines eingebetteten Plans traegt so die Schriftfarbe des Aussehens, Tag
   und Nacht; Fuellungen bleiben, wie die Datei sie setzt. */
.medium__svg svg * {
  vector-effect: non-scaling-stroke;
  stroke-width: var(--plan-strich);
  stroke: currentColor;
}
/* DREI STRICHSTAERKEN STATT EINER (J. S., 24. 9.): Die Plaene kommen aus dem
   CAD mit 0.144 / 0.288 / 0.432 Einheiten — das Verhaeltnis 1 : 2 : 3, also
   0.4 / 0.8 / 1.2 pt. Bis heute zog die Regel oben alle auf dieselbe Staerke,
   und die Zeichnung verlor ihre Hierarchie.

   Die Klassen `plan-1` bis `plan-3` traegt die Datei selbst (beim Einbau
   umbenannt, weil Illustrator sie `st2`, `st5` … nennt und die Namen je
   Export wechseln). `non-scaling-stroke` steht schon oben: Die Staerke misst
   in Bildschirmpixeln, sie waechst also NICHT mit, wenn der Plan gross
   dargestellt wird — genau darum geht es. */
.medium__svg svg .plan-15 { stroke-width: calc(var(--plan-strich) * 1.5); }
.medium__svg svg .plan-2  { stroke-width: calc(var(--plan-strich) * 2); }
.medium__svg svg .plan-3  { stroke-width: calc(var(--plan-strich) * 3); }
/* Was in der Datei rot ist, bleibt rot — die Schnittfuehrung ist keine
   Zeichnung, sondern eine Angabe. */
.medium__svg svg .plan-rot { stroke: red; }

/* KENNUNG ZUM ABSTIMMEN (Provisorium 17. 9., ausserhalb der Schicht, damit
   nichts sie ueberdeckt): Buchstabe und Stelle oben links. Vor dem Livegang
   samt `kennungen:` im Frontmatter entfernen. */
.eintrag__kennung {
  position: absolute;
  inset-block-start: .4em;
  inset-inline-start: .4em;
  z-index: 4;
  padding: .15em .45em;
  background: var(--on-surface, #000);
  color: var(--surface, #fff);
  border-radius: .2em;
  pointer-events: none;
}
.liste--masonry > .eintrag { position: relative; }
