/* =====================================================================
   REVOLUTION II – Abendzettel
   Die Plakate sind das Gesetz: eine Schrift, ein Schnitt, zwei Farben.

   Die Plakate sind in Folio Bold Condensed gesetzt (Bauer 1957) – eine
   kommerzielle Schrift, die nicht auf einen Webserver gehört. Aus dem Feld
   der frei lizenzierten Annäherungen sitzt Big Shoulders Display 900 am
   nächsten dran; gemessen an den Originalplakaten (300 dpi, Pixelanalyse):

     Merkmal                Plakat   Big Shoulders 900
     Versalbreite N/H       1,227    1,143
     x-Höhe / Versalhöhe    0,741    0,750
     Unterlänge / Versal    0,266    0,250
     Stammbreite / Versal   0,201    0,188   ← 6 % zu leicht
     „REVOLUTION“ in cap     5,61     5,77

   Die fehlende Strichstärke holt allein die Riesenzeile mit einer Kontur von
   1 Einheit (bei Schriftgrad 100) zurück, siehe .riese. Sonst gilt die Regel
   der Plakate: kein Tracking, keine zweite Familie, kein leichterer Schnitt –
   auch der Haftungsausschluss in 0,9 % Versalhöhe bleibt fett.

   Aufbau dieser Datei
     1  Marken (Custom Properties)
     2  Grundlage
     3  Papier und Raster
     4  Der Bogen (Seitengerüst: Absender, Bogenleiste, Haftung)
     5  Satz (Riesenzeilen, Zeilenblöcke, Fließtext)
     6  Bausteine (Stempel, Medaillon, Marquee, Graffito, Plakate)
     7  Programm
     8  Fragezettel
     9  Bewegung
   ===================================================================== */

/* 1 ── Marken ──────────────────────────────────────────────────────── */

:root {
  /* Zwei Farben, mehr hatten die Plakate nicht. Grau entsteht nur als
     Raster, nie als Fläche – deshalb gibt es hier kein Grau. */
  --tinte: #000;
  --papier: #fff;
  /* Gedrucktes bleibt gedruckt: Plakate und Fotos kippen im Dunkelmodus nicht
     mit, sonst hinge da ein Negativ an der Wand. */
  --druck-tinte: #000;
  --druck-papier: #fff;

  /* Geflutete Felder. Im Negativ wird aus der Fläche ein Rahmen – sonst
     leuchtet einem im Foyer eine weiße Fläche entgegen. Diese vier Marken
     halten die Regel an einer Stelle fest, statt sie in vier Media Queries
     zu wiederholen; nur so lässt sich der Modus auch von Hand wählen. */
  --voll-grund: var(--tinte);
  --voll-schrift: var(--papier);
  --voll-kante: transparent;
  --voll-kante-raster: var(--tinte);
  --voll-strich: 0px;
  /* Das Graffito ist gesprühte Farbe auf Papier: im Negativ muss es kippen,
     sonst steht schwarze Schrift auf schwarzem Grund. */
  --graffito-filter: brightness(1.12) contrast(1.15);
  /* Multiplizieren, nicht „normal": Weiß mal Weiß trägt nichts bei, es
     entsteht gar kein eigener weißer Kasten. Steht hier „normal", liegt das
     Bild als undurchsichtige Ebene über dem Blatt – und iOS mischt eine
     gefilterte Ebene auf einem P3-Schirm minimal heller als die Fläche
     daneben. Im Bildschirmfoto sieht man das nicht, weil das flach fällt. */
  --graffito-mischung: multiply;

  --schrift: "Big Shoulders Display", "Haettenschweiler", "Arial Narrow", sans-serif;
  /* Was das Publikum selbst schreibt, steht in seiner eigenen Schrift, nicht in
     der des Plakats. Das betrifft ausschließlich Eingabefelder und die in der
     Redaktion gelesenen Fragen – jeder Text, den das Blatt selbst sagt, steht
     in der Plakatschrift, bis hinunter zum Haftungsausschluss. */
  --hand: ui-sans-serif, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* Versalhöhe von Big Shoulders = 0,800 em. Die Stufen sind die Plakatstufen,
     umgerechnet: Schriftgrad = gewünschte Versalhöhe / 0,800 */
  /* Die Stufen des Absenderkopfs stehen einzeln, weil sie die Kopfhöhe
     bestimmen (siehe --kopf-absender) und weil das Plakat sie so setzt. */
  --t-absender-1: clamp(1.009rem, 3.114vw, 1.611rem);
  --t-absender-2: clamp(0.859rem, 2.523vw, 1.288rem);
  --t-absender-3: clamp(0.773rem, 2.147vw, 1.095rem);
  --t-reiter: clamp(0.752rem, 2.792vw, 1.074rem);
  --t-termin: clamp(1.127rem, 3.865vw, 1.933rem);
  --t-lauf: clamp(1.02rem, 3.221vw, 1.557rem);
  --t-lauftext: clamp(1.235rem, 1.095rem + 0.644vw, 1.611rem);
  --t-punkt: clamp(1.342rem, 4.724vw, 1.879rem);
  --t-nebensatz: 1rem;

  --t-gross: clamp(2.255rem, 9.127vw, 4.939rem);
  --t-mittel: clamp(1.45rem, 3.865vw, 2.255rem);
  --t-text: clamp(1.141rem, 1.031rem + 0.537vw, 1.396rem);
  --t-klein: clamp(0.94rem, 0.891rem + 0.215vw, 1.074rem);
  --t-winz: clamp(0.738rem, 0.709rem + 0.129vw, 0.872rem);

  /* Abstände: die Plakate kennen nur wenige, dafür klare Sprünge. */
  --luft-1: 0.35rem;
  --luft-2: 0.75rem;
  --luft-3: 1.5rem;
  --luft-4: 2.5rem;
  --luft-5: clamp(3rem, 8vw, 6rem);
  --rand: clamp(0.75rem, 3.5vw, 2.25rem);
  --breite: 62rem;

  --rund-lichtkasten: clamp(18px, 5vw, 42px);

  --strich: 3px;
  --strich-dick: 7px;

  /* Was über dem Umschlag steht, aus denselben clamp-Werten zurückgerechnet:
     Absenderkopf (Polster oben + unten + drei Zeilen à 1,06 + Trennstrich)
     und die Bogenleiste (Mindesthöhe + Trennstrich). Reine CSS-Rechnung,
     kein Skript misst nach – die Ein-Fenster-Regel darf nicht davon abhängen,
     dass JavaScript läuft. Gemessen stimmt sie auf wenige Pixel. */
  --kopf-absender: calc(
    clamp(0.7rem, 2.2vw, 1.15rem) + clamp(0.55rem, 1.6vw, 0.9rem) + var(--strich)
    + 1.06 * (var(--t-absender-1) + var(--t-absender-2) + var(--t-absender-3)));
  --kopf-leiste: calc(3.1rem + var(--strich));
  /* Höhe der unten klebenden Bogenleiste. Eine Marke, keine drei Zahlen:
     bei 200 % Zoom bricht sie zweizeilig um, und Polster, Kopfhöhe und
     Sprungziel müssen alle drei mitwachsen. */
  --leiste: 3.6rem;
  --kopfhoehe: calc(var(--kopf-absender) + var(--kopf-leiste));

  color-scheme: light dark;
}

/* Im Halbdunkel des Foyers steht das Plakat als Negativ da – so, wie es
   aus dem Kopierer kommt, wenn man den Deckel offen lässt. */
/* Der Dunkelmodus ist ein Negativ, kein zweites Thema: getauscht werden
   genau zwei Farben und die vier Marken für geflutete Felder.
   Zweimal notiert, weil eine Media Query keine Klasse mitbringen kann:
   einmal für die Einstellung des Geräts, einmal für die des Publikums. */
@media (prefers-color-scheme: dark) {
  :root:not([data-modus="hell"]) {
    --tinte: #fff;
    --papier: #000;
    --voll-grund: var(--papier);
    --voll-schrift: var(--tinte);
    --voll-kante: var(--tinte);
    --voll-kante-raster: var(--tinte);
    --voll-strich: var(--strich-dick);
    --graffito-filter: brightness(1.12) contrast(1.15) invert(1);
    --graffito-mischung: screen;
  }
}
:root[data-modus="dunkel"] {
  --tinte: #fff;
  --papier: #000;
  --voll-grund: var(--papier);
  --voll-schrift: var(--tinte);
  --voll-kante: var(--tinte);
  --voll-kante-raster: var(--tinte);
  --voll-strich: var(--strich-dick);
  --graffito-filter: brightness(1.12) contrast(1.15) invert(1);
  --graffito-mischung: screen;
}
:root[data-modus="hell"] { color-scheme: light; }
:root[data-modus="dunkel"] { color-scheme: dark; }

