/* THA-Bundle, 13.08.2026: https://www.tha.de/frontend/css/bundle/FrontendAssets.min.1782285070.css (Hash 1782285070). Die Werte liegen hier,
   damit ein fremder Rebuild weder Aussehen noch Verfuegbarkeit bestimmt. */

@font-face {
  font-family: "Inter";
  src: url("inter-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-display: swap;
  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+2190, U+2191, U+2192, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Inter";
  src: url("inter-latin-ext.woff2") format("woff2");
  font-weight: 400 700;
  font-display: swap;
  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;
}

/* Flaggenzeichen fuer Windows. Eine Flagge ist im Unicode kein eigenes Zeichen,
   sondern ein Paar Regional Indicator Letters, und Windows zeichnet sie
   grundsaetzlich nicht - dort standen bis zum 08.09.2026 Kaestchen. Diese
   Schrift enthaelt ausschliesslich die Flaggen; alles andere faellt auf die
   Systemschrift zurueck, deshalb steht sie in `font-family` VOR ihr und wird
   nur an den Flaggenstellen ueberhaupt aufgerufen.

   Herkunft: country-flag-emoji-polyfill 0.1.8 (MIT), Glyphen aus Twemoji
   (CC-BY 4.0), geholt am 08.09.2026, 78 292 Bytes,
   sha256 9f04f14429bb6a9f415c7a4dd902a918... Kopie wie beim THA-Bundle und bei
   Inter (ADR-0009): ausgeliefert wird aus dem eigenen Verzeichnis, nie von
   einem fremden Server - die Seite laeuft hinter einem Reverse Proxy. */
@font-face {
  font-family: "Flaggen";
  src: url("twemoji-flaggen.woff2") format("woff2");
  font-display: swap;
  unicode-range: U+1F1E6-1F1FF;
}

:root {
  --grund: #ffffff;
  --schrift: #1e1e1e;
  --gedaempft: #5a5a5a;
  --linie: #d8d8d8;
  --akzent: #ff0350;
  --akzent-text: #e60046;
  /* Hover und Fokus auf roter Fläche: dunkler statt heller, damit Weiß darauf
     nicht unter 4,5:1 fällt (auf #b3003a sind es gut 7:1). */
  --akzent-tief: #b3003a;
  --kante: #1e1e1e;
  --badge-radius: 0.7rem;
  --zebra: #f5f5f6;
  --flaeche-ruhig: #f2f2f3;
  --zebra-auflage: rgba(0,0,0,.045);
  /* Kontrast #1e1e1e, rein / mit Zebraauflage: Blau 14,52 / 13,16;
     Türkis 14,40 / 13,04; Grün 14,58 / 13,23; Limette 14,73 / 13,34;
     Bernstein 14,74 / 13,35; Sand 14,34 / 12,97; Flieder 14,04 / 12,76;
     Mauve 14,20 / 12,92; Grau 14,38 / 13,00. Alle Werte sind ≥ 4,5:1. */
  --kat-blau: #e8f0fb;
  --kat-tuerkis: #e0f2f1;
  --kat-gruen: #e8f3e4;
  --kat-limette: #f0f3dc;
  --kat-bernstein: #fbf0d9;
  --kat-sand: #f4ede2;
  --kat-flieder: #efe9f8;
  --kat-mauve: #f7e9f2;
  --kat-grau: #eeeeef;
  --badge-grund: #e0e0e3;
  /* Die zwei hinteren Abschnitte der Verwaltungsleiste. Kraeftiger als der
     erste Entwurf: gegen das Grau der uebrigen Plaettchen (#e0e0e3) war ein
     blasser Ton im Bild nicht mehr als eine Ahnung, und eine Gruppierung, die
     man suchen muss, gruppiert nichts. Kontrast gegen --schrift (#1e1e1e):
     Blau 11,85:1, Flieder 11,38:1; gegen --gedaempft 4,90:1 und 4,71:1 - beide
     ueber 4,5:1, obwohl in der Leiste nur --schrift steht. Kein Rot: das
     traegt in dieser Anwendung die Warnung. */
  --badge-betrieb: #c9dcec;
  --badge-rechte: #e0cfee;
  --badge-verweis: #d3d3d7;
  /* Das Semesterbadge im Kopf. Der Grund ist der vorhandene Betriebston, die
     Schrift ein dunkles Blau derselben Familie: 7,76:1. Blau und nicht grau,
     damit es sich von den ruhenden Plaettchen abhebt, ohne eine Wertung zu
     tragen - gruen hiesse „in Ordnung", rot ist die Warnung. */
  --semester-text: #0f3f66;
  --badge-verweis-aktiv: #c6c6cb;
  --verwaltungsweg: #ebebed;
  --verwaltungsweg-aktiv: #dededf;
  --status-offen-grund: #fff5c2;
  --status-ok-grund: #dff3e8;
  --status-alt-grund: #ece2f5;
  /* Blasses Rot mit dem dunkleren Rotton darauf: `--akzent-text` erreicht
     darauf nur 4,0:1 und ist fuer kleine Schrift zu hell. 6,03:1. */
  --rot-blass: #ffe3ec;
  --rot-text: #b00034;
  --status-offen: #6b5d00;
  --status-ok: #126b45;
  --status-alt: #5a3b78;
  /* Die Erlaeuterung an einem Eingabefeld. Eigene Werte und ausdruecklich
     NICHT --status-ok-grund/--status-ok: gruen heisst in der Badgesprache
     dieser Seite „in Ordnung", und das behauptet eine Erklaerung nicht. Getrennt
     gehalten, damit eine spaetere Aenderung an den Statusfarben die Erklaerung
     nicht mit umfaerbt. Kontrast --erlaeuterung-text auf --erlaeuterung-grund:
     5,71:1, also ueber 4,5:1 auch bei der kleinen Schrift. */
  --erlaeuterung-grund: #e8f3e4;
  --erlaeuterung-text: #126b45;
  --erlaeuterung-linie: #cfe3c8;

  /* Die Detailspalte der Ergebnisliste: je Art der Angabe ein Ton, damit die
     Spalte sich senkrecht ueberfliegen laesst. Eigene Werte und ausdruecklich
     nicht die Status- oder Kategorientoene: Gruen heisst hier „in Ordnung",
     Rot die Warnung, und die Kategorientoene faerben die Zeile DARUNTER --
     ein Badge, das denselben Wert traegt wie seine Zeile, verschwindet darin.
     Die Gruende liegen deshalb bei 57-75 % Helligkeit und damit unter jeder
     der neun Kategorienflaechen (93-96 %).

     Die Farbe traegt nie die Aussage: jedes Plaettchen sagt in Worten, was es
     meint („2 CP", „Online", „auf Deutsch"). Sie ist ein Lesehilfsmittel, und
     die Helligkeiten sind zusaetzlich gestaffelt, damit die Ordnung auch ohne
     Farbwahrnehmung bleibt. Alle Textkontraste ueber 4,5:1, gerechnet und im
     Bild nachgesehen: Sprache 7,09:1 -- CP 6,38:1 -- SWS 6,28:1 --
     Durchfuehrung 5,99:1 -- Format 7,09:1.

     CP und SWS stehen immer nebeneinander und sind beide warm; im ersten Bild
     waren sie mit 3 Punkten Helligkeitsabstand kaum auseinanderzuhalten. Der
     SWS-Ton ist deshalb deutlich dunkler (52 % gegen 66 %) -- zwei Angaben, die
     nebeneinander stehen, muessen weiter auseinanderliegen als zwei, die sich
     nie begegnen. */
  --badge-sprache-grund: #bcd7f1;
  --badge-sprache-text: #14416a;
  --badge-cp-grund: #ecd287;
  --badge-cp-text: #5a4200;
  --badge-sws-grund: #d3bc9a;
  --badge-sws-text: #4a3520;
  --badge-durchfuehrung-grund: #a9d9d1;
  --badge-durchfuehrung-text: #0c5049;
  --badge-format-grund: #d0c1ea;
  --badge-format-text: #412870;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-width: 0;
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  color: var(--schrift);
  background: var(--grund);
  line-height: 1.5;
  letter-spacing: 0.0156em;
}

/* Enger als die Vorgabe des Browsers: dessen Abstaende (0,67em vor und nach
   einer h1) sind fuer Fliesstext gedacht. Diese Seiten sind kurze Abschnitte
   mit vielen Ueberschriften, und die Luft zwischen ihnen trennt dort nicht,
   sondern zerreisst. Nach oben bleibt mehr Abstand als nach unten, damit eine
   Ueberschrift zu dem gehoert, was ihr folgt. */
h1, h2, h3, h4 { font-weight: 500; letter-spacing: 0; margin: 1rem 0 0.35rem; }
h1 { margin-top: 0.4rem; }
h1 + p, h2 + p, h3 + p { margin-top: 0.25rem; }
h1, h2 { color: var(--akzent); }
/* Eine Überschrift, die umbricht, soll als eine Zeile gelesen werden; mit der
   Zeilenhöhe des Fließtexts (1,5) stand zwischen ihren Zeilen ein Loch, am
   deutlichsten beim langen Modultitel (Wunsch vom 11.09.2026). */
h1, h2, h3, h4, h5 { line-height: 1.2; }
a { color: var(--akzent-text); text-decoration: none; }
a:hover, a:focus-visible { text-decoration: underline; }
button, input, select, textarea { border-radius: 0; font: inherit; max-width: 100%; }

/* Die Kopfzeile ist Navigation und nicht Inhalt: sie bekommt so wenig Hoehe
   wie moeglich, damit die Liste darunter frueher beginnt. Seitlich bleibt der
   Abstand, er richtet die Zeile am Inhalt aus. */
.kopf {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: center;
  justify-content: space-between;
  padding: 0.35rem clamp(1rem, 4vw, 3rem);
  border-bottom: 2px solid var(--kante);
}
/* `align-items: center` und nicht der Vorgabewert `stretch`: das aktive Badge
   ist groesser, und gestreckt zoege es alle uebrigen auf seine Hoehe mit - dann
   waere die ganze Leiste hoeher und nur die Schrift des aktiven groesser. Der
   Unterschied soll das eine Badge sein, nicht die Leiste. Im Markup nicht zu
   sehen, im Bild sofort. */
.verwaltungsleiste { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; padding: 0.3rem clamp(1rem, 4vw, 3rem); border-bottom: 1px solid var(--linie); }
.modulziele { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.45rem 0; }
/* Der Anmeldehinweis der oeffentlichen Sicht. Ruhige Flaeche statt Rahmen:
   er ist eine Auskunft und keine Warnung - Rot traegt hier die Warnung, und
   ein Kasten ueber der Liste zoege mehr Aufmerksamkeit als die Liste selbst. */
/* Der einzige rote Kasten, der keine Warnung ist (Entscheidung vom
   17.09.2026): er sagt, dass man gerade nur zusieht, und wer sich einschreiben
   will, muss ihn sehen. Die Flaeche ist deshalb der blasse Rotton und nicht
   der gesaettigte -- auf `--akzent` (#ff0350) erreicht Weiss nur 3,90:1, und
   der Satz steht in Grundgroesse. `--rot-text` auf `--rot-blass`: 6,03:1. Den
   Nachdruck traegt die Kante in der vollen THA-Farbe, die keinen Text haelt. */
.anmeldehinweis { background: var(--rot-blass); border-left: 0.3rem solid var(--akzent);
  padding: 0.65rem 0.9rem; margin: 0 0 1rem; border-radius: 0.35rem; }
.anmeldehinweis p { margin: 0; color: var(--rot-text); font-weight: 600; }
/* Das Plaettchen im Satz. Weiss auf `--akzent-text` (#e60046): 4,72:1, im
   Zeigezustand auf `--rot-text` 7,25:1. `white-space: nowrap` haelt „Login →"
   zusammen -- der Pfeil allein am Zeilenanfang waere ein Zeichen ohne Sinn. */
a.badge-anmelden { background: var(--akzent-text); color: #fff; text-decoration: none;
  font-weight: 600; padding: 0.1rem 0.5rem; white-space: nowrap; position: relative; }
/* Das Plaettchen misst 66 x 24 px; Apple empfiehlt 44 x 44 pt, Android 48 dp -
   am Daumen ist das der wichtigste Knopf der Seite in halber Hoehe. Groesser
   darf es nicht werden: es steht MITTEN im Satz und zoege die Zeilen
   auseinander. Die Trefferflaeche waechst deshalb unsichtbar auf 44 px; unter
   ihr liegt nur Text, sie kann also keinem anderen Bedienelement den Tipp
   wegnehmen. Am Bild ist davon nichts zu sehen - gemessen, nicht geschaetzt
   (17.09.2026). */
a.badge-anmelden::after { content: ""; position: absolute; inset: -10px -4px; }
a.badge-anmelden:hover, a.badge-anmelden:focus { background: var(--rot-text); text-decoration: underline; }
/* Ein Badge, das ein Verweis ist. Es traegt die NORMALE Schriftfarbe, obwohl es
   ein Link ist: die Form sagt schon, dass man darauf klickt, und rot daneben
   waere ein zweites Signal fuer dieselbe Aussage. Dafuer ist der Grund etwas
   dunkler als beim ruhenden Badge -- die Klickbarkeit haengt damit an der
   Flaeche und nicht an der Farbe, was auch ohne Farbwahrnehmung traegt. */
a.badge-verweis {
  background: var(--badge-verweis);
  color: var(--schrift);
  text-decoration: none;
  vertical-align: middle;
}
a.badge-verweis:hover, a.badge-verweis:focus { background: var(--badge-verweis-aktiv); text-decoration: underline; }

/* Enger gesetzt und staerker gerundet als zuvor, dafuer auf dunklerem Grund:
   ein Badge soll als Plaettchen lesbar sein und nicht als Kasten mit Luft.
   Kontrast auf --badge-grund (#e0e0e3): Schrift 12,65:1, gedaempft 5,24:1,
   status-ok 4,96:1, status-alt 6,84:1, status-offen 5,00:1. Auf
   --badge-verweis (#d3d3d7) steht ausschliesslich --schrift: 11,17:1, im
   Zeigezustand 9,80:1. Alle ueber 4,5:1. */
.badge { display: inline-block; padding: 0.05rem 0.4rem; border-radius: var(--badge-radius); background: var(--badge-grund); color: var(--schrift); font-size: 0.85rem; }
/* Die Leiste zerfaellt in drei Abschnitte: Redaktionsarbeit, Betrieb, Rechte.
   Die Klassen kommen aus `verwaltung.kopfziele()` und nicht aus der Vorlage. */
.badge-betrieb { background: var(--badge-betrieb); }
.badge-rechte { background: var(--badge-rechte); }
.badge-semester { background: var(--badge-betrieb); color: var(--semester-text); font-weight: 600; }
/* Die zwei Reihen der Modulübersicht. Die Wege tragen den Betriebston der
   Leiste (--schrift darauf 11,85:1), die Arbeitsvorräte den Offen-Ton, den die
   Anwendung schon für „hier liegt etwas" führt (--status-offen darauf 5,98:1).
   Kein Rot: das trägt hier die Warnung. */
.badge-werkzeug { background: var(--badge-betrieb); }
.badge-vorrat { background: var(--status-offen-grund); color: var(--status-offen); }
/* Das aktive Ziel der Leiste. Groesser UND gruen, damit es auch ohne
   Farbwahrnehmung heraussticht. --status-ok (#126b45) auf --status-ok-grund
   (#dff3e8): 5,64:1. */
.badge-aktiv { background: var(--status-ok-grund); color: var(--status-ok); font-size: 1rem; padding: 0.15rem 0.55rem; }
.zustand-beides-offen { background: var(--status-offen-grund); color: var(--status-offen); }
.zustand-angebot-ok, .zustand-beschreibung-ok { background: var(--status-ok-grund); color: var(--status-ok); }
.zustand-alle-ok { background: var(--status-ok-grund); color: var(--status-ok); }
/* „nicht aktuell" ist weder offen noch in Ordnung: ein geprueftes, aber
   gealtertes Urteil. Eigene Farbe, damit es nicht mit „ungeprueft" verschmilzt
   - und nicht rot, denn Rot traegt in dieser Anwendung die Warnungen. */
.beschreibung-ungeprueft { background: var(--badge-grund); color: var(--gedaempft); }
.beschreibung-geprueft { background: var(--status-ok-grund); color: var(--status-ok); }
.beschreibung-nicht-aktuell { background: var(--status-alt-grund); color: var(--status-alt); }

/* Der Weg in die Verwaltung steht links in der Kopfzeile jeder Seite, aber nur
   fuer ein Konto mit Rechten. Grau und oval statt in der Akzentfarbe: er ist ein
   Dienstweg und keine Einladung - und Grau nimmt der Marke daneben nichts weg. */
.kopflinks { display: flex; align-items: baseline; gap: 0.75rem; min-width: 0; }
.verwaltungsweg {
  font-size: 0.85rem;
  padding: 0.15rem 0.7rem;
  border-radius: 999px;
  background: var(--verwaltungsweg);
  color: var(--schrift);
  white-space: nowrap;
}
.verwaltungsweg:hover, .verwaltungsweg:focus-visible { background: var(--verwaltungsweg-aktiv); }

/* Die Marke steht fett in Grundgroesse und ist damit kein "grosser Text" im
   Sinne der Kontrastregel - sie nimmt den dunkleren der beiden Akzentwerte. */
.marke { font-weight: 700; text-decoration: none; color: var(--akzent-text); }

/* Der Satz und das Badge sind EIN Bedienelement in einer Zeile. `nowrap` haelt
   sie zusammen; kuerzen darf stattdessen der Satz. Der Abstand davor ist
   groesser als ein Wortabstand, damit „Abmelden" nicht als Satzende gelesen
   wird.

   Diese Regeln standen am 08.09.2026 versehentlich IN der Medienabfrage
   `max-width: 40rem` -- also galten sie auf jedem gewoehnlichen Bildschirm
   nicht, und genau dort brach das Badge um. Wer hier etwas ergaenzt, sieht
   nach, ob er sich noch ausserhalb der Abfrage am Dateiende befindet. */
.sitzung { display: flex; flex-wrap: nowrap; gap: 1.1rem; align-items: center; min-width: 0; }
.angemeldet-als {
  color: var(--gedaempft);
  font-size: 0.9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Ohne das waere das <form> ein eigener Block und schoebe die Abmeldung
   darunter -- genau der gemeldete Umbruch. */
.sitzung form { margin: 0; display: flex; flex: 0 0 auto; }
.testbadge { background: var(--rot-blass); color: var(--rot-text); padding: 0.15rem 0.45rem;
  border-radius: var(--badge-radius); font-size: 0.85rem; white-space: nowrap; }
/* Ein Knopf, der wie ein Badge aussieht: die Browservorgaben fuer <button>
   muessen dafuer weg. `white-space: nowrap` verhindert, dass er selbst umbricht. */
.sitzung button.badge-verweis {
  border: 0;
  cursor: pointer;
  font-size: 0.85rem;
  padding: 0.15rem 0.45rem;
  white-space: nowrap;
}
/* Die zwei Betriebszustände sind grosse Schalter statt einer Einstellung mit
   nachgestelltem Speichern. Die aktuelle Klasse und `aria-pressed` im Markup
   tragen den Zustand auch ohne Farbwahrnehmung. */
.betriebsschalter { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.betriebsschalter-knopf { border: 0; cursor: pointer; font-size: 1.1rem; padding: 0.45rem 1rem; }
.betriebsschalter-aktuell { outline: 3px solid var(--kante); outline-offset: 2px; font-weight: 700; }

main { min-width: 0; padding: 1.5rem clamp(1rem, 4vw, 3rem); max-width: 72rem; }
.inhaltkopf { display: flex; flex-wrap: wrap; gap: 0.75rem 1.5rem; align-items: baseline;
  justify-content: space-between; margin-bottom: 1.5rem; }
/* Aufgeklappt legt sich die Wahl UEBER die Zeile statt sie zu dehnen: im Kopf
   schoebe sie sonst die ganze Seite nach unten, und das sieht aus wie ein
   Sprung. Ein `<select>` waere das Naheliegende und scheidet aus - es koennte
   ohne JavaScript nichts ausloesen. */
.sprache { position: relative; font-size: 1rem; }
.sprache summary { cursor: pointer; white-space: nowrap; }
.flagge, .sprache summary, .sprache > a { font-family: "Flaggen", "Inter", system-ui, sans-serif; }
.sprache > a { display: block; white-space: nowrap; padding: 0.2rem 0.5rem; }
.sprache[open] > a:first-of-type { margin-top: 0.3rem; }
.sprache[open] { z-index: 2; }
.sprache[open] > a { position: relative; background: var(--grund); }
.sitzung .sprache[open]::after { content: ""; position: absolute; inset: 1.6rem -0.5rem -0.4rem -0.5rem;
  background: var(--grund); border: 1px solid var(--linie); z-index: -1; }
.sprache a:focus-visible { outline: 2px solid var(--kante); outline-offset: 2px; }

.kategorie, .unterkategorie, .ergebnisliste .terminkurz, .ergebnisliste .lehrkraefte { color: var(--gedaempft); font-size: 0.9rem; }

/* Buchsatz statt Gitter: eine kraeftigere Linie ueber und unter der Tabelle,
   eine feine unter der Kopfzeile - und sonst keine. Senkrechte Linien und
   Zeilentrenner gibt es bewusst nicht; die Zebra-Zeichnung fuehrt das Auge
   ueber die Zeile, statt sie in Streifen zu zerlegen. Die Luft um die Linien
   gehoert dazu: ohne sie klebt der Satz an ihnen. */
main table {
  border-collapse: collapse;
  width: 100%;
  max-width: 100%;
  border-top: 2px solid var(--kante);
  border-bottom: 2px solid var(--kante);
}
main table th, main table td { text-align: left; line-height: 1.35; }
main table thead th { padding: 0.2rem 0.75rem 0.2rem 0; border-bottom: 1px solid var(--linie); }
main table td, main table tbody th { padding: 0.5rem 0.75rem 0.5rem 0; font-weight: 400; }
main table tbody tr:nth-child(even) { background: var(--zebra); }

/* Diese Regeln stehen nach den allgemeinen Tabellenregeln: bei gleicher
   Spezifitaet muss die Auflage vor der Tönung kommen, sonst verschwindet sie
   ausgerechnet auf jeder zweiten, gefaerbten Zeile. */
.ergebnisliste tbody tr:nth-child(even) { background-color: transparent;
  background-image: linear-gradient(var(--zebra-auflage), var(--zebra-auflage)); }
.ergebnisliste tbody tr.farbe-blau { background-color: var(--kat-blau); }
.ergebnisliste tbody tr.farbe-tuerkis { background-color: var(--kat-tuerkis); }
.ergebnisliste tbody tr.farbe-gruen { background-color: var(--kat-gruen); }
.ergebnisliste tbody tr.farbe-limette { background-color: var(--kat-limette); }
.ergebnisliste tbody tr.farbe-bernstein { background-color: var(--kat-bernstein); }
.ergebnisliste tbody tr.farbe-sand { background-color: var(--kat-sand); }
.ergebnisliste tbody tr.farbe-flieder { background-color: var(--kat-flieder); }
.ergebnisliste tbody tr.farbe-mauve { background-color: var(--kat-mauve); }
.ergebnisliste tbody tr.farbe-grau { background-color: var(--kat-grau); }
.ergebnisliste .titel { color: var(--schrift); font-weight: 600; }
.ergebnisliste .titel:hover, .ergebnisliste .titel:focus-visible { color: var(--akzent-text); text-decoration: underline; }
/* Gilt fuer JEDE sortierbare Kopfzeile und nicht nur fuer die Ergebnisliste:
   sechs rote Koepfe sind kein Hinweis mehr, und Rot gehoert hier den
   Warnungen. Erkennbar bleibt der Verweis ueber die Unterstreichung beim
   Zeigen und ueber den Pfeil am aktiven Kopf. */
.modultabelle th a, .angebotstabelle th a { color: var(--schrift); }
.modultabelle th a:hover, .modultabelle th a:focus-visible,
.angebotstabelle th a:hover, .angebotstabelle th a:focus-visible {
  color: var(--akzent-text); text-decoration: underline; }
.sortierpfeil { display: inline-block; margin-left: 0.2rem; width: 0.8em; }
/* Die Modulliste der Verwaltung, und zwar NUR sie: `modultabelle` traegt auch
   die Aliastabelle im Moduleditor, die vier schmale Spalten hat.

   Ohne Breitenangabe nimmt der Titel fast das ganze Blatt und die vier
   Angabenspalten kleben rechts aneinander; ohne `nowrap` bricht „derzeit nicht"
   in jeder der 1005 Zeilen um und verdoppelt ihre Hoehe. Beides steht so in
   keinem Markup - es war erst im Bild zu sehen (10.09.2026), wie schon
   „Mo 09:45-13:15" in Ticket 32. Die Kategorie bricht absichtlich weiter um:
   „Innovation, Gruendung, Selbststaendigkeit" erzwaenge sonst ihre volle
   Breite. */
/* Beides erst ab einer Breite, in der es passt. Auf 420 px sind 46 % Titel
   193 px, und die vier uebrigen Spalten brauchen mehr als der Rest hergibt -
   die Tabelle schob sich ueber den Rand und die Seite scrollte seitwaerts.
   Schmal bekommt sie deshalb ihre natuerliche Verteilung zurueck, und „derzeit
   nicht" bricht dort wieder um; das ist auf einem Telefon richtig und der
   einzige Weg. Auch das war erst im Bild zu sehen. */
@media (min-width: 700px) {
  .modulliste th:first-child { width: 46%; }
  .modulliste th:nth-child(n+3), .modulliste td:nth-child(n+3) { white-space: nowrap; }
}
.ergebnisliste td, .ergebnisliste th { vertical-align: top; }
.ergebnisliste th { text-align: center; }
.ergebnisliste td:first-child { padding-left: 5px; }
/* Der Titel bleibt die tragende Spalte und behaelt seine Breite; Platz
   schaffen die Terminspalte und die umbruchfreien Plaettchen daneben. Die
   Terminzeilen sind dafuer kurz genug, um lesbar umzubrechen - was nicht
   brechen darf, haelt ein geschuetztes Leerzeichen zusammen (`termin.py`). */
.ergebnisliste th:first-child { width: 32%; }
.ergebnisliste .terminkurz { width: 8%; }
/* `termingruppe` und NICHT `terminblock`: den Namen traegt seit Ticket 08 die
   Terminliste der Angebotsmaske, samt `margin-top: 2.5rem` weiter unten. Als
   `terminblock` erbte jede Terminzelle der Liste diese 40 px und stand zwei
   Zeilen tiefer als ihre Nachbarspalten - bei richtigem Markup, gruenen Tests
   und einer Regel `vertical-align: top`, die tat, was sie sollte. Erst im Bild
   zu sehen (17.09.2026). */
.ergebnisliste .termingruppe + .termingruppe, .ergebnisliste .termingruppe-weitere,
.ergebnisliste .termingruppe-weitere .termingruppe { margin-top: 0.3rem; }
.ergebnisliste .gruppenbeschriftung { font-weight: 600; }
.ergebnisliste .termingruppe-weitere summary { cursor: pointer; }
/* Der Abstand UNTEREINANDER entsteht bei inline-block nicht von selbst -
   ihn bestimmte die Zeilenhoehe der Zelle. Eine Flaeche mit `gap` steuert
   beide Richtungen getrennt; der Warnsatz nimmt darin eine eigene Zeile. */
.ergebnisliste .angabenzelle { display: flex; flex-wrap: wrap; gap: 0.4rem 0.35rem; }
.ergebnisliste .angabenzelle .badge { white-space: nowrap; }
/* Je Art ihr Ton. Die Gruppenzahl bleibt bewusst ohne eigene Klasse: sie zaehlt,
   statt eine Eigenschaft zu nennen, und traegt deshalb den Grundton. */
.badge-sprache { background: var(--badge-sprache-grund); color: var(--badge-sprache-text); }
.badge-cp { background: var(--badge-cp-grund); color: var(--badge-cp-text); }
.badge-sws { background: var(--badge-sws-grund); color: var(--badge-sws-text); }
.badge-durchfuehrung { background: var(--badge-durchfuehrung-grund); color: var(--badge-durchfuehrung-text); }
.badge-format { background: var(--badge-format-grund); color: var(--badge-format-text); }
.ergebnisliste .angabenzelle .warnhinweis { flex: 0 0 100%; }
.farbmuster { display: inline-block; width: 1rem; height: 1rem; border: 1px solid var(--linie); vertical-align: middle; }
.farbmuster.farbe-blau { background: var(--kat-blau); }
.farbmuster.farbe-tuerkis { background: var(--kat-tuerkis); }
.farbmuster.farbe-gruen { background: var(--kat-gruen); }
.farbmuster.farbe-limette { background: var(--kat-limette); }
.farbmuster.farbe-bernstein { background: var(--kat-bernstein); }
.farbmuster.farbe-sand { background: var(--kat-sand); }
.farbmuster.farbe-flieder { background: var(--kat-flieder); }
.farbmuster.farbe-mauve { background: var(--kat-mauve); }
.farbmuster.farbe-grau { background: var(--kat-grau); }
/* Kontrast: Treffer ok 5,64:1, leer 6,03:1. */
.trefferbadge { display: inline-block; margin: 0; padding: 0.15rem 0.45rem; border-radius: var(--badge-radius); font-size: 0.85rem; }
.treffer-ok { background: var(--status-ok-grund); color: var(--status-ok); }
.treffer-leer { background: var(--rot-blass); color: var(--rot-text); }

/* Kontrast --status-ok auf --status-ok-grund: 5,64:1. Der Kasten ist eine
   handlungsfähige Information, keine Warnung; deshalb derselbe grüne Ton wie
   bestätigte Zustände. */
.eigene-module { background: var(--status-ok-grund); color: var(--status-ok);
  padding: 0.6rem 0.9rem; margin: 0 0 1rem; border-radius: 0.25rem; }
.eigene-module p { margin: 0; }
.eigene-module ul { margin: 0.4rem 0 0; padding-left: 1.2rem; }
.eigene-module a:not(.badge) { color: var(--status-ok); font-weight: 600; }
.eigene-module .badge { margin-left: 0.25rem; }

.selbstpflege-zuordnung { margin-bottom: 1.5rem; }
.selbstpflege-zuordnung ul { margin: 0.25rem 0 0; padding-left: 1.2rem; }
.maske.selbstpflege { max-width: 80rem; }
.selbstpflege-feld { border: 0; border-top: 1px solid var(--linie); margin: 0 0 1rem; padding: 0.6rem 0 0; }
.selbstpflege-feld legend { font-weight: 600; padding-right: 0.5rem; }
.selbstpflege-feld label { display: block; min-width: 0; margin-bottom: 0.6rem; }
.selbstpflege-feld textarea { width: 100%; box-sizing: border-box; }
@media (min-width: 900px) {
  /* Deutsch und Englisch nebeneinander: eine Übersetzung schreibt man mit
     dem Original im Blick, und die Maske wird halb so lang. */
  .selbstpflege-feld { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 1rem; }
  .selbstpflege-feld legend { grid-column: 1 / -1; }
}
.maske.selbstpflege textarea { padding: 0.4rem; font: inherit; line-height: 1.45; }

/* Die Sammelpflege ist ein Arbeitsblatt: kurze Zeilen halten viele Module
   zugleich im Blick, ohne einen zweiten Tabellenstil zu erfinden. */
.modul-sammelmaske td { padding: 0.25rem 0.75rem 0.25rem 0; }
.modul-sammelmaske select { max-width: 100%; }

/* Der Arbeitsvorrat der Vorschlaege liest sich wie die Modulliste: Zebra,
   Buchsatzlinien, sortierbare Koepfe. Die Zellen stehen oben buendig -
   sie sind verschieden hoch (ein Anriss von 160 Zeichen neben einem
   Feldnamen), und mittig gesetzt fluchtet dann nichts mehr. Kleiner
   Satz ueberall ausser beim Modultitel: er ist der Anker der Zeile. */
.vorschlagstabelle td, .vorschlagstabelle th { vertical-align: top; }
.vorschlagstabelle td { font-size: 0.85rem; }
.vorschlagstabelle td.vorschlagstitel { font-size: 1rem; }
.vorschlagstabelle .hinweis { font-size: 0.8rem; }
.vorschlagstabelle details summary { cursor: pointer; }
.vorschlagstabelle .laenge { color: var(--gedaempft); white-space: nowrap; }
/* Ausgeklappt weicht der Anriss dem Volltext: sonst stuenden die ersten
   160 Zeichen zweimal untereinander. */
.langtext[open] > summary > .anriss { display: none; }
.langtext[open] > summary { color: var(--gedaempft); }
.vorschlagstabelle button { font-size: 0.85rem; white-space: nowrap; }

/* Der Titel ist der Anker der Zeile und traegt deshalb keine Verweisfarbe: bei
   68 roten Titeln untereinander ist die Farbe kein Hinweis mehr, und das Rot
   gehoert hier den Warnungen. Erkennbar bleibt er ueber die Unterstreichung
   beim Zeigen und ueber seine Stellung. */
.modultabelle td a, .angebotstabelle td a { color: var(--schrift); }
.modultabelle td a:hover, .angebotstabelle td a:hover,
.modultabelle td a:focus-visible, .angebotstabelle td a:focus-visible {
  color: var(--akzent-text); text-decoration: underline;
}

/* Das Aenderungsprotokoll: viele schmale Spalten, deshalb kleiner Satz und
   waagrecht rollbar, statt die Seite zu sprengen. */
.protokoll { font-size: 0.9rem; }
.protokoll th, .protokoll td { vertical-align: top; }
.protokoll .zeitpunkt { white-space: nowrap; }
.protokoll .gedaempft { color: var(--gedaempft); }
/* Ein Wert ist oft ein ganzer Redaktionstext. Er steht roh da - Markdown als
   Quelltext, gerendert wird nur aussen (Ticket 46) - und behaelt seine
   Umbrueche; `pre-wrap` statt `pre-line`, damit auch ein Einzug stehen bleibt.
   Eine lange Adresse bricht notfalls im Wort, statt die Tabelle zu sprengen;
   die Mindestbreite haelt die Spalte dabei lesbar. */
.protokoll .wert { white-space: pre-wrap; overflow-wrap: anywhere; min-width: 14rem; }
/* Rot bleibt der Unterschied vorbehalten; Einfuegungen sind ohne Unterstreichung lesbar. */
.protokoll del.unterschied, .protokoll ins.unterschied { color: var(--akzent-text); }
.protokoll del.unterschied { text-decoration: line-through; }
.protokoll ins.unterschied { text-decoration: none; font-weight: 600; }

.protokollblock { margin-top: 2.5rem; overflow-x: auto; }

.maske { display: grid; gap: 0.75rem; max-width: 32rem; }
.maske label { display: grid; gap: 0.25rem; }
.maske input, .maske select { padding: 0.4rem; font: inherit; }
/* Die Einstellungsmaske pflegt ausschliesslich Fliesstext - die Grundregeln zur
   Anmeldung sind ueber 500 Zeichen lang. In den 32rem der gewoehnlichen Maske
   ist das eine Schlucht: dort sind die Felder schmal genug, weil sie kurze
   Angaben tragen. Hier steht der Text so breit, wie er auch gelesen wird. */
.maske.texte { max-width: 60rem; }
.maske.texte textarea { padding: 0.4rem; font: inherit; line-height: 1.45; }

/* Die Mehrfachauswahl klappt aus. Sie steht NACH `.maske label`, weil sie
   dessen `display: grid` fuer ihre eigenen Kaestchen zuruecknimmt -- bei
   gleicher Spezifitaet gewinnt die spaetere Regel. */
.mehrfachauswahl {
  border: 1px solid var(--linie);
  border-radius: 0.25rem;
  padding: 0.3rem 0.6rem;
}
.mehrfachauswahl > summary { cursor: pointer; }
/* `.maske label` ist ein Grid: der Hinweis bekommt dadurch seine eigene
   Zeile unter der Beschriftung und steht nicht dahinter. So gewollt --
   ein Nachsatz in Klammern hinter einer fetten Beschriftung liest sich
   wie ein Teil von ihr. In der Zusammenfassung einer Mehrfachauswahl
   bleibt er dagegen inline, weil dort kein Grid darueber liegt. */
.feldhinweis { color: var(--gedaempft); font-size: 0.9rem; font-weight: 400; }
/* Der gespeicherte Stand steht gedaempft neben der Ueberschrift; er ist eine
   Auskunft und keine zweite Ueberschrift. */
.mehrfachauswahl-gewaehlt { color: var(--gedaempft); font-weight: 400; }
/* Neben- UND untereinander: die Kaestchen fliessen in Zeilen um. Der
   Zeilenabstand ist bewusst klein - es sind bis zu 20 kurze Marken, und
   Luft zwischen ihnen macht die Liste laenger, nicht lesbarer. */
.mehrfachauswahl-werte {
  display: flex;
  flex-wrap: wrap;
  gap: 0.05rem 0.9rem;
  margin-top: 0.3rem;
}
.mehrfachauswahl-werte label {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  line-height: 1.2;
}

/* Die beschreibenden Modultexte bleiben beim Speichern im Formular, auch wenn
   ihre Gruppe geschlossen ist. Die Vorschau ist nur der gespeicherte Stand. */
.feldgruppe { border: 1px solid var(--linie); border-radius: 0.25rem; padding: 0.3rem 0.6rem; }
/* Die Zusammenfassung ist EINE Zeile: sieben Felder mit je drei Zeilen
   Vorschau waeren wieder die halbe Bildschirmhoehe, die das Zuklappen gerade
   einsparen soll. Wie viel zu sehen ist, entscheidet damit die Fensterbreite
   und nicht eine Zeichenzahl im Code. `min-width: 0` gehoert dazu - ohne sie
   schrumpft ein Flex-Kind nicht unter seinen Inhalt, und `text-overflow` griffe
   nie. Erst im Bild zu sehen (10.09.2026). */
.feldgruppe > summary { cursor: pointer; display: flex; align-items: baseline; gap: 0.4rem; }
/* `display: flex` unterdrueckt den nativen Aufklappmarker - in Chromium ist er
   dann einfach weg, und ohne ihn sieht niemand, dass die Box aufgeht. Er wird
   deshalb selbst gesetzt. Im Markup nicht zu sehen, im Bild sofort
   (10.09.2026).

   Und zwar als **Form aus Raendern** und nicht als Schriftzeichen. Beide
   naheliegenden Zeichen sind durchgefallen: U+25B6 hat eine
   Emoji-Praesentation und wurde mit der mitgelieferten Emoji-Schrift als
   oranges Symbol gezeichnet; U+25B8 hat keine, dafuer fehlt Inter die Glyphe,
   und es stand ein leeres Kaestchen da. Dieselbe Frage wie bei den Flaggen
   (08.09.2026), nur zweimal andersherum. Ein Dreieck aus Raendern haengt an
   gar keiner Schrift und stellt sie nicht noch einmal. */
.feldgruppe > summary::marker, .feldgruppe > summary::-webkit-details-marker { content: ""; display: none; }
.feldgruppe > summary::before {
  content: ""; flex: none; align-self: center;
  border: 0.32rem solid transparent; border-left-color: var(--gedaempft);
  border-right-width: 0; margin-left: 0.1rem;
}
.feldgruppe[open] > summary::before {
  border-left-color: transparent; border-top-color: var(--gedaempft);
  border-right-width: 0.32rem; border-bottom-width: 0; margin-left: 0;
}
.feldgruppe-vorschau { color: var(--gedaempft); font-size: 0.9rem; font-weight: 400;
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.feldgruppe-leer { background: var(--rot-blass); color: var(--rot-text); font-size: 0.9rem; font-weight: 400; padding: 0.05rem 0.3rem; }
.feldgruppe-eingaben { display: grid; gap: 0.75rem; margin-top: 0.5rem; }

/* Die Erläuterung ist Zusatzwissen zur einen Angabe und bleibt deshalb wie
   Mehrfachauswahl und Feldgruppe aufklappbar statt die Maske aufzublähen. */
/* Die Erlaeuterung eines Eingabefelds: zugeklappt ein Plaettchen NEBEN der
   Beschriftung, aufgeklappt ein Kasten darunter. Sie steht nicht im <label>,
   sondern daneben - ein <details> darin waere ein Bedienelement im anderen
   (siehe formular.html). */
.feld-erlaeutert { display: grid; gap: 0.25rem; }
.feldkopf { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem 0.5rem; }
/* Die Reihenfolge in der Kopfzeile haengt am Zustand, und das ist der Grund
   fuer die drei `order`: zugeklappt steht das Plaettchen NEBEN der
   Beschriftung, aufgeklappt muss der Kasten hinter den Hinweis rutschen --
   sonst stuende zwischen „Modulart" und seinem Eingabefeld ein halber
   Bildschirm Text. Im Markup ist beides dieselbe Zeile. */
.feldkopf > .feldhinweis { order: 1; flex-basis: 100%; }
.feldkopf > .felderlaeuterung { order: 0; }
.feldkopf > .felderlaeuterung[open] { order: 2; }
/* Als Rasterzelle wuechse der Kasten ohne `min-width: 0` auf die Mindestbreite
   seines breitesten Inhalts, und das ist die Fristentabelle - er zoege damit
   die ganze Maske ueber ihre 32rem hinaus. Breiter sein darf allein die
   Tabelle, und nur in ihrem eigenen Rollbehaelter.

   Nachgemessen am 18.09.2026 mit dem Gegenbild: dieselbe Maske bei 420 px OHNE
   diesen Kasten laeuft genauso seitwaerts aus dem Bild. Der Ueberlauf dort ist
   der Stand der Modulmaske und nicht die Folge dieser Zeilen -- sie halten den
   Kasten nur davon ab, der naechste Grund dafuer zu werden. */
.felderlaeuterung { min-width: 0; }
.felderlaeuterung > summary {
  display: flex; align-items: baseline; gap: 0.35rem;
  width: max-content; max-width: 100%;
  background: var(--erlaeuterung-grund); color: var(--erlaeuterung-text);
  border-radius: var(--badge-radius); padding: 0.05rem 0.55rem;
  font-size: 0.8rem; cursor: pointer;
}
/* `display: flex` an einem <summary> nimmt den nativen Aufklappmarker mit - in
   Chromium ist er dann einfach weg, und ohne ihn sieht niemand, dass die Box
   aufgeht. Ersetzt wird er wie bei der Feldgruppe durch eine Form aus Raendern
   und nie durch ein Schriftzeichen: U+25B6 kommt als Emoji orange daher, und
   fuer U+25B8 fehlt Inter die Glyphe (10.09.2026). */
.felderlaeuterung > summary::marker,
.felderlaeuterung > summary::-webkit-details-marker { content: ""; display: none; }
.felderlaeuterung > summary::before {
  content: ""; flex: none; align-self: center;
  border: 0.28rem solid transparent; border-left-color: var(--erlaeuterung-text);
  border-right-width: 0;
}
.felderlaeuterung[open] > summary::before {
  border-left-color: transparent; border-top-color: var(--erlaeuterung-text);
  border-right-width: 0.28rem; border-bottom-width: 0;
}
/* Aufgeklappt nimmt der Kasten die ganze Zeile: als Flexkind neben der
   Beschriftung stuende sein Text auf deren Restbreite. */
.felderlaeuterung[open] {
  flex-basis: 100%;
  background: var(--erlaeuterung-grund);
  border-radius: var(--badge-radius);
  padding: 0.25rem 0.55rem 0.5rem;
}
.felderlaeuterung[open] > summary { background: none; padding-left: 0; padding-right: 0; }
.felderlaeuterung-inhalt { font-size: 0.8rem; color: var(--erlaeuterung-text); }
.felderlaeuterung p { margin: 0.5rem 0; }
.felderlaeuterung-tabelle { overflow-x: auto; }
.felderlaeuterung table { border-collapse: collapse; margin: 0.5rem 0; width: 100%; }
.felderlaeuterung th, .felderlaeuterung td { border: 1px solid var(--erlaeuterung-linie); padding: 0.25rem 0.35rem; text-align: left; vertical-align: top; }
.felderlaeuterung-zelle { white-space: pre-line; }

.fehler { color: var(--akzent-text); }
.hinweis { color: var(--gedaempft); }
.unterrichtssprache { font-weight: 600; }
.unterrichtssprache.warnung { color: var(--akzent-text); }
.warnhinweis { display: block; font-weight: 400; }
/* Rot sind allein die Studiengaenge: das ist die Angabe, nach der jemand
   sucht, und ein durchgehend roter Tabellenwert wuerde nichts hervorheben. */
.sperrung-namen { color: var(--akzent-text); font-weight: 600; }

/* Vier Listboxen nebeneinander, gleich hoch und ohne Aufklappzustand: eine
   `<details>`-Liste schob beim Oeffnen die Nachbarn weg und liess den Knopf
   allein stehen. `align-items: start` haelt die Kaesten oben buendig. */
.filterleiste { display: flex; flex-wrap: wrap; gap: 0.75rem 1.5rem; margin: 1rem 0; align-items: start; }
/* 14rem, weil „Innovation, Gruendung, Selbststaendigkeit" der laengste Wert ist. */
/* Eingeklappt, und aufgeklappt legt sich die Liste UEBER die Zeile: staende
   sie im Fluss, schoebe jedes Oeffnen die Nachbarn und die Tabelle nach unten.
   Der Pfeil ist die Vorgabemarke von <summary>; ein eigener braeuchte ein
   Skript oder eine zweite Wahrheit ueber den Zustand. */
.filterfeld { flex: 0 1 auto; min-width: 0; position: relative; }
.filterfeld summary { cursor: pointer; padding: 0.2rem 0.4rem; border: 1px solid var(--linie);
  border-radius: var(--badge-radius); white-space: nowrap; }
.filterfeld[open] summary { background: var(--flaeche-ruhig); }
/* Ankreuzfelder statt eines `<select multiple>`: nur so gibt es links ein
   Kaestchen und eine eigene Markierfarbe - die Auswahlfarbe eines
   Auswahlfeldes gehoert dem Betriebssystem und laesst sich nicht setzen.
   Die Liste liegt UEBER der Seite und ist so breit wie ihr laengster Wert;
   sehr lange Listen rollen in sich statt die Seite zu dehnen. */
.filterfeld > .filterliste { position: absolute; z-index: 3; margin-top: 0.25rem;
  max-height: 16rem; overflow-y: auto; white-space: nowrap;
  background: var(--grund); border: 1px solid var(--kante); padding: 0.2rem; }
.filterliste label { display: block; padding: 0.1rem 0.5rem 0.1rem 0.2rem;
  border-radius: var(--badge-radius); cursor: pointer; }
/* Der angekreuzte Wert wird zusaetzlich hinterlegt - das Kaestchen allein ist
   in einer langen Liste leicht zu uebersehen. Faellt `:has()` aus, bleibt das
   Kaestchen die Aussage; die Farbe ist Zugabe und nie der einzige Traeger. */
.filterliste label:has(input:checked) { background: var(--rot-blass); color: var(--rot-text); }
.filterabsenden { flex: 0 0 100%; display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
/* Auffaelliger als die Plaettchen daneben: er ist das Bedienelement, sie sind
   die Auskunft. */
.filterleiste button { border: 0; cursor: pointer; font-size: 1rem; padding: 0.25rem 0.9rem; }
.aktive-filter { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.filterchip { display: inline-block; padding: 0.05rem 0.45rem; border-radius: var(--badge-radius);
  background: var(--rot-blass); color: var(--rot-text); font-size: 0.8rem; text-decoration: none;
  white-space: nowrap; }
.filterchip:hover, .filterchip:focus-visible { text-decoration: underline; }

/* Das aktuelle Semester steht im Kopf jeder Seite: es ist redaktionell gesetzt
   und kann eines sein, das noch nicht begonnen hat. */
.semester { margin: 0; color: var(--gedaempft); font-size: 0.9rem; }
.semester strong { color: var(--schrift); }

/* Die Angebote eines Moduls stehen untereinander - Parallelkurse sind der
   Regelfall, nicht die Ausnahme. */
.angebot {
  padding: 0.75rem 0 0.25rem;
  border-top: 1px solid var(--linie);
}

.angebot h3 { margin: 0 0 0.5rem; font-size: 1rem; }

.abgesagt { color: var(--akzent-text); }

.angebotsblock { margin-top: 2.5rem; }

/* Die Termine eines Angebots. Jeder steht als ein gesetzter Satz da, mit der
   Art davor - eine Phase muss sich vom Praesenztermin unterscheiden lassen. */
.termine { margin-top: 0.75rem; }
.termine h4 {
  margin: 0 0 0.4rem;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--gedaempft);
}

.termin { margin-bottom: 0.5rem; }
.terminzeile { margin: 0; }

.terminart {
  display: inline-block;
  margin-right: 0.4rem;
  padding: 0 0.35em;
  border: 1px solid var(--linie);
  border-radius: 0;
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--gedaempft);
}

/* Eine Phase ist kein Praesenztermin: sie hat keine Uhrzeit und wird nicht
   besucht. Das steht nicht nur im Wort, sondern auch im Bild. */
.termin-phase .terminart {
  border-style: dashed;
  border-color: var(--gedaempft);
}

.terminblock { margin-top: 2.5rem; }

.unvollstaendig { color: var(--akzent-text); font-size: 0.85rem; }

/* Die AWP-ID ist eine Nummer, keine Prosa: in gleicher Breite steht sie
   untereinander, und Parallelkurse sind auf einen Blick als solche zu sehen. */
.aliastabelle td:first-child, .eingangskorb td:first-child {
  font-family: "Cascadia Mono", Consolas, monospace;
}

.aliasblock { margin-top: 2.5rem; }

.importlaeufe th, .importlaeufe td,
.importvorschau th, .importvorschau td {
  vertical-align: top;
}

/* Die Zeilennummer der Quelle: in gleicher Breite steht sie untereinander, und
   im Bericht ist sie die Angabe, mit der jemand in die Tabelle zurückgeht. */
.importvorschau td:nth-child(2) {
  font-family: "Cascadia Mono", Consolas, monospace;
  text-align: right;
}

.importvorschau .anmerkung {
  display: block; font-size: 0.85rem; color: var(--gedaempft);
}

/* Der Bericht ist ein Protokoll und keine Prosa - er bleibt, wie er
   festgehalten wurde, mit seinen Einrückungen. */
.importbericht {
  font-family: "Cascadia Mono", Consolas, monospace;
  font-size: 0.85rem; overflow-x: auto;
  border: 1px solid var(--linie); padding: 0.75rem;
}

/* Zwei Wege aus der Vorschau heraus, und beide sind ein Knopf: schreiben oder
   verwerfen. Nebeneinander, damit die Entscheidung eine ist. */
.entscheidung { display: flex; gap: 0.75rem; flex-wrap: wrap; margin: 1rem 0; }

/* Die Angaben sind eine zweispaltige Tabelle: Bezeichnung, Wert. Zebra statt
   Trennlinien wie bei den uebrigen Tabellen (Ticket 32), und sehr enge Zeilen
   -- es sind kurze Werte, und Luft dazwischen macht die Liste laenger, nicht
   lesbarer. Der Grauwert ist der vorhandene --zebra. */
/* Die einzige Tabelle ohne Kopfzeile, und sie traegt den Buchsatzrahmen
   trotzdem: er umfasst hier keine Titelzeile, sondern klammert die Angaben
   selbst - entschieden am Bild und nicht am Begriff. Die Beschriftungsspalte
   bleibt bei Gewicht 500: „unterhalb der Titelzeile nicht fett" redet von
   einer Titelzeile, die es hier nicht gibt. */
.angaben { width: auto; margin: 0.4rem 0 0.8rem; }
.angaben th, .angaben td { padding: 0.12rem 0.9rem 0.12rem 0.4rem; vertical-align: top; }
.angaben th { font-weight: 500; color: var(--gedaempft); white-space: nowrap; }
.anmeldung-zwischen { max-width: 48rem; }
.anmeldung-zwischen section { margin: 1.5rem 0; }
.anmeldung-zwischen p { white-space: pre-line; }
.anmeldung-quelle { font-size: 0.9rem; }
/* Der Weg zur Anmeldung ist auf zwei Seiten die Handlung, für die jemand dort
   ist - „Zur Anmeldung“ auf der Detailseite, „Hier zur Anmeldung“ ins
   Fremdsystem auf der Einschreibungsseite - und steht auf beiden als derselbe
   rote Pfeil (Wunsch vom 11.09.2026). Der Grund ist --akzent-text (#e60046)
   und nicht --akzent (#ff0350): Weiß auf der THA-Farbe sind 3,90:1, auf
   --akzent-text 4,72:1 - im Bild nicht zu sehen, nur zu messen (ADR-0009).
   Die Pfeilform schneidet `clip-path` - und der schneidet auch
   den Fokusrahmen weg, der außerhalb der Fläche läge. Der Fokus zeigt sich
   deshalb an der dunkleren Fläche und der Unterstreichung, genau wie Hover;
   ohne das sähe eine Tastaturbedienung nicht, wo sie steht. `nowrap`, weil ein
   zweizeiliger Pfeil seine Spitze auf halber Höhe des Blocks hätte. */
.anmeldeknopf { margin: 0.9rem 0 0.5rem; }
.anmeldeknopf a {
  display: inline-block; padding: 0.7rem 2.3rem 0.7rem 1.2rem;
  background: var(--akzent-text); color: white;
  font-size: 1.35rem; font-weight: 600; white-space: nowrap;
  clip-path: polygon(0 0, calc(100% - 1.1rem) 0, 100% 50%, calc(100% - 1.1rem) 100%, 0 100%);
}
.anmeldeknopf a:hover, .anmeldeknopf a:focus-visible {
  background: var(--akzent-tief); text-decoration: underline; text-underline-offset: 0.2em;
}
/* Die Werte tragen die Zeilenumbrueche der Redaktion: die Anmeldefrist des
   Losverfahrens sind drei Saetze, die drei Zeilen sein sollen, und ein
   Leistungsnachweis haengt seine Anwesenheitspflicht als eigene Zeile an. HTML
   macht daraus sonst einen Fliesstext, in dem die zweite Angabe an die erste
   klebt. `pre-line` erhaelt allein die Umbrueche und laesst alles andere
   umfliessen -- `pre` verboete den Umbruch am Rand und sprengte den schmalen
   Schirm. */
.angaben td { white-space: pre-line; }

/* Die Textauszeichnung erzeugt eigene Bloecke. Einzelne Zeilenumbrueche im
   redaktionellen Fliesstext bleiben sichtbar wie in `.angaben td` - vorher
   stand der ganze Text in einem <p> ohne `pre-line`, und selbst eine Leerzeile
   verschwand im Fliesstext. Listen bekommen nur
   den knappen Einzug des uebrigen Buchsatzes. */
.beschreibung p, .beschreibung blockquote { white-space: pre-line; }
/* Eigene Groessen, weil die Rangfolge sonst kippt: `h1, h2, h3, h4` oben
   setzt 500 und keine Groesse, `h4` stuende damit wie Fliesstext da, waehrend
   `h5` vom Browser fett bleibt - „##" saehe gewichtiger aus als „#". */
.beschreibung h4 { font-size: 1.1rem; font-weight: 600; margin: 0.9rem 0 0.2rem; }
.beschreibung h5 { font-size: 1rem; font-weight: 600; margin: 0.8rem 0 0.2rem; }
/* Absätze enger als die Browservorgabe (1em). Die Liste nimmt sich oben ihren
   bisherigen Abstand selbst, damit der Übergang Absatz → Liste bleibt, wie er
   war - der war ausdrücklich richtig. Nach einer Überschrift entscheidet dagegen
   das folgende Element: sein oberer Rand fällt mit dem unteren der Überschrift
   zusammen und überdeckte ihn sonst. Der Feldname (h2) gehört dazu, weil
   `.beschreibung p` spezifischer ist als `h2 + p` oben und den Abstand unter
   „Inhalt“ sonst vergrößerte. Diese Regel steht zuletzt: gleiche Spezifität,
   also entscheidet die Reihenfolge. */
.beschreibung p { margin: 0.5em 0; }
.beschreibung ul, .beschreibung ol { margin: 1em 0 0.6em; padding-left: 1.5rem; }
.beschreibung :is(h2, h4, h5) + * { margin-top: 0.25rem; }
/* Nicht bloss `monospace`: fuer genau diesen Namen setzen die Browser 13px
   statt der umgebenden Groesse, und der Code stand sichtbar kleiner da. */
.beschreibung code { font-family: ui-monospace, Consolas, "Liberation Mono", monospace; font-size: 0.92em; }
.beschreibung blockquote { margin: 0.5rem 0; padding-left: 0.8rem; border-left: 0.2rem solid currentColor; }

/* Die Markdown-Anleitung stellt Rohtext und Ergebnis direkt gegenüber. Die
   Mindestbreite null lässt lange Adressen in beiden Spalten umbrechen, statt
   das Raster auf einem schmalen Bildschirm über seinen Container zu drücken. */
.anleitung-textauszeichnung-kopf, .anleitung-textauszeichnung-zeile {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1rem;
}
.anleitung-textauszeichnung-kopf { margin: 1.5rem 0 0.4rem; font-weight: 600; }
.anleitung-textauszeichnung-zeile { margin: 0.6rem 0 1.5rem; }
/* Quelltext bleibt als solcher lesbar, darf aber bei langen Zeilen am Rand
   umbrechen. Schrift und Größe entsprechen dem Inline-Code der Detailseite. */
.anleitung-textauszeichnung-zeile pre {
  margin: 0; padding: 0.8rem; min-width: 0; overflow-wrap: anywhere;
  white-space: pre-wrap; background: var(--flaeche-ruhig);
  font-family: ui-monospace, Consolas, "Liberation Mono", monospace; font-size: 0.92em;
}
.anleitung-textauszeichnung-zeile .beschreibung { min-width: 0; }
.anleitung-textauszeichnung-verweis { margin: 1.2rem 0 0.5rem; }
@media (max-width: 699px) {
  .anleitung-textauszeichnung-kopf { display: none; }
  .anleitung-textauszeichnung-zeile { grid-template-columns: minmax(0, 1fr); gap: 0.5rem; }
}

/* Die Ankreuzgitter schrumpfen auf ihren Inhalt; die Zellen bleiben eng, aber
   das ganze Feld bleibt anklickbar: der Abstand liegt am Label, nicht an der
   Zelle. `maske` wuerde dessen Raster aufbrechen. */
.rollenmatrix, .kontenmatrix { width: auto; margin: 0.4rem 0 0.8rem; }
.rollenmatrix th, .rollenmatrix td, .kontenmatrix th, .kontenmatrix td { padding: 0; line-height: 1.2; }
.rollenmatrix thead th, .kontenmatrix thead th { padding: 0.25rem 0.4rem; vertical-align: bottom; }
/* Ein Matrixkopf ist so breit wie sein Wort, eine Matrixspalte so breit wie ein
   Ankreuzfeld - gerade steht der Kopf also quer zu dem, was darunter passiert.
   Bei fuenf Rollen wurde die Tabelle dadurch breiter als das Fenster.
   `vertical-rl` plus die halbe Drehung laesst den Text von unten nach oben
   laufen; nur `vertical-rl` liefe von oben nach unten und waere im Deutschen
   ungewohnt.

   **Ohne feste Hoehe**, und das ist der zweite Anlauf: eine feste liess ueber
   dem kurzen „Studi" eine Handbreit Luft bis zur oberen Linie. Bei
   `writing-mode` nimmt das Element die Laenge seines Textes von selbst, und
   `vertical-align: bottom` am Kopf stellt alle auf dieselbe Grundlinie. */
.drehkopf { writing-mode: vertical-rl; transform: rotate(180deg); white-space: nowrap;
  display: inline-block; text-align: left; font-weight: normal; }
/* Der Kopf steht ueber SEINEM Knopf und nicht daneben. Zwei Dinge zogen ihn
   nach links: `main table th` richtet linksbuendig aus, und der Kopf trug ein
   anderes seitliches Polster (0,4rem) als die Zelle darunter (0,6rem am Label).
   Beides gleichgezogen - sonst bestimmt mal der Kopf, mal die Zelle die
   Spaltenbreite, und die Mitte liegt je Spalte woanders. Nur die gedrehten
   Koepfe: „Recht", „Konto" und „Personenbezug" bleiben linksbuendig. */
.rollenmatrix thead th.drehspalte, .kontenmatrix thead th.drehspalte {
  text-align: center; padding-left: 0.6rem; padding-right: 0.6rem; }
.rollenmatrix .drehkopf code { font-size: 0.9rem; }
/* Die Bezeichnungen als eigene Tabelle unter der Matrix statt in ihren Koepfen:
   dort kostete jede ein Eingabefeld an Spaltenbreite. Umbenennen bleibt
   moeglich (Ticket 40), und beides geht in einem Speichervorgang. */
.rollennamen { width: auto; margin: 0.4rem 0 0.9rem; }
.rollennamen th, .rollennamen td { padding: 0.2rem 0.9rem 0.2rem 0; }
.rollennamen tbody th { font-weight: normal; }
.rollennamen input { font: inherit; padding: 0.25rem 0.4rem; width: 14rem; }
.rollenprotokolle > summary { cursor: pointer; margin: 0.6rem 0; }
.rollenprotokolle h3 { margin: 1rem 0 0.3rem; font-size: 1rem; }
/* Wie die Felder der Maske: ohne diese Zeile trägt das Eingabefeld die
   Browservorgabe und sitzt neben den übrigen Feldern der Seite wie ein Fremder. */
.rollenmatrix thead input { max-width: 10rem; padding: 0.2rem; font: inherit; }
.rollenmatrix tbody th, .kontenmatrix tbody th { padding: 0.2rem 0.6rem 0.2rem 0.4rem; }
/* Die Zeilenfarbe steht am `tr` und nicht nur auf jeder zweiten: die erste
   Spalte klebt beim Rollen fest (siehe unten) und erbt sie von dort. Ohne
   gesetzte Farbe auf den ungeraden Zeilen schiene der Inhalt hindurch. */
.rollenmatrix tbody tr, .kontenmatrix tbody tr { background: var(--grund); }
.rollenmatrix td label, .kontenmatrix td label { display: block; padding: 0.2rem 0.6rem; text-align: center; }
/* Auf dem schmalen Schirm rollt die Matrix in ihrem Behälter (`main table`
   weiter unten). Rollte die Beschriftungsspalte mit, stünde man vor einem Feld
   aus Kästchen ohne Zeilennamen - das Bild zeigt es, das Markup nicht. */
.rollenmatrix tbody th, .kontenmatrix tbody th,
.rollenmatrix thead th:first-child, .kontenmatrix thead th:first-child {
  position: sticky; left: 0; background: inherit; z-index: 1;
}
.rollenmatrix thead th:first-child, .kontenmatrix thead th:first-child { background: var(--grund); }
/* Ein Name gehoert in eine Zeile. Mit der Spalte aus Ticket 45 wird die Matrix
   enger, und „Antje Krumme (schaafa)" brach dreizeilig um, waehrend rechts Platz
   frei blieb - im Markup nicht zu sehen, im Bild sofort. */
.kontenmatrix tbody th { white-space: nowrap; }
.kontenmatrix .kontoaktion { padding: 0.2rem 0.4rem; text-align: left; }
.kontenmatrix .kontoaktion small { display: block; white-space: nowrap; }

/* Im Dozierendenabgleich standen Haekchen, Name, Kennung, Herkunft und
   Radioknopf als ein durchlaufender Satz in einer Zelle und brachen mitten im
   Namen um; welches Bedienelement wofuer steht, war nicht mehr zu erkennen.
   Im Bildcheck vom 13.09.2026 am Serverabzug aufgefallen, im Markup nicht zu
   sehen und von keinem Test. Eine Zeile je Person, die Verantwortung rechts. */
.vorschlagszeile { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 0.75rem; padding: 0.15rem 0; }
.vorschlagsperson { flex: 1 1 16rem; }
.vorschlagsherkunft { color: var(--gedaempft); font-size: 0.85rem; white-space: nowrap; }
/* Die Zuordnung „Radioknopf gehoert zu dieser Person" darf nicht am Umbruch
   haengen - getrennt gelesen stuende ein nacktes „verantwortlich" unter einem
   fremden Namen. */
.vorschlagsverantwortung { white-space: nowrap; }
/* Aus der Modulmaske angesprungen: bei 73 Modulen im Vorrat muss die Zeile
   auch dann zu finden sein, wenn sie am Seitenende nicht ganz nach oben rollt. */
.dozierendenabgleichvorschlaege tr:target { outline: 2px solid var(--akzent); outline-offset: -2px; }
.vorschlagsentscheidung { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; margin-top: 0.45rem; padding-top: 0.35rem; border-top: 1px solid var(--linie); }

@media (max-width: 40rem) {
  main table { display: block; overflow-x: auto; }
  .kopf { align-items: center; }
  /* Auf schmalen Schirmen darf der Satz weichen, das Badge nicht: es ist das
     Bedienelement, der Satz nur die Auskunft. */
  .angemeldet-als { max-width: 45vw; }
  .filterleiste button { width: 100%; }
  .unterrichtssprache { font-size: 1rem; }
  .maske, .maske fieldset { max-width: none; min-width: 0; }
}