@font-face {
  font-family: "Big Shoulders Display";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/bigshoulders-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Big Shoulders Display";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/bigshoulders-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* 2 ── Grundlage ───────────────────────────────────────────────────── */

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

/* Muss vor allen display-Regeln stehen und sie überstimmen: sonst zeigt
   .raumfilter{display:flex} den Filter auch ohne JavaScript, und .knopf
   {display:inline-block} den Teilen-Knopf auch dort, wo es nichts zu
   teilen gibt. Beides sind Knöpfe, die nichts tun. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  /* Sonst verdeckt die klebende Bogenleiste den Anfang des Sprungziels. */
  scroll-padding-top: 4.5rem;
  /* Unter 48em steht die Bogenleiste unten am Rand. Ohne dieses Polster
     landet ein Sprungziel dahinter statt davor. */
  scroll-padding-bottom: calc(var(--leiste) + 0.6rem);
}

body {
  margin: 0;
  background: var(--papier);
  color: var(--tinte);
  font-family: var(--schrift);
  font-size: var(--t-text);
  line-height: 1.42;
  font-weight: 900;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

/* Diese Elemente bringen ein eigenes Gewicht vom Browser mit und erben die
   900 oben deshalb nicht: Überschriften kommen als 700, Bedienelemente als
   400. Sie fielen damit aus der Ein-Schnitt-Regel heraus – auf jedem Bogen
   sichtbar, in der Zwischenüberschrift wie im Raumfilter. */
h1, h2, h3, h4, h5, h6,
button, select, optgroup {
  font-weight: 900;
}
/* Ausgenommen: was das Publikum selbst hineinschreibt. Das steht in seiner
   eigenen Schrift und in deren normalem Schnitt – siehe die Fremde-Hand-Regel
   in DESIGN.md. Die Sammelregel oben hatte sie mitgerissen. */
input, textarea { font-weight: 400; }

img, svg { max-width: 100%; display: block; }
img { height: auto; }

a { color: inherit; text-underline-offset: 0.14em; text-decoration-thickness: 2px; }

:where(a, button, input, textarea, select, summary, [tabindex]):focus-visible {
  outline: var(--strich) solid var(--tinte);
  outline-offset: 3px;
}

/* Sprungmarke für die Tastatur – sieht aus wie ein aufgeklebter Zettel. */
.sprung {
  position: absolute; left: var(--rand); top: -100px; z-index: 50;
  background: var(--tinte); color: var(--papier);
  padding: 0.85rem 0.9rem; font-size: var(--t-klein);
  text-transform: uppercase; text-decoration: none;
  transition: top 0.12s;
}
.sprung:focus { top: 0.5rem; }

.nur-vorleser {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* 3 ── Papier und Raster ───────────────────────────────────────────── */

/* Kein Raster als Fläche: die Vergrößerung der Plakate zeigt keinen
   regelmäßigen Punktraster, sondern einen stochastischen Diffusionsdither.
   Ein Ben-Day-Punktraster wäre das falsche Signal (Siebdruck statt
   Kopierer). Was hier steht, ist deshalb kein Raster, sondern Rauschen –
   und es liegt nicht als Bild auf dem Grund, sondern als MASKE über einer
   Tintenfläche: der Alphawert der Maske IST die Tintendeckung. Damit gilt
   die Kein-Grau-Regel dem Buchstaben nach (es gibt nur Tinte und Papier)
   und die Negativ-Regel von selbst: im Dunkeln wird --tinte zu #fff, und
   aus den Poren werden helle Punkte auf Schwarz. Ein Foto kippt nicht mit,
   eine Maske schon.

   Die Zahlen sind an der Betonwand des ersten Plakats gemessen, nicht
   gewählt:
     baseFrequency .2/.38  die Flecken der Wand sind waagerecht gestreckt
                           (Autokorrelation 0,51 waagerecht, 0,21 senkrecht)
     numOctaves 3          trifft die Skalenverteilung der Wand
     width/height 200      200·0,2 = 40 und 200·0,38 = 76 sind in allen drei
                           Oktaven ganze Zahlen; stitchTiles muss nichts
                           runden, die Kachel ist exakt nahtlos
     tableValues           an das Histogramm der Wand angeglichen: zehn der
                           siebzehn Stufen sind 0, drei Viertel der Fläche
                           bleiben unangetastetes Papier, die tiefste Pore
                           trägt 0,395 Tinte
     sRGB                  ohne das rechnet der Filter in linearRGB und
                           verzerrt die ganze Statistik

   DAS BUDGET: Feldmittel 253 auf Papier 255 – zwei Graustufen, mehr darf es
   nie werden. Bei 251 liest die Fläche als Grau, und dann ist es genau das,
   was die Kein-Grau-Regel verbietet. Wer an tableValues dreht, misst nach.

   DIE FALLE: .umschlag bekommt NUR position: relative. Kein isolation,
   kein z-index, kein filter, kein transform, keine opacity < 1 – jedes
   davon macht einen Stapelkontext auf, und dann mischt das Graffito
   (mix-blend-mode: multiply) gegen einen durchsichtigen Grund. Multiplizieren
   mit Alpha 0 gibt die Quelle unverändert zurück: das Gesprühte stünde als
   weißer Kasten auf dem Blatt.

   Die data:-Zeile bleibt UNGEBROCHEN. Ein Zeilenumbruch im url()-Token
   macht das Stylesheet an dieser Stelle ungültig.
   ------------------------------------------------------------------- */
.umschlag::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--tinte);
  --korn: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='k' x='0' y='0' width='200' height='200' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='.2 .38' numOctaves='3' seed='5' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0'/><feComponentTransfer><feFuncA type='table' tableValues='0 0 0 0 0 0 0 0 0 0 .012 .031 .085 .202 .353 .395 .395'/></feComponentTransfer></filter><rect width='200' height='200' filter='url(%23k)'/></svg>");
  /* Feste Pixel, damit das Korn auch auf einem 3x-Gerät 2–3 CSS-Pixel groß
     bleibt und nicht auf Gerätepixel schrumpft und flimmert. */
  -webkit-mask: var(--korn) 0 0/200px 200px repeat;
          mask: var(--korn) 0 0/200px 200px repeat;
}

/* 4 ── Der Bogen ───────────────────────────────────────────────────── */

.bogen {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}

/* Der Absenderkopf steht auf jedem Plakat und auf jedem Blatt: wer lädt,
   in welcher Eigenschaft, wozu. Er ist keine Zierzeile, sondern der Kopf
   eines Briefes – und rechtlich der Absender. */
.absender {
  text-align: center;
  padding: clamp(0.7rem, 2.2vw, 1.15rem) var(--rand) clamp(0.55rem, 1.6vw, 0.9rem);
  line-height: 1.06;
  text-transform: uppercase;
  border-bottom: var(--strich) solid var(--tinte);
}
.absender a { display: block; text-decoration: none; }
.absender b {
  display: block;
  font-weight: 900;
  font-size: var(--t-absender-1);
  letter-spacing: 0.005em;
}
.absender span {
  display: block;
  font-size: var(--t-absender-2);
}
.absender em {
  display: block;
  font-style: normal;
  font-size: var(--t-absender-3);
  text-transform: none;
}
.absender a { text-decoration: none; }
.absender a:hover b { text-decoration: underline; }

/* Die Bogenleiste ist die Faltkante: fünf Felder, das aufgeschlagene
   steht im Negativ. Oben auf großen Schirmen, unten am Daumen auf dem
   Telefon – dort, wo im Foyer die Hand liegt. */
.bogenleiste {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  border-bottom: var(--strich) solid var(--tinte);
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--papier);
}
.bogenleiste a {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.05em;
  padding: 0.5rem 0.2rem 0.45rem;
  text-decoration: none;
  text-transform: uppercase;
  line-height: 1;
  border-right: var(--strich) solid var(--tinte);
  min-height: 3.1rem;
}
.bogenleiste a:last-child { border-right: 0; }
.bogenleiste a i {
  font-style: normal;
  font-size: var(--t-winz);
  opacity: 0.75;
}
.bogenleiste a b {
  font-weight: 900;
  font-size: var(--t-reiter);
}
.bogenleiste a[aria-current="page"] { background: var(--tinte); color: var(--papier); }
.bogenleiste a:hover:not([aria-current="page"]) { background: var(--tinte); color: var(--papier); }

/* Bei 200 % Zoom bleiben von 390 px noch 195 px übrig – für fünf Reiter
   nebeneinander zu wenig. Dann bricht die Leiste in zwei Reihen um, statt
   den letzten Reiter aus dem Bild zu schieben. */
@media (max-width: 19.9em) {
  :root { --leiste: 6.9rem; }
  .bogenleiste { grid-template-columns: repeat(3, 1fr); }
  .bogenleiste a:nth-child(3) { border-right: 0; }
  .bogenleiste a:nth-child(-n+3) { border-bottom: var(--strich) solid var(--tinte); }
}

@media (max-width: 47.99em) {
  .bogenleiste {
    position: fixed;
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    border-top: var(--strich-dick) solid var(--tinte);
    border-bottom: 0;
    padding-bottom: env(safe-area-inset-bottom);
  }
  body { padding-bottom: var(--leiste); }
  /* Unten am Daumen liegt die Leiste über dem Blatt, sie nimmt keinen Platz
     im Fluss – dafür verdeckt sie die untersten 3,6 rem. */
  :root { --kopfhoehe: calc(var(--kopf-absender) + var(--leiste)); }
}

.blatt {
  flex: 1;
  width: 100%;
  max-width: var(--breite);
  margin: 0 auto;
  padding: 0 var(--rand);
}
.blatt-breit { max-width: none; }

/* Der Haftungsausschluss des Europäischen Parlaments. Er steht auf jedem
   Plakat im gleichen fetten Schnitt wie alles andere – genau so bleibt er. */
.haftung {
  border-top: var(--strich) solid var(--tinte);
  margin-top: var(--luft-5);
  padding: var(--luft-3) var(--rand) var(--luft-4);
  text-align: center;
  font-size: var(--t-winz);
  line-height: 1.35;
  text-transform: none;
}
.haftung p { margin: 0 auto; max-width: 27em; }
/* Vier Angaben, kein Aufzählungszeichen: getrennt wird durch Zwischenraum
   und Umbruch, nicht durch eine Kette von Mittelpunkten. Der Punkt ist ein
   Trennzeichen für zwei Dinge, nicht für vier. */
.haftung .impressum {
  margin-top: var(--luft-2);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.35em 1.6em;
}
/* Auch ein Link im Kleingedruckten muss zu treffen sein. */
/* 44 px Trefferhöhe auch hier: bei 11,85 px Schrift braucht es dafür
   1,2 em Polster oben wie unten. Der negative Rand hält den Satz zusammen. */
.haftung .impressum a {
  text-decoration-thickness: 1px;
  display: inline-block;
  padding: 1.2em 0.5em;
  margin: -1.2em -0.5em;
}
.hinweg {
  display: inline-block;
  min-height: 44px;
  padding: 0.7em 0;
  font-family: var(--schrift);
  font-size: var(--t-klein);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  text-underline-offset: 0.25em;
}
.strichliste a {
  display: inline-block;
  padding: 0.55em 0.35em;
  margin: -0.55em -0.35em;
}

/* Der Umschlag füllt das erste Fenster, so wie das Plakat die Litfaßsäule:
   Bildschirmhöhe minus alles, was darüber steht.

   Der Titel wird hier über die ZEILENHÖHE gesetzt, nicht über die Breite.
   Sonst gilt auf einem querformatigen Laptop entweder „von Kante zu Kante“
   oder „passt ins Fenster“, aber nie beides: das Plakat ist hochformatig,
   der Bildschirm nicht. `--hoehenbudget` ist der Platz geteilt durch die Zahl
   der Zeilen; daraus und aus --vmax ergibt sich die Breite des Titelblocks,
   und auf breiten Fenstern deckelt ihn die Seitenbreite – dann steht er
   wieder randbündig. */
.umschlag {
  /* Zwei Grenzen, die kleinere gewinnt:
     1. Wie viel Höhe im ersten Fenster übrig ist, geteilt durch die Zeilenzahl.
     2. Wie hoch eine Zeile sein darf, damit die breiteste noch auf die Seite
        passt – 100 % Breite geteilt durch deren Verhältnis von Breite zu Höhe
        (--vmax, kommt aus der Messung in build.mjs).
     Dadurch stehen ALLE Zeilen exakt gleich hoch, egal welche gerade an der
     Breite anschlägt. Genau das ging vorher schief. */
  --hoehenbudget: max(4.5rem, calc((100svh - var(--kopfhoehe) - 13.5rem) / 2));
  min-height: calc(100svh - var(--kopfhoehe));
  /* Trägt das Papierkorn aus Abschnitt 3. Nur relative – siehe die Falle
     dort: jeder Stapelkontext hier zerstört die Mischkette des Graffitos. */
  position: relative;
  display: grid;
  grid-template-rows: 1fr auto auto;
  /* Notbremse: nichts auf dem Umschlag darf die Seite waagerecht aufziehen. */
  overflow-x: clip;
}
.umschlag > .riesenblock { align-self: center; padding-inline: var(--rand); }
/* Breiten stehen als Inline-Prozente an den Zeilen selbst (build.mjs). */
.umschlag .graffito { height: auto; }


/* Der Titelblock ist so breit wie seine breiteste Zeile. Begrenzt wird er
   allein über die Höhe, die im ersten Fenster noch frei ist: Zeilenhöhe mal
   Seitenverhältnis der breitesten Zeile (--vmax, steht als Inline-Wert am
   Element und kommt aus der Messung in build.mjs).

   Die Zeilen selbst tragen feste Prozentbreiten aus build.mjs. Es wird also
   nichts im Browser gerechnet, was schiefgehen könnte – gleiche Zeilenhöhe
   folgt aus der Geometrie, nicht aus einer Formel. */
.riesenblock .breit,
.riesenblock .schmal {
  max-width: calc(var(--hoehenbudget) * var(--vmax));
  flex-direction: column;
  /* Gleicher Abstand zwischen allen Zeilen. */
  gap: clamp(0.15rem, 0.7vw, 0.6rem);
}

@media (max-width: 47.99em) {
  /* Drei Zeilen statt zwei, dafür liegt unten die Leiste über dem Blatt. */
  /* Drei Titelzeilen plus die zwei Drittel hohe Zeile mit dem Gesprühten. */
  .umschlag { --hoehenbudget: max(3rem, calc((100svh - var(--kopfhoehe) - 9.5rem) / 3.75)); }
}

/* Auf breiten Schirmen läuft der Titel in einer Zeile, auf schmalen bricht
   er wie auf dem Betonplakat: REVO / LUTI / ON II. */
.riesenblock .schmal { display: none; }
.riesenblock .breit { display: flex; }
@media (max-width: 47.99em) {
  .riesenblock .schmal { display: flex; }
  .riesenblock .breit { display: none; }
}

/* 5 ── Satz ────────────────────────────────────────────────────────── */

h1, h2, h3, p, ul, ol, figure, blockquote { margin: 0; }

/* Die Riesenzeile. Als SVG gesetzt, damit sie wie auf dem Plakat exakt
   von Kante zu Kante läuft – die Buchstaben werden dabei nicht verzerrt,
   die Zeile skaliert als Ganzes. */
.riese {
  display: block;
  width: 100%;
  fill: currentColor;
  /* Big Shoulders ist 6 % leichter als die Plakatschrift. Bei Schriftgrad 100
     in der viewBox holt eine Kontur von 1 Einheit genau diese Differenz zurück,
     ohne die Buchstabenbreiten anzutasten. */
  stroke: currentColor;
  stroke-width: 1;
  paint-order: stroke fill;
}
.riese + .riese { margin-top: -0.04em; }

.riesenblock {
  display: block;
  padding: clamp(0.6rem, 2vw, 1.4rem) 0 clamp(0.7rem, 2.4vw, 1.6rem);
}
.riesenblock > svg { display: block; }
.riesenblock > svg + svg { margin-top: 0.5vw; }

/* Eine Zeile des Titels, die aus mehreren Teilen besteht: II neben dem
   gesprühten Motto, oder ON und II auseinandergezogen. Die Teile stehen auf
   einer Grundlinie, der Zwischenraum füllt den Rest der Zeilenbreite – so
   schließt die Zeile rechts bündig mit der ersten ab, ganz wie auf dem
   Betonplakat, wo zwischen ON und II eine ganze Versalhöhe Luft steht. */
.riesenblock .zeile {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(0.75rem, 3vw, 3rem);
  min-width: 0;
}
.riesenblock .zeile > svg { flex: 0 0 auto; }

/* Auf dem Telefon steht der Titel dreizeilig und füllt die Breite; neben LUTI
   bliebe für das Gesprühte nur ein Streifen von einem Sechstel der Seite.
   Statt es dort auf Briefmarkengröße zu quetschen, bekommt es eine eigene
   Zeile unter dem Titel – rechtsbündig, in der Höhe einer Titelzeile. */
.riesenblock .zeile-hand { display: none; }
@media (max-width: 47.99em) {
  .riesenblock .zeile-hand {
    display: flex;
    justify-content: flex-end;
    margin-top: 0.3rem;
  }
  /* Die Breite steht am Element selbst: gut zwei Drittel einer Titelzeile
     hoch – groß genug zum Lesen, klein genug fürs erste Fenster. */
}

/* Das Gesprühte gibt nach, bevor die Zeile über die Kante läuft. */
.riesenblock .zeile .graffito {
  flex: 0 1 auto;
  min-width: 0;
  object-fit: contain;
  object-position: right bottom;
}

/* Titelzeilen ohne SVG (Zwischenüberschriften) */
.titel {
  font-size: var(--t-gross);
  line-height: 0.88;
  text-transform: uppercase;
  margin: 0;
}
.zwischentitel {
  font-size: var(--t-mittel);
  line-height: 1;
  text-transform: uppercase;
  margin: var(--luft-5) 0 var(--luft-3);
  padding-bottom: var(--luft-1);
  border-bottom: var(--strich) solid var(--tinte);
}
.zwischentitel:first-child { margin-top: var(--luft-4); }

/* Kleinere Zwischenüberschrift innerhalb eines Abschnitts. */
.kleintitel {
  font-size: var(--t-klein);
  font-weight: 900;
  line-height: 1.1;
  text-transform: uppercase;
  margin: var(--luft-4) 0 var(--luft-2);
}

/* Aufzählung im Plakatton: kein Punkt, ein Strich. */
.strichliste {
  list-style: none;
  padding: 0;
  margin: 0;
  max-width: 30em;
}
.strichliste li {
  position: relative;
  padding-left: 1.4em;
  margin-bottom: 0.4em;
  line-height: 1.35;
}
.strichliste li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 0.9em;
  height: 3px;
  background: currentColor;
}
.strichliste b { font-weight: 900; }

/* Der Veranstaltungsblock: drei Zeilen, mittig, wie auf dem Plakat. */
.termin {
  text-align: center;
  text-transform: uppercase;
  line-height: 1.06;
  padding: var(--luft-2) 0 var(--luft-3);
}
.termin b { display: block; font-weight: 900; font-size: var(--t-termin); }
/* Ein Stempel hält sich an seinen Text; über die volle Breite sähe er aus
   wie ein Knopf, der nichts tut. */
.termin .frei {
  display: inline-block;
  border: var(--strich) solid currentColor;
  padding: 0.08em 0.55em 0.12em;
}
.termin span { display: block; font-size: var(--t-termin); }

/* Fließtext. Der einzige Ort, an dem das Plakat je einen Absatz hätte –
   kurze Zeilen, viel Durchschuss, damit die fette Schrift lesbar bleibt. */
.lauftext {
  /* 34em waren gemessen 81 Zeichen – die Schrift läuft schmal, da passen
     in dieselbe Laufweite deutlich mehr Zeichen als in eine normale. */
  max-width: 26em;
  font-size: var(--t-lauftext);
  line-height: 1.46;
}
.lauftext p + p { margin-top: 0.9em; }
.lauftext .stark {
  display: block;
  font-size: 1.28em;
  line-height: 1.16;
  text-transform: uppercase;
  margin-top: 0.7em;
}

.gross { font-size: var(--t-mittel); text-transform: uppercase; line-height: 1.1; }

/* Knopf: ein schwarzer Kasten, der beim Drücken kippt. */
.knopf {
  display: inline-block;
  background: var(--tinte);
  color: var(--papier);
  border: var(--strich) solid var(--tinte);
  padding: 0.5em 1.1em 0.45em;
  font-family: var(--schrift);
  font-weight: 900;
  font-size: var(--t-mittel);
  /* Zeilenhöhe 1 schneidet bei Big Shoulders die Versalien unten an. */
  line-height: 1.12;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
/* Tastaturfokus gilt überall, auch auf einem Tablet mit Tastatur. */
.knopf:focus-visible { background: var(--papier); color: var(--tinte); }
/* Überfahren nur, wo es ein Überfahren gibt: iOS lässt den Zustand sonst
   nach dem Antippen am Knopf kleben. */
@media (hover: hover) and (pointer: fine) {
  .knopf:hover { background: var(--papier); color: var(--tinte); }
}
/* Das versprochene Kippen. Ohne Übergang: eine Rückmeldung auf die
   Berührung darf nicht erst eintreffen. */
.knopf:active { background: var(--papier); color: var(--tinte); }

.knopf[disabled] { opacity: 0.62; cursor: not-allowed; }

.knopf-leer { background: var(--papier); color: var(--tinte); }
.knopf-leer:focus-visible { background: var(--tinte); color: var(--papier); }
@media (hover: hover) and (pointer: fine) {
  .knopf-leer:hover { background: var(--tinte); color: var(--papier); }
}
.knopf-leer:active { background: var(--tinte); color: var(--papier); }

.knopfreihe { display: flex; flex-wrap: wrap; gap: var(--luft-2); align-items: center; }

/* 6 ── Bausteine ───────────────────────────────────────────────────── */

/* Stempel: was noch nicht feststeht, wird gestempelt, nicht verschwiegen. */
.stempel {
  display: inline-block;
  border: var(--strich) solid var(--tinte);
  padding: 0.24em 0.6em 0.18em;
  font-size: var(--t-klein);
  line-height: 1.1;
  text-transform: uppercase;
  transform: rotate(-2.5deg);
}
.stempel-voll { background: var(--tinte); color: var(--papier); }

/* Laufband des Zustands: vorher, heute, vorbei. */
.lauf {
  border-top: var(--strich) solid var(--tinte);
  border-bottom: var(--strich) solid var(--tinte);
  text-align: center;
  text-transform: uppercase;
  font-size: var(--t-lauf);
  /* Bricht das Band auf schmalen Geräten um, stießen die Zeilen bei 1,0
     aneinander. 1,2 hält sie auseinander, ohne das Band aufzublähen. */
  line-height: 1.2;
  padding: 0.5em var(--rand) 0.42em;
}
.lauf.jetzt { background: var(--tinte); color: var(--papier); }
/* Der Weg vom Umschlag ins laufende Programm – im Halbdunkel, im Gehen.
   Das Band ist 59 px hoch, der Verweis darin war 30 px. */
.lauf a {
  display: inline-block;
  min-height: 44px;
  padding: 0.4em 0.5em;
  margin: -0.4em -0.2em;
  text-underline-offset: 0.2em;
}

/* Medaillon nach dem Porträtplakat: der Kreisausschnitt, ohne den Lorbeer –
   der bleibt dem Plakat vorbehalten. */
.medaillon {
  width: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
  overflow: hidden;
  border: var(--strich) solid var(--tinte);
  filter: grayscale(1) contrast(1.35);
  background: var(--druck-papier);
}
.medaillon img { width: 100%; height: 100%; object-fit: cover; }

/* Marquee-Rahmen nach Plakat 3: Lichtkasten mit Birnen, gerastert.
   Die Birnen sind echte Kreise auf einem gerasterten Band. */
.marquee {
  position: relative;
  padding: clamp(1.4rem, 5vw, 3rem);
  isolation: isolate;
}
.marquee::before {
  content: "";
  position: absolute;
  inset: 0;
  border: clamp(14px, 4.5vw, 34px) solid var(--tinte);
  border-radius: var(--rund-lichtkasten);
  z-index: -2;
}
.marquee::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--rund-lichtkasten);
  background:
    repeating-linear-gradient(to right, var(--papier) 0 12px, transparent 12px 40px) top / 100% clamp(14px, 4.5vw, 34px) no-repeat,
    repeating-linear-gradient(to right, var(--papier) 0 12px, transparent 12px 40px) bottom / 100% clamp(14px, 4.5vw, 34px) no-repeat,
    repeating-linear-gradient(to bottom, var(--papier) 0 12px, transparent 12px 40px) left / clamp(14px, 4.5vw, 34px) 100% no-repeat,
    repeating-linear-gradient(to bottom, var(--papier) 0 12px, transparent 12px 40px) right / clamp(14px, 4.5vw, 34px) 100% no-repeat;
  -webkit-mask: radial-gradient(circle at 50% 50%, #000 42%, transparent 43%) 0 0 / 26px 26px;
  mask: radial-gradient(circle at 50% 50%, #000 42%, transparent 43%) 0 0 / 26px 26px;
  z-index: -1;
  pointer-events: none;
}

/* Graffito: die einzige Handschrift auf den Plakaten. Keine Schriftart kann
   das nachmachen, also steht hier das Original – aus dem Betonplakat
   herausgeschnitten und so weit aufgehellt, dass vom Putz nur die Sprühspur
   übrig bleibt. Multiply blendet das restliche Weiß ins Papier; im Negativ
   wird invertiert und mit Screen aufgelegt. Der rechte Rand ist auf dem
   Plakat angeschnitten – das bleibt so. */
.graffito {
  display: block;
  width: clamp(6.5rem, 21vw, 15rem);
  height: auto;
  /* Der letzte Rest Putzgrau muss auf reines Weiß, sonst zeichnet sich beim
     Multiplizieren der Rand des Ausschnitts als Kasten ab. */
  filter: var(--graffito-filter);
  mix-blend-mode: var(--graffito-mischung);
  transform: rotate(-1.5deg);
}

/* Der Aushang: die vier gedruckten Plakate. */
/* Die Anschlagwand. Die vier Plakate haengen als Papier davor – deshalb
   behalten sie ihre eigenen Farben, auch wenn die Seite im Negativ steht. */
.anschlagwand {
  background: var(--voll-grund);
  color: var(--voll-schrift);
  padding: var(--luft-4) var(--rand) var(--luft-5);
  margin-top: var(--luft-5);
}
.anschlagwand .zwischentitel {
  margin-top: 0;
  border-bottom-color: var(--voll-schrift);
}
.anschlagwand .lauftext { max-width: 38em; }

.aushang {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
  gap: clamp(0.75rem, 2.4vw, 1.75rem);
  margin: var(--luft-3) auto 0;
  max-width: 78rem;
}
.aushang figure { margin: 0; }
.aushang a {
  display: block;
  background: var(--druck-papier);
  color: var(--druck-tinte);
  text-decoration: none;
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.35);
}
.aushang a:hover, .aushang a:focus-visible { outline: var(--strich) solid var(--papier); outline-offset: 4px; }
.aushang img { width: 100%; }
.aushang figcaption {
  font-size: var(--t-klein);
  text-transform: uppercase;
  padding: 0.45em 0.6em 0.35em;
  border-top: var(--strich) solid var(--druck-tinte);
  line-height: 1.15;
}

/* Der Zwischenruf: einmal pro Seite darf das Plakat dazwischenschreien.
   Vollbreit, im Negativ, in der Lautstärke der Straße. */
.zwischenruf {
  background: var(--voll-grund);
  color: var(--voll-schrift);
  margin: var(--luft-5) 0;
  padding: clamp(1.5rem, 5vw, 3.5rem) var(--rand) clamp(1.2rem, 4vw, 2.6rem);
}
.zwischenruf .riese { fill: var(--voll-schrift); }
.zwischenruf .riese + .riese { margin-top: 0.6vw; }
.zwischenruf figcaption {
  margin-top: clamp(0.8rem, 2.5vw, 1.6rem);
  font-size: var(--t-klein);
  text-transform: uppercase;
  line-height: 1.3;
  max-width: 30em;
}

/* Der Aufruf: die einzige Handlung, die diese Seite verlangt. */
.aufruf {
  border-top: var(--strich-dick) solid var(--tinte);
  border-bottom: var(--strich-dick) solid var(--tinte);
  margin: var(--luft-5) 0;
  padding: var(--luft-3) 0;
}
.aufruf .gross { margin-bottom: var(--luft-2); }

/* Vier Zeilen, die den Abend beziffern statt ihn zu behaupten. Ein Register
   aus dem Plakatsatz: Schlagwort links in der Fläche, Sache rechts. */
.abendzeilen {
  margin: var(--luft-4) 0 var(--luft-3);
  border-top: var(--strich) solid var(--tinte);
}
.abendzeilen > div {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2em 0.8em;
  padding: 0.55em 0;
  border-bottom: var(--strich) solid var(--tinte);
}
.abendzeilen dt {
  /* Feste Spalte statt Mindestbreite: „Auf den Bühnen" ist länger als 9,5rem
     und schob die Beschreibungen dieser einen Zeile um 34 px nach rechts. */
  flex: 0 0 12rem;
  font-size: var(--t-punkt);
  line-height: 1.05;
  text-transform: uppercase;
}
.abendzeilen dd {
  flex: 1 1 14rem;
  margin: 0;
  font-size: var(--t-text);
  line-height: 1.3;
}
@media (max-width: 26em) {
  .abendzeilen dt { min-width: 100%; }
}

/* Solange die Räume „Raum 1" heißen, muss die Seite das selbst sagen –
   sonst sucht am Abend jemand im Haus nach einem Schild, das es nicht gibt. */
.raumhinweis { margin: var(--luft-2) 0 0; }

/* Kleingedrucktes im Fließtext (Datenschutz, Lizenzen). Auf den Plakaten steht
   auch der Haftungsausschluss im selben fetten Schnitt – hier ebenso. */
.was-klein {
  font-size: var(--t-nebensatz);
  line-height: 1.5;
  /* Wie .lauftext: rund 2,3 Zeichen je em in dieser schmalen Grotesk. */
  max-width: 27em;
  margin-top: 1.2em;
}

/* Besetzung: die Gastgeber im Medaillon, darunter die offenen Plätze. */
.gastgeberreihe {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
  gap: var(--luft-4);
  margin-top: var(--luft-3);
}
/* minmax(0, 1fr) statt 1fr: sonst hält die Textspalte ihre Min-Content-Breite
   und die Karte läuft bei starkem Zoom über die Kante. */
.gastgeber {
  margin: 0;
  display: grid;
  grid-template-columns: minmax(4rem, 7.5rem) minmax(0, 1fr);
  gap: var(--luft-3);
  align-items: start;
  overflow-wrap: anywhere;
}
@media (max-width: 19.9em) {
  .gastgeber { grid-template-columns: 1fr; }
  .gastgeber .medaillon { max-width: 7rem; }
}
.gastgeber figcaption b {
  display: block;
  font-weight: 900;
  font-size: var(--t-mittel);
  line-height: 1;
  text-transform: uppercase;
  text-wrap: balance;
}
.gastgeber figcaption span {
  display: block;
  font-size: var(--t-klein);
  line-height: 1.2;
  margin-top: 0.35em;
  text-transform: uppercase;
}
.gastgeber figcaption p {
  font-size: var(--t-nebensatz);
  line-height: 1.45;
  margin-top: 0.7em;
}

.besetzungsliste { list-style: none; padding: 0; margin: var(--luft-3) 0 0; }
.besetzungsliste li {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4em var(--luft-3);
  align-items: baseline;
  justify-content: space-between;
  border-bottom: var(--strich) solid var(--tinte);
  padding: 0.55em 0 0.5em;
}
.besetzungsliste li:first-child { border-top: var(--strich) solid var(--tinte); }
.besetzungsliste .rolle { font-size: var(--t-mittel); line-height: 1; text-transform: uppercase; }
.besetzungsliste { max-width: 34rem; }
.besetzungsliste .platzhalter { font-size: var(--t-klein); text-transform: uppercase; opacity: 0.62; }

/* Der Programm-Reiter blinkt nicht, er kippt: am Abend steht er im Negativ. */
.bogenleiste a.laeuft-gerade:not([aria-current="page"]) {
  background: var(--tinte);
  color: var(--papier);
}

/* Im Dunkelmodus werden aus den schwarzen Feldern gerahmte Felder. Sonst
   leuchtet einem im Foyer mitten im Scrollen eine weiße Fläche entgegen –
   die Gliederung trägt dann der Rahmen, nicht die Füllung. */
/* Im Negativ tragen diese beiden Felder ihre Gliederung im Rahmen statt in
   der Füllung – siehe die Marken --voll-* ganz oben. */
.zwischenruf,
.anschlagwand {
  border-top: var(--voll-strich) solid var(--voll-kante);
  border-bottom: var(--voll-strich) solid var(--voll-kante);
}
.zwischenruf .riese { fill: var(--voll-schrift); }
.anschlagwand .zwischentitel { border-bottom-color: var(--voll-schrift); }
.aushang a:hover, .aushang a:focus-visible { outline-color: var(--voll-schrift); }
:root[data-modus="dunkel"] .aushang a { box-shadow: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-modus="hell"]) .aushang a { box-shadow: none; }
}

/* 7 ── Programm ────────────────────────────────────────────────────── */

.programmkopf {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--luft-2);
  margin-top: var(--luft-4);
}

/* Der Filter ist die Antwort auf ein schmales Display. Im Raster ab 60em
   stehen ohnehin alle Räume nebeneinander – dort wäre er nur ein Knopf, der
   Löcher in den Stundenplan reißt. */
.raumfilter {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  margin: var(--luft-3) 0;
  border: var(--strich) solid var(--tinte);
}
.raumfilter button {
  flex: 1 1 auto;
  font-family: var(--schrift);
  font-size: var(--t-klein);
  text-transform: uppercase;
  line-height: 1;
  /* 44 px Mindesthöhe: darunter trifft ein Daumen im Gehen nicht zuverlässig. */
  min-height: 44px;
  padding: 0.62em 0.7em 0.55em;
  background: var(--papier);
  color: var(--tinte);
  border: 0;
  border-right: var(--strich) solid var(--tinte);
  cursor: pointer;
}
.raumfilter button:last-child { border-right: 0; }
.raumfilter button[aria-pressed="true"] { background: var(--tinte); color: var(--papier); }

/* Zeitleiste: auf dem Telefon eine Liste nach Uhrzeit, ab Tablet ein Raster
   aus Zeit und Raum. Dieselben Elemente, nur anders gesetzt – die Stundenmarke
   ist in der Liste die Zwischenüberschrift und im Raster die linke Spalte. */
.zeitleiste { margin: var(--luft-3) 0 0; }

.zeitmarke {
  font-size: var(--t-mittel);
  font-weight: 900;
  line-height: 1;
  text-transform: uppercase;
  margin: var(--luft-4) 0 var(--luft-2);
  padding-top: var(--luft-1);
  border-top: var(--strich-dick) solid var(--tinte);
}
.zeitmarke:first-of-type { margin-top: var(--luft-3); }
.zeitmarke[hidden] { display: none; }

/* Die Spaltenköpfe gehören nur ins Raster; in der Liste sagt jeder
   Programmpunkt seinen Raum ohnehin selbst. */
.spaltenkopf { display: none; }
.spaltenkopf i { font-style: normal; opacity: 0.7; }

.punkt {
  display: block;
  border: var(--strich) solid var(--tinte);
  padding: 0.7rem 0.8rem 0.75rem;
  margin-bottom: var(--luft-2);
  background: var(--papier);
}
.punkt[hidden] { display: none; }
.punkt .kopfzeile {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
  align-items: baseline;
  justify-content: space-between;
  font-size: var(--t-klein);
  text-transform: uppercase;
  line-height: 1.1;
}
.punkt .zeit { white-space: nowrap; }
.punkt .raum { border-bottom: 2px solid currentColor; }
/* „Raum 3" allein sagt im Haus nichts. Solange die echten Saalnamen fehlen,
   ist der Hausname die einzige Angabe, mit der jemand losgehen kann. */
.punkt .raum i { font-style: normal; opacity: 0.7; }
.punkt .raum i::before { content: " · "; }
.punkt h3 {
  font-weight: 900;
  font-size: var(--t-punkt);
  line-height: 1.02;
  text-transform: uppercase;
  margin: 0.3em 0 0;
}
.punkt .wer {
  font-size: var(--t-text);
  line-height: 1.2;
  margin-top: 0.25em;
}
.punkt .wer.offen { text-transform: uppercase; opacity: 0.66; }
.punkt .was {
  font-size: var(--t-nebensatz);
  line-height: 1.4;
  margin-top: 0.5em;
  max-width: 42em;
}
.punkt.laeuft {
  background: var(--voll-grund);
  color: var(--voll-schrift);
  /* Alle vier Marken, nicht nur zwei: im Hellen ein gefülltes Feld, im
     Negativ ein dick gerahmtes. Mit bloßem border-color war der laufende
     Punkt im Dunkeln von einem kommenden nicht zu unterscheiden. */
  border: var(--voll-strich) solid var(--voll-kante);
  outline: var(--strich) solid var(--tinte);
  outline-offset: calc(-1 * var(--strich));
}
/* Eine Zeile hoch, also darf die Marke fluten – auf dem im Negativ hellen
   Kasten heißt das schwarz auf weiß. */
.punkt.laeuft .jetztmarke { background: var(--voll-schrift); color: var(--voll-grund); }
/* Auf gefluteten Feldern zeichnete der Fokusring schwarz auf schwarz.
   currentColor ist immer die Farbe, die gegen den eigenen Grund steht –
   auch dann noch, wenn der Knopf beim Überfahren umkippt. */
.anschlagwand :where(a, button):focus-visible,
.punkt.laeuft :where(a, button):focus-visible,
.knopf:focus-visible,
.raumfilter button[aria-pressed="true"]:focus-visible,
.modus button[aria-pressed="true"]:focus-visible,
.bogenleiste a[aria-current="page"]:focus-visible {
  outline-color: currentColor;
  /* Nach innen: außen läge der Ring auf dem Grund des Nachbarn und
     verschwände dort. */
  outline-offset: calc(-2 * var(--strich));
}
/* Unten am Rand ist außerhalb kein Platz mehr. */
@media (max-width: 47.99em) {
  .bogenleiste a:focus-visible { outline-offset: calc(-2 * var(--strich)); }
}
/* Im Kontrastmodus des Systems verschwinden Flächen und Deckkraft. Dann
   trägt allein die Kante, was sonst die Füllung sagt. */
@media (forced-colors: active) {
  .bogenleiste a[aria-current="page"],
  .raumfilter button[aria-pressed="true"],
  .modus button[aria-pressed="true"],
  .wahl label:has(input:checked),
  .punkt.laeuft {
    border: 3px solid CanvasText;
    forced-color-adjust: none;
    background: Highlight;
    color: HighlightText;
  }
  .punkt.gewesen { opacity: 1; text-decoration: line-through; }
  /* Im Kontrastmodus bestimmt das System die Flächen. Dann fällt das Korn
     ersatzlos weg, statt als System-Fläche zurückzukommen. */
  .umschlag::before { content: none; }
}
/* Rahmenpunkte laufen den ganzen Abend (Türen offen, Basar). Sie werden
   markiert, aber nicht geflutet: sonst ist die größte schwarze Fläche des
   Bogens ausgerechnet der Eintrag, für den niemand aufsteht. Der Code kennt
   die Unterscheidung schon beim Sprungziel und beim Sortieren. */
.punkt.rahmen.laeuft {
  background: var(--papier);
  color: var(--tinte);
  /* Einfacher Strich, wo der laufende Punkt einen doppelten trägt: das
     unterscheidet die beiden in beiden Modi, weil es nicht aus den
     --voll-Marken kommt und deshalb nicht mitkippt. */
  border: var(--strich) solid var(--tinte);
  outline: 0;
}
.punkt.rahmen.laeuft .jetztmarke { background: var(--tinte); color: var(--papier); }
/* 0,4 ergäbe 2,85:1 – nach 21 Uhr wäre der halbe Bogen unter der Grenze. */
.punkt.gewesen { opacity: 0.62; }
/* Deckkräfte multiplizieren sich: 0,62 × 0,66 wären 0,41 und damit 2,9:1.
   Innerhalb eines schon gedämpften Kastens wird nichts noch einmal gedämpft. */
.punkt.gewesen .wer.offen { opacity: 1; }
/* Zweite Stelle derselben Falle: 0,7 × 0,62 sind 0,434 und damit 3,2:1. */
.punkt.gewesen .raum i { opacity: 1; }

.jetztmarke {
  display: inline-block;
  font-size: var(--t-klein);
  text-transform: uppercase;
  border: 2px solid currentColor;
  padding: 0.1em 0.45em 0.05em;
  margin-left: 0.5em;
}

@media (min-width: 60em) {
  .raumfilter { display: none; }

  .zeitleiste.raster-ansicht {
    display: grid;
    grid-template-columns: 6.4rem repeat(var(--spalten, 4), 1fr);
    /* Eine Zeile = 5 Minuten. Ohne feste Zeilenhöhe sind die Zeilen
       inhaltshoch, und ein Punkt über 20 Minuten steht so hoch wie einer
       über 45 – das Bild verspräche einen Stundenplan und wäre eine Tabelle.
       Das Maß richtet sich nach dem kürzesten Punkt: passt sein Inhalt nicht
       in seine Zeilen, dehnt er sie und mit ihnen die Zeitachse aller
       Spalten. 20 Minuten sind vier Zeilen, also braucht eine Zeile ein
       Viertel der Höhe, die ein Punkt mindestens einnimmt. */
    grid-auto-rows: minmax(1.45rem, auto);
    gap: 0;
    /* Rundum geschlossen: die Zellen zeichnen rechts und unten, der Rahmen
       oben und links – und rechts/unten noch einmal, damit die Kante auch
       dort steht, wo die letzte Spalte leer ausläuft. */
    border: var(--strich) solid var(--tinte);
  }
  .zeitleiste.raster-ansicht > * {
    border-right: var(--strich) solid var(--tinte);
    border-bottom: var(--strich) solid var(--tinte);
  }
  /* Im Raster klebt über der Bogenleiste noch der Spaltenkopf. Ein Sprung
     aus der Jetzt-Liste landete sonst unter beidem. */
  html { scroll-padding-top: calc(var(--kopf-leiste) + 3.8rem); }
  .zeitleiste.raster-ansicht .spaltenkopf {
    display: block;
    padding: 0.5rem 0.6rem;
    text-transform: uppercase;
    font-size: var(--t-klein);
    line-height: 1.1;
    position: sticky;
    /* An der tatsächlichen Höhe der Bogenleiste, nicht an einer Zufallszahl. */
    top: var(--kopf-leiste);
    background: var(--papier);
    z-index: 5;
  }
  .zeitleiste.raster-ansicht .zeitmarke {
    margin: 0;
    padding: 0.5rem 0.6rem;
    white-space: nowrap;
    border-top: 0;
    font-size: var(--t-klein);
    text-transform: uppercase;
  }
  .zeitleiste.raster-ansicht .punkt {
    /* Oben und links nimmt der Nachbar die Kante; rechts und unten zeichnet
       sie das Raster. Ein pauschales border:0 nahm sie auch dort weg, und
       überall wo etwas lief, verschmolzen die Spalten zu einem Band. */
    border-top: 0;
    border-left: 0;
    border-right: var(--strich) solid var(--tinte);
    border-bottom: var(--strich) solid var(--tinte);
    margin: 0;
    /* Im Raster ist ein Punkt keine Karte, sondern ein Block im Stundenplan:
       er muss in seine Dauer passen. Ein 20-Minuten-Punkt hat 4 von 48
       Rasterzeilen – bei Kartensatz sprengte allein die Überschrift sie. */
    padding: 0.35rem 0.5rem 0.4rem;
  }
  /* Der laufende Punkt trägt seinen Rahmen im Raster als outline: eine
     zweite Kante würde die Zeilenrechnung um 6 px verschieben. */
  .zeitleiste.raster-ansicht .punkt.laeuft {
    outline: var(--strich-dick) solid var(--voll-kante-raster);
    outline-offset: calc(-1 * var(--strich-dick));
  }
  /* Der Rahmenpunkt läuft den ganzen Abend – einfacher Strich statt
     doppeltem. Ohne diese Ausnahme trug er im Raster dieselbe Kante wie
     ein Punkt, der gerade anfängt. */
  .zeitleiste.raster-ansicht .punkt.rahmen.laeuft {
    outline: var(--strich) solid var(--voll-kante-raster);
    outline-offset: calc(-1 * var(--strich));
  }
  .zeitleiste.raster-ansicht .punkt .kopfzeile { font-size: var(--t-winz); }
  /* Im Raster steht der Hausname im Spaltenkopf – hier wäre er doppelt. */
  .zeitleiste.raster-ansicht .punkt .raum i { display: none; }
  .zeitleiste.raster-ansicht .punkt h3 {
    font-size: var(--t-text);
    line-height: 1.05;
    margin-top: 0.15em;
  }
  .zeitleiste.raster-ansicht .punkt .wer {
    font-size: var(--t-nebensatz);
    line-height: 1.15;
    margin-top: 0.1em;
  }
  /* Der Beschreibungssatz gehört in die Liste, nicht in den Stundenplan –
     im Raster antwortet die Zelle auf „wann und wo", nicht auf „worüber". */
  .zeitleiste.raster-ansicht .punkt .was { display: none; }
  .zeitleiste.raster-ansicht .leerzelle { display: block; }
}

/* Leerzellen schließen das Raster: ohne sie stünden die Programmpunkte als
   einzelne Kästen im Nichts statt in einem Stundenplan. */
.leerzelle { display: none; }
.leerzelle[hidden] { display: none; }

.spaltenkopf .vorlaeufig {
  display: inline-block;
  border: 2px solid currentColor;
  padding: 0 0.3em;
  font-size: 0.8em;
  vertical-align: 0.1em;
}

/* Am Veranstaltungsabend steht hier, was gerade läuft – bevor irgendetwas
   anderes kommt. JavaScript füllt und zeigt ihn; an allen anderen Tagen
   bleibt er weg. */
.jetztblock {
  max-width: 48rem;
  border-top: var(--strich-dick) solid var(--tinte);
  border-bottom: var(--strich-dick) solid var(--tinte);
  margin: var(--luft-3) 0;
  padding: var(--luft-2) 0 var(--luft-3);
}
.jetztblock h2 {
  /* Stand vorher auf derselben Stufe wie die Titel darunter und hatte damit
     keinen Rang über der eigenen Liste. */
  font-size: var(--t-mittel);
  font-weight: 900;
  line-height: 1;
  text-transform: uppercase;
  margin-bottom: var(--luft-2);
  /* Der Kasten hält sich an den Text: eine gefüllte Fläche über die volle
     Breite wäre im Negativ ein Blendfeld. */
  display: inline-block;
  background: var(--tinte);
  color: var(--papier);
  padding: 0.12em 0.3em 0.2em;
}
.jetztliste { list-style: none; padding: 0; margin: 0; }
.jetztliste li { border-top: var(--strich) solid var(--tinte); }
.jetztliste a {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2em 0.7em;
  align-items: baseline;
  min-height: 44px;
  padding: 0.5em 0;
  text-decoration: none;
}
.jetztliste .raum { font-size: var(--t-klein); text-transform: uppercase; border-bottom: 2px solid currentColor; }
/* Läuft etwas in einem weggefilterten Raum, wird das gesagt – und lässt sich
   von hier aus aufheben, statt den Filter oben suchen zu müssen. */
.jetztliste .verdeckt button {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2em 0.8em;
  width: 100%;
  min-height: 44px;
  padding: 0.6em 0;
  font: inherit;
  font-size: var(--t-klein);
  text-transform: uppercase;
  text-align: left;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}
/* Zahl und Handlung sind zwei Dinge: die Zahl sagt, was fehlt, die
   Unterstreichung sagt, was der Knopf tut. */
.jetztliste .verdeckt .wieviele { opacity: 0.7; }
.jetztliste .verdeckt .tun { text-decoration: underline; text-underline-offset: 0.2em; }
.jetztliste .titel { font-size: var(--t-punkt); line-height: 1.05; text-transform: uppercase; margin: 0; }
.jetztliste .bis { font-size: var(--t-klein); text-transform: uppercase; opacity: 0.7; margin-left: auto; }
/* Die zweite Frage des Abends. Steht unter der Liste, weil sie erst nach
   „was läuft" kommt – aber im selben Kasten, weil sie dazugehört. */
.alsnaechstes {
  border-top: var(--strich) solid var(--tinte);
  margin: 0;
  padding-top: 0.7em;
  /* Stand auf der kleinsten Stufe bei 0,7 – die Angabe, für die man aufsteht,
     war die leiseste im Kasten. */
  font-size: var(--t-text);
  text-transform: uppercase;
  line-height: 1.25;
}
.alsnaechstes b {
  display: inline-block;
  background: var(--tinte);
  color: var(--papier);
  padding: 0.05em 0.35em 0.12em;
  margin-right: 0.4em;
}

/* 8 ── Fragezettel ─────────────────────────────────────────────────── */

.zettelfeld { max-width: 40rem; margin-top: var(--luft-3); }

.feld { display: block; margin-bottom: var(--luft-3); }
.feld > .marke {
  display: block;
  font-size: var(--t-klein);
  text-transform: uppercase;
  line-height: 1.1;
  margin-bottom: 0.3em;
}
.feld > .marke .dazu { opacity: 0.75; }

/* Kleingedrucktes direkt am Knopf: die Zusicherung gehört vor die
   Entscheidung, nicht in einen Abschnitt darunter. */
.beiknopf {
  font-size: var(--t-klein);
  line-height: 1.35;
  opacity: 0.75;
  max-width: 26em;
  margin-top: 0.4em;
}
.knopfreihe .beiknopf { flex: 1 1 15em; margin-top: 0; }
.feld > .hinweis {
  display: block;
  font-size: var(--t-nebensatz);
  line-height: 1.35;
  margin-bottom: 0.45em;
  opacity: 0.75;
}

input[type="text"], textarea {
  width: 100%;
  font-family: var(--hand);
  font-size: 1.0625rem;
  line-height: 1.45;
  color: var(--tinte);
  background: var(--papier);
  border: var(--strich) solid var(--tinte);
  border-radius: 0;
  padding: 0.6rem 0.7rem;
  -webkit-appearance: none;
  appearance: none;
}
textarea { min-height: 8.5rem; resize: vertical; }
/* 0,5 ergäbe #808080 und damit nur 3,9:1 – zu wenig für Hilfetext im Feld. */
::placeholder { color: var(--tinte); opacity: 0.62; }

.wahl { display: flex; flex-wrap: wrap; border: var(--strich) solid var(--tinte); }
.wahl label {
  flex: 1 1 auto;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  font-size: var(--t-klein);
  text-transform: uppercase;
  line-height: 1.1;
  padding: 0.5em 0.7em;
  border-right: var(--strich) solid var(--tinte);
  cursor: pointer;
  text-align: center;
}
.wahl label:last-child { border-right: 0; }
.wahl input { position: absolute; opacity: 0; inset: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
/* Rückfallebene für Browser ohne :has() – dort trägt das <span> die Marke. */
.wahl input:checked + span { background: var(--tinte); color: var(--papier); }
.wahl input:focus-visible + span { outline: var(--strich) solid var(--tinte); outline-offset: 2px; }
.wahl label:has(input:checked) { background: var(--tinte); color: var(--papier); }
.wahl label:has(input:checked) span { background: none; color: inherit; }
.wahl label:has(input:focus-visible) { outline: var(--strich) solid var(--tinte); outline-offset: 2px; }
.wahl label:has(input:focus-visible) span { outline: 0; }

/* Nicht wie eine Feldmarke setzen: das Zählwerk gehört zum Feld darüber,
   nicht zum Feld darunter. Deshalb rechtsbündig und in der Hand des
   Publikums, nicht in der Plakatschrift. */
.zaehlwerk {
  display: block;
  font-size: var(--t-klein);
  text-align: right;
  opacity: 0.7;
  margin-top: 0.3em;
}
.zaehlwerk.zuviel { background: var(--tinte); color: var(--papier); opacity: 1; padding: 0 0.35em; }

/* Der Honigtopf. Menschen sehen ihn nicht, Maschinen füllen ihn aus. */
.honig {
  position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden;
}

/* Eine Formularmeldung ist kein Zwischenruf: Fließtextgröße, gemischte
   Schreibung, und der Rahmen trägt die Auszeichnung statt einer Flutung. */
.meldung {
  border: var(--strich-dick) solid var(--tinte);
  padding: var(--luft-2) var(--luft-3);
  margin-bottom: var(--luft-3);
  font-size: var(--t-text);
  line-height: 1.4;
}
.meldung a { font-weight: inherit; }
.meldung[hidden] { display: none; }

/* Die Quittung steht im Lichtkasten vom Plakat „Shitshow Deutschland“:
   Wenn der Zettel angekommen ist, geht das Schild an. */
.quittung {
  margin-top: var(--luft-4);
  text-align: center;
}
.quittung .titel { margin-bottom: var(--luft-2); font-size: var(--t-gross); }
.quittung .lauftext { margin-inline: auto; }
/* Das Zitat trägt Anführungszeichen, keinen Balken – die Plakate kennen
   keine farbige Kante als Auszeichnungsmittel. */
.abgegeben {
  font-family: var(--hand);
  font-weight: 400;
  font-size: 1.0625rem;
  line-height: 1.45;
  max-width: 32em;
  margin: 0 auto var(--luft-3);
  text-align: left;
}
.abgegeben::before { content: "„"; }
.abgegeben::after { content: "“"; }
.abgegeben:empty { display: none; }
.quittung .knopfreihe { justify-content: center; }

/* 9 ── Bewegung ────────────────────────────────────────────────────── */

/* Der eine gestaltete Moment: der Bogen wird umgeblättert. Kein Fade,
   kein Sliden – Papier klappt um seine Kante. */
@view-transition { navigation: auto; }

::view-transition-old(root) {
  animation: blatt-weg 0.26s cubic-bezier(0.22, 0.61, 0.36, 1) both;
  transform-origin: left center;
}
::view-transition-new(root) {
  animation: blatt-her 0.3s cubic-bezier(0.22, 0.61, 0.36, 1) both;
  transform-origin: left center;
}
@keyframes blatt-weg {
  to { transform: translateX(-1.2%); opacity: 0; }
}
@keyframes blatt-her {
  from { transform: translateX(1.6%); opacity: 0; }
}

/* Der zweite gestaltete Moment: das Schild geht an. Nur hier, nur einmal
   pro Mensch – die Quittung erscheint ausschließlich als Antwort auf eine
   abgeschickte Frage, nie beim Laden. Kein Fade als Seitenmöbel, sondern
   ein Lichtkasten, der Strom bekommt. */
#quittung {
  transition:
    opacity 240ms cubic-bezier(0.22, 0.61, 0.36, 1),
    transform 240ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
@starting-style {
  #quittung { opacity: 0; transform: scale(0.985); }
}

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .stempel, .graffito { transform: none; }
}

/* Druck: der Bogen soll sich ausdrucken lassen und im Foyer aushängen. */
@media print {
  /* Papier ist weiß. Der Negativmodus steht mit (0,2,0) in der Spezifität
     (`:root[data-modus="dunkel"]`, `:root:not([data-modus="hell"])`), ein
     schlichtes `:root` mit (0,1,0) kam dagegen nicht an – wer im Dunkeln
     druckte, bekam ein schwarzes Blatt, und mit der üblichen Einstellung
     „keine Hintergrundgrafiken" ein leeres. Alle drei Wähler, alle Marken. */
  :root,
  :root[data-modus="dunkel"],
  :root:not([data-modus="hell"]) {
    --tinte: #000;
    --papier: #fff;
    --voll-grund: var(--tinte);
    --voll-schrift: var(--papier);
    --voll-kante: transparent;
    --voll-kante-raster: var(--tinte);
    --voll-strich: 0px;
    --graffito-filter: brightness(1.12) contrast(1.15);
    --graffito-mischung: multiply;
    color-scheme: light;
  }
  /* Gefüllte Felder tragen ihre Aussage in der Fläche. Ohne das kippt der
     Zwischenruf in weiße Schrift auf weißem Papier. */
  .zwischenruf, .anschlagwand, .punkt.laeuft, .stempel, .knopf,
  .bogenleiste a[aria-current="page"] { print-color-adjust: exact; }
  .bogenleiste, .sprung, .raumfilter, .knopfreihe, .modus { display: none !important; }
  /* Auf Papier liegt schon Papier. Der Kopierer soll es nicht mitdrucken. */
  .umschlag::before { content: none; }
  body { font-size: 11pt; padding-bottom: 0; }
  .punkt, .aushang a { break-inside: avoid; }
  .haftung { margin-top: 1.5rem; }
}

/* ── Modusschalter ───────────────────────────────────────────────────
   Es gab einmal einen „Nachtrag Dunkelmodus" mit vier Media Queries an vier
   Stellen der Datei. Der ist fort: die Negativ-Regel steht jetzt in den
   Marken --voll-grund/-schrift/-kante/-strich ganz oben, und weil sie in
   Marken steht, lässt sie sich auch von Hand umlegen.

   Es gilt die Regel aus DESIGN.md: Im Negativ darf nur fluten, was höchstens
   eine Zeile hoch ist. Alles Größere wird zum gerahmten Feld.
   ------------------------------------------------------------------- */

/* Eine Fehlermeldung ist ein gefülltes Feld – im Negativ ein doppelt
   gerahmtes, weil sie über mehr als eine Zeile geht. */
.meldung.schlecht {
  background: var(--voll-grund);
  color: var(--voll-schrift);
  border-width: var(--strich-dick);
}

/* Der Schalter im Haftungsfuß. Drei Zustände, weil „wie das Gerät" ein
   eigener ist und nicht dasselbe wie „hell". Steht unten, weil man ihn
   einmal umlegt und dann nie wieder. */
/* Der Schalter steht zweimal im Bogen. Im Kopf ist er im Foyer erreichbar,
   ohne durch die halbe Seite zu rollen – dort ist aber erst ab 48em Platz
   neben dem Absender. Darunter gilt der im Fuß. Sichtbar ist immer einer. */
.modus {
  display: none;
  align-items: baseline;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5em;
  margin: 0 0 var(--luft-2);
  font-size: var(--t-winz);
  text-transform: uppercase;
}
.haftung .modus[data-an] { display: flex; }
.absender .modus { margin: 0; }
@media (min-width: 48em) {
  .absender { position: relative; }
  .absender .modus[data-an] {
    display: flex;
    position: absolute;
    right: var(--rand);
    top: 50%;
    transform: translateY(-50%);
    margin: 0;
  }
  .absender .modus .marke { display: none; }
  .haftung .modus[data-an] { display: none; }
}
.modus .marke { opacity: 0.75; }
.modus .schalter {
  display: flex;
  border: var(--strich) solid var(--tinte);
}
.modus button {
  font-family: var(--schrift);
  font-weight: 900;
  font-size: var(--t-winz);
  text-transform: uppercase;
  line-height: 1;
  min-height: 44px;
  padding: 0 0.85em;
  background: var(--papier);
  color: var(--tinte);
  border: 0;
  border-right: var(--strich) solid var(--tinte);
  cursor: pointer;
}
.modus button:last-child { border-right: 0; }
.modus button[aria-pressed="true"] { background: var(--tinte); color: var(--papier); }
