/* BiStat — Gestaltung
   ============================================================================
   Angelehnt an so.ch: dunkles Schieferblau statt Schwarz, Solothurner Rot als
   scharfer, sparsamer Akzent, blasse Minttöne für Flächen, humanistische
   Grotesk in kräftigen Schnitten, grosszügiger Weissraum, Eingabefelder als
   Unterstrich statt als Kasten.

   Was bewusst NICHT übernommen wird: Bildwelten, Kacheln mit Kennzahlen,
   Diagramme. Kickoff-Folie 7: «Wir benötigen keine Cockpits! Keine schönen
   Darstellungen! Kein Marketinginstrument! Ein technisches Arbeitsinstrument!»
   Die Gestaltungssprache des Kantons, der Inhalt einer Arbeitsoberfläche.

   Der typografische Leitgedanke: **Kennungen und Zahlen sind Daten, keine
   Prosa.** AHV-Nummer, BUR-Nummer, Klassenbezeichnung und jeder Zahlenwert
   stehen mit Tabellenziffern, damit die Spalten untereinander stehen und die
   führende Null erkennbar bleibt. Geschützt wird sie allerdings nicht von der
   Schrift, sondern beim Einlesen — alles als Text, nie als Zahl (docs/04,
   Grundsatz G4); die Typografie zeigt nur, was dort entschieden wurde.

   Bis 0.31.1 trug diese Rolle eine zweite Familie, Fira Mono. Seit 0.32.0
   spricht die ganze Anwendung in Fira Sans: die Tabellenziffern kommen aus
   `font-variant-numeric: tabular-nums` an `body` und leisten dasselbe,
   Kennungen werden dabei 17 % und Beschriftungen 23 % schmaler (gemessen),
   und bei achtzehn Spalten ist gesparte Laufweite Datenfläche.

   Schriften: Fira Sans, SIL Open Font License 1.1, mitgeliefert
   unter static/schriften/. Selbst ausgeliefert, weil die Content-Security-
   Policy des Proxy `default-src 'self'` setzt — externe Schriftdienste sind
   gesperrt, und eine Anwendung in der privaten Zone darf ohnehin nicht vom
   Netz abhängen.
   ========================================================================= */

/* --- Schriften ----------------------------------------------------------- */

@font-face {
  font-family: "Fira Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/schriften/FiraSans-400-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+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Fira Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/schriften/FiraSans-400-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;
}
@font-face {
  font-family: "Fira Sans"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("/static/schriften/FiraSans-500-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+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Fira Sans"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("/static/schriften/FiraSans-500-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;
}
@font-face {
  font-family: "Fira Sans"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("/static/schriften/FiraSans-700-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+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Fira Sans"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("/static/schriften/FiraSans-700-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;
}

/* --- Marken ---------------------------------------------------------------
   Drei Flächen und ein Akzent. Mehr trägt eine Erhebungsmaske nicht.

   Rot ist Kennzeichen und Warnung — nichts sonst. Es markiert die Herkunft
   (Kantonsstreifen) und den Fehler. Wäre es zusätzlich Schmuck, verlöre der
   Fehler seine Stimme. Deshalb ist die Hauptschaltfläche nicht mehr rot,
   sondern in der Tinte des Hauses: der einzige rote Ton auf einer Maske soll
   der Befund sein.                                                          */

:root {
  --tinte:        #1f3348;  /* Schieferblau, Fliesstext und Titel        */
  /* Die zwei leisen Toene sind nachgemessen, nicht geschaetzt: --tinte-leise
     lag auf der Werkbank bei 4.46:1 und auf Mint bei 4.40:1, --tinte-still
     ueberall bei rund 2.7:1. Beide beschriften Dinge, die man lesen koennen
     muss — die Spur «SCHULE»/«ERHEBUNG» in der Titelleiste ist keine Zierde,
     sondern die von [DA 4.3.2.2] verlangte Anzeige. Jetzt: --tinte-leise
     4.66:1 auf der Werkbank, --tinte-still 4.68:1 auf Papier. */
  --tinte-leise:  #5a6e80;  /* Zweitrangiges, Beschriftungen  4.66:1 Werkbank */
  --tinte-still:  #667684;  /* Hinweise, Platzhalter          4.68:1 Papier   */

  --rot:          #e1001a;  /* Solothurner Rot: Kennzeichen und Fehler   */
  --rot-tief:     #b30015;
  /* Ocker und Gruen stehen auch auf ihrer eigenen, blassen Marke (.marke-ocker,
     .marke-gruen). Dort — nicht auf Papier — ist der Kontrast am knappsten:
     zuvor 3.97:1 und 3.84:1, beide unter AA. Die Toene sind so weit vertieft,
     dass sie auf ihrer Marke tragen, ohne zu Rot hinueberzukippen. */
  --ocker:        #965f00;  /* Warnung        4.77:1 auf der Ockermarke  */
  --gruen:        #26684c;  /* Erledigt       5.09:1 auf der Mintmarke   */

  /* Der Rücken: die Statusleiste, die die Anwendung unten abschliesst. Eine
     Nuance tiefer als die Tinte, damit Weiss darauf ruhig steht. */
  --ruecken:       #16283a;
  --ruecken-text:  #c6d3dd;  /*  8.9:1 auf dem Rücken  */
  --ruecken-still: #8fa3b4;  /*  5.6:1 auf dem Rücken  */
  /* Auf dunklem Grund tragen die Statustöne eigene, aufgehellte Werte —
     --rot erreicht dort nur 2.1:1 und wäre als Meldung unlesbar. */
  --ruecken-fehler:  #ff8f9b;
  --ruecken-warnung: #eab765;
  --ruecken-gut:     #74c8a0;
  /* Ueberfahren und gedrueckt auf dem Ruecken. --werkbank-tief und
     --mint-tief sind Toene fuer helle Flaechen und stuenden hier als heller
     Fleck; auf Dunkel hellt man den Grund auf, statt ihn zu vertiefen. */
  --ruecken-tief:    #22394e;
  --ruecken-hoch:    #2d475e;

  /* Die Werkbank: alles Gerät — Titelleiste, Seitenleiste, Kontextspalte. */
  --werkbank:      #eef1f5;
  --werkbank-tief: #e3e9ef;  /* überfahrener Eintrag in der Leiste */
  --kante-zart:    #d6dee6;  /* Fuge zwischen zwei Flächen, kein Bedienelement */

  --papier:       #ffffff;
  --flaeche:      #f5f7f8;  /* kühles Hellgrau                           */
  --mint:         #eaf1ed;  /* blasses Grün der so.ch-Karten             */
  --mint-tief:    #d7e5dc;
  --linie:        #d8dee3;  /* Trennlinien — reine Gliederung            */
  --linie-zart:   #edf1f4;
  /* Begrenzung eines Bedienelements. WCAG 2.1 SC 1.4.11 verlangt dafür 3:1
     gegen den Hintergrund; --linie erreicht nur 1.36:1 und taugt deshalb für
     Tabellenlinien, nicht für die Kante eines Knopfs oder Eingabefelds.
     Der frühere Wert #7e8f9e traf die 3:1 nur auf Papier (3.33:1). Suchfeld
     und Abmeldeknopf stehen aber auf der Werkbank, und dort lag er bei
     2.94:1 — unter der Schwelle, an der Stelle, die man auf jeder Maske
     sieht. #748596 trägt auf allen vier Flächen. */
  --kante:        #748596;  /* 3.79 Papier · 3.53 Fläche · 3.35 Werkbank */
  --fokusflaeche: rgb(225 0 26 / 0.16);

  /* Die Gerätestimme hatte bis 0.31.1 eine eigene Schriftfamilie (Fira Mono).
     Sie spricht jetzt in Fira Sans wie alles andere und wird allein durch
     Grösse, Versalien und Sperrung kenntlich — gemessen sind Kennungen darin
     17 % und Beschriftungen 23 % schmaler, und bei achtzehn Spalten ist das
     Datenfläche. Die Spaltenbündigkeit haengt nicht an der Laufweite: `body`
     setzt `font-variant-numeric: tabular-nums`, und Fira Sans fuehrt die
     Tabellenziffern mit (gemessen: 0123456789 und 1111111111 gleich breit). */

  --spur:         0.72rem;  /* Beschriftungsgrösse                       */
  --rand:         clamp(1rem, 3vw, 2rem);
  --breite:       74rem;    /* Lesebreite des Inhalts                    */

  /* Die Anwendung hat genau eine Erscheinung: Rücken dunkel, Werkbank grau,
     Papier weiss. Ohne diese Angabe gilt sie dem Browser als «normal», und
     er entscheidet selbst — auf einem dunkel gestellten System malt er seine
     eigene Grundfläche dunkel, und zwar in der Lücke zwischen zwei Masken,
     bevor das Stylesheet der neuen greift. Genau das sieht man beim Klicken
     als Blinken. Mit der Angabe steht die Grundfläche von Anfang an fest;
     Rollbalken und Bedienelemente des Browsers kommen dazu hell statt
     dunkel auf weissem Papier. */
  color-scheme: light;
}

/* Die Farbe gehört an das Wurzelelement, nicht nur an <body>. Der Browser
   malt die Grundfläche, bevor er <body> kennt; erbt sie erst von dort, ist
   sie bei jedem Maskenwechsel für einen Moment unbestimmt. */
html { background: var(--papier); }

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: "Fira Sans", "Segoe UI", system-ui, sans-serif;
  font-size: 15px;
  line-height: 1.5;
  color: var(--tinte);
  background: var(--papier);
  font-variant-numeric: tabular-nums;
}

/* --- Der Aufriss ----------------------------------------------------------
   Die Schale steht auf Fensterhöhe fest; nur das Blatt in der Mitte rollt.
   Damit wandert beim Blättern durch 103 Lieferungen kein Bedienelement —
   Spur, Leiste, Kontext und Statusleiste stehen, wo sie standen.

   Die Klappzustände sind Spaltenbreiten, keine Verschiebungen. Der Server
   kennt sie aus dem Cookie (ansicht.go) und schreibt sie an das <body>,
   bevor der erste Strich fällt: nichts blitzt auf, nichts rückt nach.
   Bewusst ohne Übergang — eine wandernde Spaltenkante ist genau das
   Herumspringen, das hier abgestellt gehört.                              */

body.werkbank {
  /* Schmaler als die 15rem von vorher: die Leiste traegt kurze Ziele, die
     Tabellen daneben achtzehn Spalten. Jedes Rem, das die Navigation nicht
     braucht, fehlt den Daten. */
  --leiste-breite: 12rem;
  display: grid;
  grid-template-columns: var(--leiste-breite) minmax(0, 1fr);
  grid-template-rows: 3px auto auto minmax(0, 1fr) auto;
  grid-template-areas:
    "marke  marke"
    "titel  titel"
    "leiste reiter"
    "leiste inhalt"
    "status status";
  height: 100dvh;
  overflow: hidden;
}
body.leiste-zu { --leiste-breite: 0px; }
body.leiste-zu .seitenleiste { display: none; }

/* Ohne Anmeldung gibt es kein Gerät — nur Marke, Titelleiste, Blatt, Fuss. */
body.freiflaeche {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: 3px auto 1fr auto;
  grid-template-areas: "marke" "titel" "inhalt" "status";
  min-height: 100dvh;
}
body.freiflaeche main { overflow: visible; }
body.freiflaeche .bogen { margin: 0 auto; }

/* --- Der Wechsel von Maske zu Maske ---------------------------------------
   Ein Klick holte bisher ein neues Blatt: der Browser raeumte die alte Seite
   weg, malte kurz Weiss und baute die neue von oben auf. Das Geraet — Marke,
   Titelleiste, Leiste, Fuss — steht auf jeder Maske an derselben Stelle und
   blinkte trotzdem jedes Mal mit. Wer durch 103 Lieferungen blaettert, sieht
   das hundertmal.

   Der Seitenuebergang laesst die alte Ansicht stehen, bis die neue fertig
   ist, und blendet dann um. Das Geraet traegt eigene Namen und bleibt dabei
   stehen; nur das Blatt wechselt. Kein Skript, keine Abhaengigkeit
   (ADR-007) — eine Regel im Stylesheet, und wo der Browser sie nicht kennt,
   navigiert er wie bisher.

   Nur eine Blende, keine Bewegung: eine Maske, die hereinfaehrt, waere genau
   das Herumspringen, das die feste Schale abstellen soll. 120 ms ist die
   Dauer, bei der der Wechsel ruhig wirkt und trotzdem niemand wartet.      */

@view-transition { navigation: auto; }

.kantonsmarke { view-transition-name: marke; }
.titelleiste  { view-transition-name: titelleiste; }
.seitenleiste { view-transition-name: seitenleiste; }
.statusleiste { view-transition-name: statusleiste; }
main          { view-transition-name: blatt; }

::view-transition-group(*) { animation-duration: 120ms; }
::view-transition-old(*), ::view-transition-new(*) { animation-timing-function: ease; }

/* Kennungen und Zahlen: Tabellenziffern, damit Spalten untereinander stehen
   und die führende Null nicht in einer schmaleren Ziffer verschwindet.
   `font-family: inherit` ist bei code, kbd, pre und samp keine Formsache: die
   Vorgabe des Browsers setzt dort monospace, und die waere nach dem Wegfall
   von Fira Mono die Systemschrift — eine fremde Stimme mitten im Satz. */
code, kbd, pre, samp { font-family: inherit; }
code, .kennung, .anzahl, td.anzahl {
  font-variant-numeric: tabular-nums;
  font-size: 0.9em;
}
code {
  background: var(--flaeche);
  padding: 0.08em 0.35em;
  border-radius: 2px;
  color: var(--tinte);
}

a { color: var(--tinte); text-decoration-color: var(--linie); text-underline-offset: 0.2em; }
a:hover { text-decoration-color: var(--rot); }

/* --- Ueberfahren blendet, es springt nicht --------------------------------
   Bis 0.57.0 trug keine der sechsundfuenfzig :hover-Regeln einen Uebergang.
   Wer im Schularten-Raster eine Zelle ueberfuhr, loeste damit zwei harte
   Wechsel im selben Augenblick aus: den Zeilengrund
   (.datentabelle tbody tr:hover td) und den Ring der bearbeitbaren Zelle
   (.bearbeitbar:hover). Zwei Dinge, die gleichzeitig anspringen, liest man
   als Zucken und nicht als Antwort — und im Erfassungsraster faehrt der
   Zeiger ueber achtzehn Spalten, also zuckt es achtzehnmal.

   Geblendet wird ausschliesslich ueber Malwerte: Grund, Kante, Schatten,
   Schrift, Unterstreichung. Kastenmasse ruehrt diese Regel nicht an. Ein
   Uebergang auf `padding` oder `border-width` waere genau das Schieben, das
   Gestaltungsgrundsatz «Beim Klicken bewegt sich nichts» (CLAUDE.md 1)
   ausschliesst — er schoebe es sogar in Zeitlupe. Deshalb steht hier kein
   `transition: all`: die Kurzform naehme jede kuenftig hinzukommende
   Eigenschaft blind mit, auch die verbotenen.

   90 ms, nicht die 120 ms des Seitenuebergangs. Der Zeiger ist schneller als
   eine Navigation; was ihm merklich nachlaeuft, wirkt zaeh statt ruhig. Die
   Sammelregel unter `prefers-reduced-motion` weiter unten setzt die Dauer
   ohnehin auf 0.01 ms — wer keine Bewegung will, behaelt den harten Wechsel.

   Die Liste ist zum Erweitern gedacht: eine neue Zustandsfamilie haengt sich
   hier ein, statt eine zweite Uebergangsregel aufzumachen. Nur Malwerte
   dazunehmen, und die Dauer aus --blende beziehen. */

:root { --blende: 90ms; }

a, button, a.knopf, summary, kbd,
.navklapper, .navliste a, .klappschalter, .bereichsliste a, .klassenbaum a,
.datentabelle tbody td, .blatt tbody td, .bearbeitbar,
.typwahl a, .wertelink, .loesen, .ansichtswahl a, .sichtwahl a,
.tafelkarte, .vorlage-bezug .sym, .musterkopf,
.schnell-treffer, .zeichen, .werkzeug, .ablaufsprung,
.reiter .reiter-titel, .reiter-zu,
.kopf-marke, .cm-sortieren a, .cm-eintrag,
.dateiwahl-knopf, input[type="checkbox"].meldung-weg,
input[type="file"]::file-selector-button {
  transition:
    background-color        var(--blende) ease-out,
    border-color            var(--blende) ease-out,
    box-shadow              var(--blende) ease-out,
    color                   var(--blende) ease-out,
    text-decoration-color   var(--blende) ease-out;
}

:focus-visible {
  outline: 2px solid var(--rot);
  outline-offset: 2px;
}

/* --- Die Werkbank ---------------------------------------------------------
   Drei Flächen, und man weiss immer, auf welcher man steht:

     Rücken   dunkles Schieferblau — die Statusleiste, die die Anwendung
              unten abschliesst wie die Kante eines gebundenen Bandes. Nur
              hier wird es dunkel.
     Werkbank kühles Grau — Seitenleiste, Titelleiste, Kontextspalte. Alles
              Gerät, nichts Inhalt.
     Papier   Weiss — und darauf ausschliesslich die Daten.

   Der alte Aufbau kannte diese Trennung nicht: Navigation, Kopf und Tabelle
   standen auf demselben Weiss, und dadurch stand nichts. Figur und Grund
   sind hier die ganze Gestaltung; alles Weitere ist Zurückhaltung.

   Die Schale ist auf Fensterhöhe festgenagelt, und nur das Blatt in der
   Mitte rollt. Damit wandert beim Blättern durch 103 Lieferungen kein
   einziges Bedienelement — das ist der Grund für die Bauart, nicht der
   Effekt.                                                                 */

/* Der Kopfstreifen trägt die Farbe der Statusleiste: Deckel und Boden fassen
   das Blatt als ein Rahmen ein, statt oben ein zweites Signal zu setzen. */
.kantonsmarke { grid-area: marke; background: var(--ruecken); }

/* --- Der Streifen sagt, dass der Server arbeitet --------------------------
   Zwischen Klick und neuer Maske lag bisher nichts. Bei «Offene jetzt
   versenden (12)» oder «Prüfen» ueber eine ganze Lieferung sind das
   Sekunden, in denen die Oberflaeche behauptet, es sei nichts geschehen —
   und der zweite Klick verschickt die zwölf Mails ein zweites Mal.
   absenden.js haelt den zweiten Klick auf und setzt hier oben `data-arbeitet`.

   Die Anzeige braucht keinen eigenen Platz: der drei Pixel hohe Streifen des
   Kantons liegt ohnehin ueber jeder Maske. Er bekommt fuer die Dauer der
   Abfrage ein helles Stueck, das durchlaeuft — kein Kasten kommt dazu, kein
   Mass aendert sich (CLAUDE.md §1).

   Ein Zehntel Sekunde Verzug: eine Maske, die sofort da ist, soll nicht
   vorher noch blinken.

   Hell, nicht rot. Rot ist auf dieser Oberflaeche Kennzeichen und Fehler
   (CLAUDE.md §7); ein roter Laufbalken haette gesagt, es sei etwas kaputt. */
.kantonsmarke { position: relative; overflow: hidden; }
body[data-arbeitet] .kantonsmarke::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to right, transparent, var(--ruecken-still), transparent);
  animation: arbeitsstreifen 1.1s 0.1s linear infinite both;
}
@keyframes arbeitsstreifen {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}
/* Wer keine Bewegung will, bekommt trotzdem eine Antwort: derselbe Streifen
   steht dann still und hell da. Die Sammelregel weiter unten setzt jede
   Animationsdauer auf 0.01ms — ohne diese Zeile waere die Anzeige fuer genau
   die Leute weg, die sie am ehesten brauchen. */
@media (prefers-reduced-motion: reduce) {
  body[data-arbeitet] .kantonsmarke::after {
    animation: none; transform: none;
    background: var(--ruecken-still); opacity: 0.7;
  }
}

/* Das Formular, das gerade laeuft: der Zeiger sagt es, und ein zweiter Klick
   geht ins Leere. `aria-busy` traegt dieselbe Auskunft ans Vorlesegeraet.
   Weder Farbe noch Mass des Knopfs aendern sich — er soll nicht aussehen,
   als waere er gesperrt, sondern als arbeite er. */
form[data-arbeitet] { cursor: progress; }
form[data-arbeitet] button[aria-busy="true"] { cursor: progress; }

/* Sinnbilder sind gezeichnet, nicht gefüllt: Strich in der Farbe des Texts,
   damit ein Sinnbild überall dieselbe Helligkeit hat wie die Schrift daneben. */
.sym {
  width: 16px; height: 16px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.25;
  stroke-linecap: round; stroke-linejoin: round;
}

/* Die Spur: eine kleine, gesperrte Versalienbeschriftung in der Stimme des
   Geräts. Sie benennt, was daneben steht, ohne mit ihm um Aufmerksamkeit zu
   ringen — im Kontext der Titelleiste, in der Statusleiste, am Befund. */
.marke-spur {
  font-size: 0.64rem; font-weight: 400;
  text-transform: uppercase; letter-spacing: 0.09em;
  color: var(--tinte-still); margin-right: 0.45rem;
  white-space: nowrap;
}

/* --- Titelleiste --- */

.titelleiste {
  grid-area: titel;
  display: flex; align-items: center; gap: 0.85rem;
  min-height: 2.75rem;
  padding: 0 0.75rem 0 1rem;
  background: var(--werkbank);
  border-bottom: 1px solid var(--kante-zart);
}
.wortmarke {
  display: flex; align-items: center;
  font-size: 0.86rem; font-weight: 500; letter-spacing: -0.005em;
  color: var(--tinte-leise); white-space: nowrap;
}
/* Die Wortmarke des Kantons. Beschnitten und mit durchsichtigem Grund, weil
   die Titelleiste nicht weiss ist — ein weisser Kasten um das Wappen waere
   sichtbar. Hoehe gesetzt, Breite frei: das Seitenverhaeltnis gehoert dem
   Kanton, nicht dem Raster. */
.wappen { height: 16px; width: auto; display: block; }
.wortmarke-app {
  margin-left: 0.5rem; padding-left: 0.5rem;
  border-left: 1px solid var(--kante-zart);
  font-weight: 700; color: var(--tinte);
}

/* Suchfeld: die einzige Eingabe im Gerät, deshalb als Feld gesetzt und nicht
   als Unterstrich — auf grauem Grund braucht sie eine eigene Fläche. */
/* Das Suchfeld gibt Platz ab, bevor die Schule ihn abgibt. Vorher stand hier
   eine feste Breite von 44vw; sie nahm sich den Raum zuerst, und «Schulen
   Stadt Olten» endete als «Schulen Stadt Ol…». Die Schule zu nennen ist
   Pflicht [DA 4.3.2.2], ein breites Suchfeld ist Bequemlichkeit. */
.kopfsuche {
  margin-left: auto;
  display: flex; align-items: center; gap: 0.4rem;
  width: min(32rem, 44vw); min-width: 12rem; flex: 0 1 auto;
  padding: 0 0.55rem;
  background: var(--papier);
  border: 1px solid var(--kante); border-radius: 3px;
}
.kopfsuche:focus-within {
  border-color: var(--tinte-still); box-shadow: 0 0 0 2px var(--fokusflaeche);
}
.kopfsuche .sym { width: 13px; height: 13px; color: var(--tinte-still); flex: none; }
/* Der Typ steht am Selektor, nicht bloss ».kopfsuche input«: die allgemeine
   Formularregel weiter unten trifft input[type="search"] mit derselben
   Gewichtung und steht spaeter — sie gaebe dem Kopffeld sonst ihre 0.95rem,
   ihr Polster und im Fokus ihren roten Unterstrich. Der Kopf ist Geraet, kein
   Formular.
   Die Schrift ist kleiner als die der Titelleiste, weil der Platzhalter die
   durchsuchbaren Felder aufzaehlt — abgeschnitten zaehlt er sie falsch. */
.kopfsuche input[type="search"] {
  flex: 1; min-width: 0;
  padding: 0.32rem 0; border: 0; background: transparent;
  font: inherit; font-size: 0.76rem; color: var(--tinte);
  text-overflow: ellipsis;
}
.kopfsuche input[type="search"]:focus-visible {
  outline: 0; box-shadow: none;
}
.kopfsuche input::placeholder { color: var(--tinte-still); }

/* Schule und Erhebung stehen im bleibenden Geraet, nicht in einer eigenen
   Spalte: die stand auf den meisten Masken leer und nahm dem Blatt trotzdem
   ihre Breite weg. Hier fuellen sie die Luecke zwischen Wortmarke und Suche.
   Die Titelleiste hat eine feste Mindesthoehe - ob hier etwas steht oder
   nicht, verschiebt deshalb nichts.                                        */
/* Schule und Erhebung [DA 4.3.2.2] stehen hier vollständig, nicht angerissen.
   In 0.82rem endete «Schulen Stadt Olten» als «Schulen Stadt O…» — eine Marke,
   die den Namen abschneidet, benennt die Schule gerade nicht. Kleiner gesetzt
   passt er ganz hin; die Titelleiste behält ihre feste Höhe, es rückt also
   nichts. Die Kürzung bleibt als letzte Sicherung für sehr lange Namen. */
.kontextmarke {
  display: flex; align-items: baseline; gap: 0 1.1rem; flex-wrap: wrap;
  min-width: 0; flex: 1 1 auto;
  font-size: 0.74rem;
}
/* Kein Abschneiden. «Schulen Stadt O…» benennt die Schule gerade nicht, und
   welche Schule offen ist, ist die eine Angabe, die diese Leiste zu machen
   hat. Passt der Name nicht, bricht er um — die Titelleiste hat nur eine
   Mindesthoehe und darf dafuer wachsen. */
.kontextstueck { min-width: 0; font-weight: 500; overflow-wrap: anywhere; }
/* Auf der Werkbank, nicht auf Papier: hier trägt nur der kräftigere Ton.
   Die Spur benennt Schule und Erhebung — [DA 4.3.2.2] verlangt sie
   «deutlich», und 2.65:1 ist nicht deutlich. */
.titelleiste .marke-spur { color: var(--tinte-leise); }

.konto { font-size: 0.8rem; color: var(--tinte-leise); white-space: nowrap; }
.abmeldeform { display: flex; }

/* --- Seitenleiste --- */

/* Ordnerleiste nach dem Vorbild von streamlinehq.com. Uebernommen ist die
   Form: eingerueckte Zeilen mit eigener Rundung statt Baendern ueber die volle
   Breite, eine sichtbare Stufe zwischen Gruppe und Ziel, je Ziel genau eine
   Zeile, und ein Klapper an jeder Gruppe. Nicht uebernommen ist ihre Ordnung —
   dort steht immer nur der Ordner offen, in dem man gerade ist; hier stehen
   alle offen, bis jemand zuklappt (navigation.go sagt, warum).

   Der Einzug steht in zwei Variablen, weil ihn Kopf und Ziel gemeinsam
   brauchen und sonst zwei Zahlen auseinanderliefen.                        */
.seitenleiste {
  grid-area: leiste;
  background: var(--werkbank);
  border-right: 1px solid var(--kante-zart);
  overflow-y: auto; overscroll-behavior: contain;
  padding: 0.6rem 0 1.5rem;
  --nav-rand:  0.5rem;   /* seitlicher Einzug jeder Zeile          */
  --nav-stufe: 1.5rem;   /* Stufe vom Gruppenkopf zu seinen Zielen */
}

/* Die kopflose Gruppe zuoberst wird durch eine Fuge abgesetzt, nicht durch
   Abstand allein — dieselbe Trennung, die streamlinehq zwischen dem
   persoenlichen Block und den Ordnern zieht. */
.navgruppe:first-child {
  padding-bottom: 0.5rem; margin-bottom: 0.5rem;
  border-bottom: 1px solid var(--kante-zart);
}
.navgruppe + .navgruppe { margin-top: 0.7rem; }
.navgruppe:first-child + .navgruppe { margin-top: 0; }

.navgruppe-kopf { display: flex; align-items: center; margin: 0 var(--nav-rand) 0.1rem; }

/* Der Klapper. 24 px im Quadrat (WCAG 2.2 SC 2.5.8) und ein eigenes Ziel
   neben dem Verweis auf die Bereichsseite — beides in eine Zeile zu legen
   hiesse, eine der zwei Handlungen zu verlieren. */
.navklapper {
  flex: none;
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 4px;
  color: var(--tinte-still); text-decoration: none;
}
.navklapper:hover { background: var(--werkbank-tief); color: var(--tinte); }
/* Zugeklappt zeigt der Winkel nach rechts. `rotate` ist eine Transformation
   und ruehrt an keinem Kastenmass an (CLAUDE.md §1) — anders als ein zweites
   Sinnbild, das eine andere Breite mitbraechte. Ohne Uebergang: der Zustand
   wechselt mit dem Klick, nicht mit einer Bewegung. */
.sym-klapper { width: 12px; height: 12px; }
.navgruppe-zu .sym-klapper { rotate: -90deg; }

.navgruppe-titel {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 0.4rem;
  /* Der Gruppentitel ist als Verweis auf die Bereichsseite ein eigenes
     Bedienziel und stand mit 16 px unter den 24 px aus WCAG 2.2 SC 2.5.8.
     Die Hoehe kommt aus min-height, nicht aus einem :hover — das Mass steht
     von Anfang an fest und aendert sich beim Zeigen nicht. */
  min-height: 1.5rem;
  margin: 0; padding: 0 0.35rem; border-radius: 4px;
  font-size: 0.66rem; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.11em;
  color: var(--tinte-still); text-decoration: none;
}
a.navgruppe-titel:hover { color: var(--tinte-leise); background: var(--werkbank-tief); }
/* Das Sinnbild sass vorher in einer eigenen dunklen Spur und zeigte dieselben
   drei Bereiche ein zweites Mal. Hier steht es bei seiner Beschriftung. */
.navgruppe-titel .sym { flex: none; width: 13px; height: 13px; opacity: 0.85; }
.navgruppe-titel > span { overflow: hidden; text-overflow: ellipsis; }
.navgruppe-titel[data-aktiv] { color: var(--tinte-leise); }

.navliste { list-style: none; margin: 0; padding: 0; }
/* `hidden` muss staerker sein als jede spaetere display-Regel — die schmale
   Fassung setzt `display: flex` auf dieselbe Liste, und ohne diese Zeile
   stuende eine zugeklappte Gruppe dort wieder offen da. */
.navliste[hidden] { display: none; }
.navliste a {
  display: flex; align-items: center; gap: 0.4rem;
  margin: 1px var(--nav-rand);
  padding: 0.26rem 0.45rem 0.26rem var(--nav-stufe);
  border-radius: 4px;
  font-size: 0.855rem; line-height: 1.35;
  color: var(--tinte); text-decoration: none;
  /* Eine Zeile je Ziel. Vorher brach ein langer Erhebungsname auf drei um und
     nahm der Leiste ihren Takt; den vollen Namen traegt jetzt `title`. */
  white-space: nowrap;
}
/* Die zwei Ziele der kopflosen Gruppe tragen ein Sinnbild. Es sitzt in der
   Stufe, die den uebrigen Zielen als Einzug dient — so beginnt jede
   Beschriftung der Leiste an derselben Kante. */
.navliste a:has(.sym) { padding-left: 0.3rem; }
.navliste .sym { flex: none; width: 13px; height: 13px; opacity: 0.85; }
.navwort { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.navliste a:hover { background: var(--werkbank-tief); }
/* Gedrueckt eine Nuance tiefer als ueberfahren. */
.navliste a:active { background: var(--mint-tief); }
/* Der rote Streifen liegt als Innenschatten in der Zeile, nicht als Rahmen:
   ein Schatten nimmt im Kastenmodell keinen Platz ein, kann also gar nichts
   verschieben — und er folgt der Rundung, was ein `border-left` nicht taete.
   Rot bleibt damit Kennzeichen (CLAUDE.md §7), die Form kommt von der
   Ordnerleiste. */
.navliste a[aria-current="page"] {
  background: var(--papier); font-weight: 500; color: var(--tinte);
  box-shadow: inset 3px 0 0 var(--rot);
}
.navzusatz {
  flex: none; margin-left: 0.4rem;
  font-size: 0.7rem; font-weight: 400;
  color: var(--tinte-still);
}

/* --- Klappschalter --- */

/* Er stand bis 0.41.0 links in der Titelleiste und sitzt seit 0.42.0 unten im
   Ruecken, wo das uebrige Geraet der Schale steht: Stage, Version,
   Herstellernachweis. In die Leiste selbst kann er nicht — zugeklappt ist sie
   `display: none`, und der Schalter waere mit ihr verschwunden. Er ist der
   einzige Weg zurueck und muss deshalb ausserhalb liegen.

   24 px statt 28 (WCAG 2.2 SC 2.5.8 verlangt 24), und die senkrechten Raender
   sind negativ: was das Geraet unten wegnimmt, fehlt dem Erfassungsraster
   oben, die Leiste darf durch den Schalter also nicht hoeher werden. Sie ist
   25.25 px hoch, davon 8 px Polsterung — fuer den Inhalt bleiben 17.25 px.
   Bei -3 px stand das Aussenmass des Schalters auf 18 px und die Leiste wuchs
   auf 25.98 px; erst -4 px (Aussenmass 16 px) laesst sie bei 25.25 px stehen.
   (Gemessen bei 1600 px, `getBoundingClientRect`.) Die Raender stehen fest und
   aendern sich beim Zeigen nicht. */
.klappschalter {
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; margin: -4px 0.6rem -4px -0.2rem;
  border-radius: 3px;
  color: var(--ruecken-still); text-decoration: none; flex: none;
}
.klappschalter .sym { width: 14px; height: 14px; }
.klappschalter:hover { color: var(--ruecken-text); background: var(--ruecken-tief); }
.klappschalter:active { background: var(--ruecken-hoch); }

/* --- Das Blatt --- */

main {
  grid-area: inhalt;
  background: var(--papier);
  overflow-y: auto; overscroll-behavior: contain;
  scrollbar-gutter: stable;
}
/* Das Blatt hält die Lesebreite, das Rollfeld nimmt die volle Spalte: sonst
   klebte der Bildlauf mitten im Text statt am Rand. */
.bogen { max-width: var(--breite); padding: 1.6rem var(--rand) 4rem; }

/* --- Typografie ----------------------------------------------------------- */

/* Eine Erhebungsmaske ist kein Werbeauftritt: der Titel muss die Maske
   benennen, nicht sie ankündigen. Grosse Schaugrade schöben die erste
   Tabellenzeile unter den Falz — genau das, was bei 103 Lieferungen zählt. */
h1 {
  font-size: 1.32rem; font-weight: 700;
  letter-spacing: -0.018em; line-height: 1.2;
  margin: 0 0 0.3rem;
}
h2 {
  font-size: 1.02rem; font-weight: 700; letter-spacing: -0.01em;
  margin: 2rem 0 0.6rem;
}
h3 { font-size: 0.92rem; font-weight: 700; margin: 1.4rem 0 0.4rem; }

.untertitel {
  color: var(--tinte-leise); font-size: 0.92rem;
  margin: 0 0 1.3rem; max-width: 46rem;
}
/* Die Kontextzeile unter dem Titel. Sie las sich bis 0.41.0 als Fliesstext
   mit Mittelpunkten — «Referenzjahr 2026 · Stichtag 26.08.2026 · Abgabe bis
   06.09.2026 · offen» —, und Beschriftung und Wert trugen denselben Ton.
   Damit war sie die einzige Stelle der Anwendung, die die Geraetestimme nicht
   sprach: Titelleiste und Statusleiste setzen seit jeher `.marke-spur` vor
   den Wert und trennen mit Abstand, nicht mit einem Zeichen. Derselbe Abstand
   von 1.4rem wie in der Statusleiste. */
.kontext {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 0.2rem 1.4rem;
  color: var(--tinte); margin: 0 0 1.5rem;
}
/* Ein Paar aus Spur und Wert haelt zusammen: ohne das eigene Kaestchen risse
   der Zeilenumbruch die Beschriftung von ihrem Wert. */
.kontextpaar { display: inline-flex; align-items: baseline; min-width: 0; }
/* Ein Nachsatz steht unter der Zeile, nicht in ihr — ein <br> richtet in
   einem Flexkasten nichts aus. */
.kontext .anweisung { flex: 1 0 100%; }
.hinweis { color: var(--tinte-leise); font-size: 0.9rem; max-width: 46rem; }
.anweisung { color: var(--tinte-leise); font-size: 0.88rem; }
.brotkrume { font-size: 0.9rem; margin: 0 0 1rem; }
.brotkrume a { color: var(--tinte-leise); }

/* --- Tabellen -------------------------------------------------------------
   Dichte Zeilen, keine senkrechten Linien, Kopfzeile auf Mintgrund. Die
   Tabelle ist das Arbeitsgerät dieser Anwendung, nicht ein Beiwerk.         */

.datentabelle {
  width: 100%; border-collapse: collapse;
  margin: 1rem 0 1.5rem;
  font-size: 0.94rem;
}
/* Der Spaltenkopf ist Beschriftung des Geräts, nicht Inhalt: kleiner, versal
   und gesperrt — seit 0.32.0 sind das seine einzigen Mittel, die eigene
   Schriftfamilie ist weggefallen. Und er bleibt beim Rollen stehen — wer bei
   Zeile 90 wissen will, ob in der Spalte das Liefer- oder das Stichdatum
   steht, soll nicht zurückrollen müssen. Möglich, weil das Blatt rollt und
   nicht das Fenster. */
.datentabelle th {
  position: sticky; top: 0; z-index: 1;
  text-align: left; font-weight: 500; font-size: 0.66rem;
  text-transform: uppercase; letter-spacing: 0.09em;
  color: var(--tinte-leise);
  background: var(--papier);
  padding: 0.5rem 0.7rem 0.45rem;
  box-shadow: inset 0 -1px 0 var(--kante-zart);
  white-space: nowrap;
}
.datentabelle td {
  padding: 0.6rem 0.7rem;
  border-bottom: 1px solid var(--linie-zart);
  vertical-align: top;
}
.datentabelle tbody tr:hover td { background: var(--flaeche); }
/* Feste Spaltenbreiten fuer Tabellen, deren Zeilenbestand sich beim
   Aufklappen aendert. Bei automatischer Verteilung misst der Browser aus dem
   Inhalt: kommen laengere Namen dazu, rueckt die ganze Tabelle zur Seite —
   auch das, was ueber der Klappstelle steht. Umgebrochen wird, nicht
   abgeschnitten. */
.datentabelle.tabelle-fest { table-layout: fixed; }
.datentabelle.tabelle-fest td, .datentabelle.tabelle-fest th { overflow-wrap: anywhere; }
.spalte-erhebung  { width: 30%; }
.spalte-jahr      { width: 9%; }
.spalte-schule    { width: 33%; }
.spalte-status    { width: 15%; }
.spalte-mahnstufe { width: 13%; }

.datentabelle.schmal { width: auto; min-width: 26rem; }
.datentabelle.schmal th {
  background: var(--papier); box-shadow: inset 0 -1px 0 var(--linie-zart);
  font-family: inherit;
  padding-right: 1.5rem; text-transform: none; letter-spacing: 0;
  font-size: 0.94rem; color: var(--tinte-leise);
}
.datentabelle.dicht td, .datentabelle.dicht th { padding: 0.35rem 0.5rem; }

td.anzahl, th.anzahl {
  text-align: right; font-weight: 500; width: 4.5rem;
  padding-right: 1.1rem; color: var(--tinte);
}
th.anzahl { font-weight: 400; }

/* --- Der Abschlussbericht [DA 4.1.4.4] ------------------------------------
   Der Verlauf ueber die Jahre: eine Zeile je Referenzjahr, zwei Zahlen. Er
   ist die letzte Pruefung im Prozess (Grundsatz G10) und gelingt nur, weil
   die Zahl dieser Lieferung in einer Reihe steht — allein waere sie weder
   richtig noch falsch.

   Tabelle, kein Diagramm: kein Balken, kein Verlaufsstrich, keine Kachel
   (Kickoff-Folie 7). Wer den Sprung sehen will, liest die Spalte hinunter;
   dafuer stehen die Ziffern tabellarisch (font-variant-numeric am body).   */

.datentabelle.bericht { width: auto; min-width: 24rem; }
/* Nur der Spaltenkopf, nicht jedes th: das Jahr ist ein <th scope="row"> und
   erbte sonst den weissen Grund des Kopfes — die Auszeichnung der laufenden
   Zeile begann dann erst bei der zweiten Spalte. */
.datentabelle.bericht thead th {
  background: var(--papier); box-shadow: inset 0 -1px 0 var(--linie-zart);
  text-transform: none; letter-spacing: 0;
  font-size: 0.94rem; color: var(--tinte-leise);
}
/* Das Jahr ist die Zeilenkennung (<th scope="row">), nicht eine Zahl unter
   Zahlen: es steht links und traegt nicht die Zahlenspaltenbreite.
   Zurueckzunehmen ist dabei alles, was der Spaltenkopf mitbringt — `position:
   sticky` vor allem, das die Jahreszelle als eigenen weissen Kasten aus der
   Zeile hob, dazu Versalien, Sperrung und der kleine Grad. */
.datentabelle.bericht tbody .verlauf-jahr {
  position: static;
  text-align: left; font-weight: 400; font-size: inherit;
  text-transform: none; letter-spacing: 0;
  color: var(--tinte); background: none;
  padding: 0.6rem 1.2rem 0.6rem 0.7rem;
  box-shadow: none; border-bottom: 1px solid var(--linie-zart);
  white-space: nowrap;
}
/* Die Zeile, um die es geht. Der gruene Grund ist die Auszeichnung, das Wort
   in der letzten Spalte die Auskunft — Farbe traegt nie allein. */
.datentabelle.bericht tr.verlauf-jetzt th,
.datentabelle.bericht tr.verlauf-jetzt td { background: var(--mint); font-weight: 500; }
.datentabelle.bericht tbody tr:hover td { background: inherit; }

/* --- Zustände in Zeilen ---------------------------------------------------
   Farbe trägt nie allein: jede Zeile führt zusätzlich ihren Text.           */

tr.vollstaendig td { background: #f2f8f4; }
tr.unvollstaendig td, tr.offen td { background: #fdf4f4; }
tr.abgewaehlt td { opacity: 0.55; }
/* Die eben gespeicherte Zeile. Nach dem Speichern fuehrt der Umweg ueber 303
   auf dieselbe Maske zurueck, und der Anker setzt sie ins Bild — die Marke
   sagt dann, welche der dreihundert Zeilen gemeint war. Gruen wie jede
   Bestaetigung; die Meldung oben nennt zusaetzlich den Schluessel, damit die
   Farbe die Auskunft nicht allein traegt.

   `scroll-margin-top` haelt sie unter dem klebenden Spaltenkopf: ohne das
   sprang der Anker die Zeile genau unter den Kopf, der sie verdeckte. */
tr.frisch { scroll-margin-top: 3rem; }
tr.frisch td { background: var(--mint); }
.datentabelle tbody tr.frisch:hover td { background: var(--mint-tief); }
.datentabelle tbody tr.vollstaendig:hover td { background: #e9f3ed; }
.datentabelle tbody tr.unvollstaendig:hover td,
.datentabelle tbody tr.offen:hover td { background: #fbeaea; }

/* --- Status ---------------------------------------------------------------
   Vier Zustände [DA 4.1.5.1]. Als Marke gesetzt, damit sie beim Überfliegen
   einer Überwachungsliste sofort auseinandergehen.                          */

.status {
  display: inline-block;
  padding: 0.1rem 0.55rem 0.15rem;
  font-size: 0.64rem; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.07em;
  border: 1px solid var(--linie); border-radius: 2px;
  color: var(--tinte-leise); background: var(--papier);
  white-space: nowrap;
}
.status-offen        { border-color: var(--linie);      color: var(--tinte-leise); }
.status-hochgeladen  { border-color: #9bb4c7;           color: #2c5975; background: #eff5f9; }
.status-geprueft     { border-color: var(--mint-tief);  color: var(--gruen);       background: var(--mint); }
.status-abgegeben    { border-color: var(--gruen);      color: var(--papier);      background: var(--gruen); }
.status-entwurf      { border-style: dashed; }
.status-freigegeben  { border-color: #9bb4c7;           color: #2c5975; background: #eff5f9; }
.status-geschlossen  { border-color: var(--tinte-leise); color: var(--tinte); }
.status-erledigt     { border-color: var(--mint-tief);  color: var(--gruen);       background: var(--mint); }
.status-geloescht    { border-color: var(--tinte-leise); color: var(--tinte-leise); }

/* --- Meldungen ------------------------------------------------------------
   Die fluechtige Rueckmeldung liegt UEBER dem Inhalt, nicht darin. Vorher war
   sie ein Absatz zwischen Titel und Maske: sie schob beim Erscheinen alles
   darunter nach unten, und wer eben noch auf «Datei waehlen» zielte, griff
   beim zweiten Versuch daneben. Genau das schliesst CLAUDE.md §1 aus.
   meldung.go traegt die ganze Begruendung.

   Unten rechts, ueber der Statusleiste: oben liegen Titelleiste, Reiter und
   die Ueberschrift der Maske — dort verdeckte die Tafel Arbeit. Unten rechts
   verdeckt sie hoechstens den Rand einer Tabelle, und der Blick geht nach
   dem Klick ohnehin dorthin, wo der Knopf war.                             */

.meldungsstapel {
  position: fixed;
  right: var(--rand);
  /* Ueber der Statusleiste (rund 2.1rem), nicht darauf: der Ruecken ist die
     eine dunkle Flaeche der Anwendung und bleibt unangetastet. */
  bottom: 2.9rem;
  z-index: 60;
  display: flex; flex-direction: column-reverse; gap: 0.5rem;
  /* Fixiert und ohne Inhalt misst der Stapel nichts; ohne diese Zeile finge
     er trotzdem Klicks auf der Tabelle darunter ab. */
  pointer-events: none;
  max-width: min(28rem, calc(100vw - 2 * var(--rand)));
}

.meldung {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: 0.6rem;
  padding: 0.7rem 0.75rem 0.75rem 0.8rem;
  background: var(--papier);
  border: 1px solid var(--kante);
  border-left: 4px solid;
  border-radius: 3px;
  /* Der einzige Schlagschatten der Anwendung. Er ist kein Schmuck: die Tafel
     liegt ueber Papier und ueber Tabellen, und ohne Abhebung liest sie sich
     als Teil der Zeile darunter. */
  box-shadow: 0 2px 10px rgb(31 51 72 / 0.16);
  font-size: 0.92rem;
}
.meldung-gut      { border-left-color: var(--gruen); }
.meldung-schlecht { border-left-color: var(--rot); }
.meldung-zeichen {
  flex: none; width: 17px; height: 17px; margin-top: 0.08rem;
}
.meldung-gut      .meldung-zeichen { color: var(--gruen); }
.meldung-schlecht .meldung-zeichen { color: var(--rot); }
.meldung-wort { min-width: 0; flex: 1 1 auto; }
.meldung-satz { margin: 0; }
/* Die Aufzaehlung statt der Kommaliste: «... fehlen folgende Spalten: a, b,
   c, d, e, f, g, h, i» stand als ein Satz da, und neun Spaltenkoepfe
   hintereinander liest niemand ab. Als Marken nebeneinander sind sie
   einzeln erfassbar. */
.meldung-punkte {
  list-style: none; margin: 0.45rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.25rem;
}
.meldung-punkte li {
  padding: 0.05rem 0.35rem;
  background: var(--flaeche);
  border: 1px solid var(--linie);
  border-radius: 2px;
  font-size: 0.85rem;
}
.meldung-hilfe {
  margin: 0.45rem 0 0;
  color: var(--tinte-leise); font-size: 0.87rem;
}

/* Der Schliesser IST das Ankreuzfeld — kein unsichtbares Feld hinter einer
   Beschriftung. Ein weggeblendetes Bedienelement traegt den Tastaturfokus an
   eine Stelle, die niemand sieht; hier liegt der Fokusrahmen dort, wo auch
   das Kreuz steht. 24px, weil WCAG 2.2 SC 2.5.8 das setzt.

   Mit dem Typ im Wahlausdruck, nicht nur mit der Klasse: die allgemeine
   Regel fuer Ankreuzfelder weiter unten ist input[type="checkbox"] und damit
   gewichtiger als eine blosse Klasse — der Schliesser mass sonst 16.8 px
   statt 24 (gemessen am 26.08.2026, Headless-Chromium). */
input[type="checkbox"].meldung-weg {
  appearance: none; -webkit-appearance: none;
  flex: none; width: 1.5rem; height: 1.5rem; margin: -0.1rem -0.05rem 0 0;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid transparent; border-radius: 2px;
  background: none; color: var(--tinte-still);
  font-size: 0.9rem; line-height: 1; cursor: pointer;
}
input[type="checkbox"].meldung-weg::before { content: "\00d7"; font-size: 1.05rem; }
input[type="checkbox"].meldung-weg:hover { background: var(--flaeche); color: var(--rot); }
input[type="checkbox"].meldung-weg:focus-visible { outline: 2px solid var(--rot); outline-offset: 1px; }
/* Angekreuzt heisst weg. Kein Skript noetig — meldung.js kreuzt bei einer
   guten Nachricht nach sieben Sekunden nur dasselbe Feld an. */
.meldung:has(.meldung-weg:checked) { display: none; }

/* Hereinkommen: 140 ms, nur Deckkraft und ein kurzer Weg von unten. Wer
   Bewegung abbestellt hat, bekommt sie fertig hingestellt. */
@keyframes meldung-auf {
  from { opacity: 0; transform: translateY(0.5rem); }
  to   { opacity: 1; transform: none; }
}
.meldung { animation: meldung-auf 140ms ease-out; }
@media (prefers-reduced-motion: reduce) { .meldung { animation: none; } }

/* Der bleibende Hinweis auf der Maske — nicht die fluechtige Tafel. Er sagt
   etwas ueber den Zustand («alles abgegeben»), nicht ueber einen Klick, und
   gehoert deshalb weiter in den Fluss. */
.erfolg {
  padding: 0.7rem 0.95rem;
  margin: 1rem 0;
  border-left: 4px solid var(--gruen);
  background: var(--mint);
  font-size: 0.95rem;
}

/* --- Befund ---------------------------------------------------------------
   Das Herzstück der Oberfläche: der Fehler am Feld [DA 4.1.4.3]. Er nennt
   Zeile und Merkmal, stellt den gelieferten dem erwarteten Wert gegenüber
   und sagt, was zu tun ist. Hier — und nur hier — trägt die Gestaltung auf:
   ein kräftiger Balken in der Farbe des Schweregrads, gelieferter und
   erwarteter Wert direkt untereinander, damit «15.01.2013» und «JJJJ-MM-TT»
   Zeichen für Zeichen vergleichbar sind.                                    */

.befund {
  border: 1px solid var(--linie);
  border-left: 5px solid var(--rot);
  background: var(--papier);
  padding: 0.9rem 1.1rem;
  margin: 0 0 0.7rem;
}
.befund-warnung { border-left-color: var(--ocker); }
.befund-hinweis { border-left-color: var(--tinte-still); }
.befund-erledigt { border-left-color: var(--gruen); opacity: 0.72; }

.befund-kopf {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem 0.9rem;
  font-size: var(--spur); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--tinte-leise); margin-bottom: 0.45rem;
}

.befund-werte {
  display: grid; grid-template-columns: auto 1fr; gap: 0.15rem 0.9rem;
  margin: 0.3rem 0 0.6rem; font-size: 0.94rem;
}
.befund-werte dt {
  font-size: var(--spur); text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--tinte-leise); padding-top: 0.15rem;
}
.befund-werte dd {
  margin: 0;
  font-size: 0.92rem;
}
.befund-werte .geliefert { color: var(--rot); font-weight: 500; }

.befund-meldung { margin: 0 0 0.35rem; }
.befund-anweisung {
  margin: 0 0 0.6rem; color: var(--tinte-leise); font-size: 0.9rem;
  max-width: 46rem;
}
.befund-uebersteuert {
  font-size: 0.88rem; color: var(--gruen);
}

/* --- Formulare ------------------------------------------------------------
   Eingaben als Unterstrich, nicht als Kasten — so.ch. Der rote Unterstrich
   im Fokus ist zugleich die Fokusmarke.                                     */

input[type="text"], input[type="email"], input[type="password"],
input[type="search"], input[type="date"], input[type="number"], select {
  font: inherit; font-size: 0.95rem; color: var(--tinte);
  background: var(--papier);
  padding: 0.4rem 0.1rem;
  border: 0; border-bottom: 1px solid var(--kante);
  border-radius: 0;
  max-width: 100%;
}
/* Der Fokus verdoppelt den Strich in Rot, statt ihn nur umzufärben: eine
   blosse Farbänderung an einer 1px-Linie ist als Fokusanzeige zu schwach
   (SC 2.4.7). Der Schatten liegt ausserhalb des Kastens und verschiebt
   deshalb nichts. */
input:focus-visible, select:focus-visible {
  outline: 0;
  border-bottom-color: var(--rot);
  box-shadow: 0 1px 0 0 var(--rot);
}
input::placeholder { color: var(--tinte-still); }
select { padding-right: 1rem; }

/* Der Dateiknopf ist ein Bedienelement des Browsers und käme sonst in dessen
   Vorgabe daher — kräftiger als alles, was daneben steht. Er trägt hier die
   Form der Umrissschaltfläche. */
input[type="file"] { font: inherit; font-size: 0.88rem; color: var(--tinte-leise); }
input[type="file"]::file-selector-button {
  font: inherit; font-size: 0.88rem; font-weight: 500; line-height: 1.4;
  padding: 0.3rem 0.95rem; margin-right: 0.6rem;
  color: var(--tinte); background: transparent;
  border: 1px solid var(--kante); border-radius: 3px;
  cursor: pointer;
}
input[type="file"]::file-selector-button:hover {
  border-color: var(--tinte-leise); background: var(--flaeche);
}

/* --- Dateiwahl auf Deutsch -------------------------------------------------
   Der native Dateiknopf beschriftet sich in der Sprache des Browsers. Auf den
   Codelisten stand «Choose File / No file chosen» vierundzwanzigmal in einer
   deutschen Maske. datei.js legt deshalb eine eigene Beschriftung darueber;
   die zwei Texte kommen aus dem Katalog.

   Das native Feld bleibt im Fluss stehen und behaelt Name, Pflicht und
   Filter — es wird nur weggeblendet, nicht entfernt, und bleibt mit der
   Tastatur erreichbar. Deshalb `clip`, nicht `display: none`: ein
   ausgeblendetes Feld nimmt kein Fokusziel mehr an. Ohne JavaScript greift
   nichts davon, und das native Feld steht sichtbar da.                     */
.dateiwahl { display: inline-flex; align-items: center; }
.dateiwahl input[type="file"] {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
.dateiwahl-marke {
  display: inline-flex; align-items: baseline; gap: 0.6rem;
  cursor: pointer; font-size: 0.88rem;
}
.dateiwahl-knopf {
  font-weight: 500; line-height: 1.4;
  padding: 0.3rem 0.95rem;
  color: var(--tinte); background: transparent;
  border: 1px solid var(--kante); border-radius: 3px;
  white-space: nowrap;
}
/* Nur Farbe, kein Kastenmass: ein :hover, der die Kante verdickt, schoebe die
   Nachbarn — genau das, was hier nicht passieren darf. */
.dateiwahl-marke:hover .dateiwahl-knopf {
  border-color: var(--tinte-leise); background: var(--flaeche);
}
/* Der Abschickknopf wartet auf die Datei. Ohne sie weist ihn der Browser
   ohnehin ab (`required`), und ein Knopf, der nichts tut, kostet einen
   Versuch und eine Fehlermeldung.
   `visibility` statt `display`: der Knopf behaelt sein Kastenmass, damit beim
   Waehlen einer Datei nichts springt — dieselbe Regel wie beim Zelleneditor.
   Und die Regel greift nur, wenn das Skript gelaufen ist (`hat-dateiwahl`):
   ohne JavaScript stuende der Knopf sonst nie da, und Hochladen waere
   unmoeglich. */
.hat-dateiwahl:not(.datei-gewaehlt) button[type="submit"] { visibility: hidden; }

/* Der gewaehlte Dateiname steht ganz da. Abgeschnitten («lernende_2025_ko…»)
   beantwortet er die einzige Frage nicht, fuer die er da ist: ob die richtige
   Datei gewaehlt wurde. */
.dateiwahl-name { color: var(--tinte-still); overflow-wrap: anywhere; }
/* Gewaehlt: der Name ist dann eine Angabe und keine Andeutung mehr. */
.dateiwahl-voll .dateiwahl-name { color: var(--tinte); }
/* Der Fokus liegt auf dem weggeblendeten Feld und muss an der Beschriftung
   sichtbar werden — sonst tastet man blind. Schatten statt Rahmen, damit das
   Kastenmass gleich bleibt. */
.dateiwahl input[type="file"]:focus-visible + .dateiwahl-marke .dateiwahl-knopf {
  border-color: var(--rot);
  box-shadow: 0 0 0 2px var(--fokusflaeche);
}

label { font-size: 0.92rem; }

.stammform { max-width: 30rem; }
.stammform label {
  display: block; margin: 0.9rem 0 0.1rem;
  font-size: var(--spur); text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 700; color: var(--tinte-leise);
}
/* Die Dateiwahl ist zwar ein <label>, aber die Schaltflaeche selbst und nicht
   die Beschriftung eines Feldes darueber. Sonst stuende dort «DATEI WÄHLEN»
   in gesperrten Versalien, wo ein Knopf hingehoert. */
.stammform .dateiwahl-marke {
  display: inline-flex; margin: 0;
  font-size: 0.88rem; text-transform: none; letter-spacing: normal;
  font-weight: 400; color: var(--tinte);
}
/* Kaestchen und Schalter sind ausgenommen: `width: 100%` machte aus einem
   Kaestchen einen Balken ueber die ganze Formularbreite. Frueher stand die
   Ausnahme als eigene, staerkere Regel darunter (`width: auto`) — und die
   ueberschrieb dann auch die Zielgroesse aus dem Grundsatzabschnitt, sodass
   das Kaestchen bei 13 px blieb. Die Ausnahme gehoert hierher, nicht in eine
   Gegenregel. */
.stammform input:not([type="checkbox"]):not([type="radio"]),
.stammform select { width: 100%; }
.stammform button { margin-top: 1.3rem; }
.stammform label input[type="checkbox"] { vertical-align: middle; }

/* Das Kaestchen kam bisher in der Vorgabe des Browsers daher: gemessen
   13x13 px. Im aufgeklappten Feldkatalog stand eines davon 21.9 px neben
   «Befristen» — zwei Ziele unter 24 px in weniger als 24 px Abstand, also
   der Fall, den WCAG 2.2 SC 2.5.8 gerade ausschliesst, und daneben liegt
   der Knopf, der ein Merkmal beendet. Das Kaestchen traegt jetzt 1.05rem
   (16.8 px) und sein Rand ringsum die fehlenden Pixel; die Beschriftung
   gehoert ohnehin zum Ziel, weil das Feld im <label> steht. */
input[type="checkbox"], input[type="radio"] {
  width: 1.05rem; height: 1.05rem;
  margin: 0.2rem 0.45rem 0.2rem 0.2rem;
  accent-color: var(--tinte);
  cursor: pointer;
}

.zeilenform { display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; }
/* Dateiwahl und Absenden sind zwei Handlungen, nicht eine: «keine Datei
   gewaehlt» stiess sonst direkt an «Datei hochladen». */
.zeilenform .dateiwahl { margin-right: 0.7rem; }
.zeilenform input[type="text"] { min-width: 7rem; }

/* Mehrere Handlungen an derselben Zeile stehen nebeneinander, nicht
   untereinander: gestapelt macht jede Zeile doppelt hoch, und bei
   fuenfhundert Zeilen ist das eine zweite Seite. */
.zeilenaktionen { display: flex; gap: 0.4rem; align-items: center; }
.zeilenaktionen > form:first-child { flex: 1; min-width: 0; }

/* Die Textliste traegt in jeder Zeile ein Eingabefeld. Ohne Spaltenbreiten
   nimmt der Standardtext den Platz, das Feld faellt unter seine Schaltflaeche
   und die Zeile wird doppelt hoch — genau das, was auf einer Liste dieser
   Laenge nicht passieren darf. */
.textliste th:nth-child(1), .textliste td:nth-child(1) { width: 20%; }
.textliste th:nth-child(2), .textliste td:nth-child(2) { width: 18%; }
.textliste th:nth-child(4), .textliste td:nth-child(4) { width: 34%; }
.textliste .zeilenform { flex-wrap: nowrap; }
/* min-width: 0 statt 5rem: die Zelle ist ein Flex-Kind und waere sonst nie
   schmaler als ihr Inhalt geworden. Auf 1280 schob die Liste den Bogen um
   drei Bildpunkte auf und die Seite rollte waagrecht — auf einer Maske mit
   ueber zweihundert Zeilen. */
.textliste .zeilenform input[type="text"] { flex: 1; min-width: 0; }
.textliste td { overflow-wrap: anywhere; }

.suchleiste {
  display: flex; gap: 0.7rem; align-items: center; flex-wrap: wrap;
  margin: 0 0 1.5rem;
}
.suchleiste label {
  font-size: var(--spur); text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 700; color: var(--tinte-leise);
}
.suchleiste input[type="search"] { min-width: min(24rem, 60vw); }

/* --- Schaltflächen --------------------------------------------------------
   Die Hauptaktion ist rot und rund — der Suchknopf von so.ch als Grundform.
   Aber klein und in mittlerer Strichstärke: eine Schaltfläche muss
   auffindbar sein, nicht laut. Fett gesetzt und grossflächig zöge sie den
   Blick von dem ab, worauf es auf dieser Oberfläche ankommt — dem Befund am
   Feld. Rot hat hier zwei Aufgaben, Handlung und Fehler; die Handlung darf
   die Warnung nicht übertönen.

   Zustände: ruhend · überfahren · Tastaturfokus · gedrückt · gesperrt.
   Der Fokusring kommt aus der globalen :focus-visible-Regel (Rot, 4.98:1).

   Grössen (Zeilenhöhe 1.4):
     normal   ~31 px hoch — über den 24 px aus WCAG 2.2 SC 2.5.8
     klein    min-height 1.6rem, damit auch «Abmelden» die 24 px hält    */

/* `a.knopf` steht in jeder dieser Regeln neben `button` und nicht in einer
   eigenen Abschrift daneben: es ist derselbe Knopf, nur fuehrt er auf eine
   Maske, statt zu schreiben. Zwei Regelsaetze fuer dasselbe Aussehen liefen
   auseinander, und dann stuenden in einer Leiste zwei verschieden hohe
   Knoepfe. Gebraucht wird er, seit «Abgeben» erst den Abschlussbericht
   aufschlaegt [DA 4.1.4.4] — ein Formular waere dort die falsche Auskunft
   ueber eine Handlung, die noch nichts tut. */
button, a.knopf {
  font: inherit;
  font-size: 0.82rem; font-weight: 500; line-height: 1.4;
  padding: 0.3rem 0.85rem;
  color: var(--papier); background: var(--tinte);
  border: 1px solid var(--tinte); border-radius: 3px;
  cursor: pointer;
}
a.knopf { display: inline-block; text-decoration: none; text-align: center; }
button:hover, a.knopf:hover { background: #16283a; border-color: #16283a; }

button.sekundaer, a.knopf.sekundaer {
  background: transparent; color: var(--tinte); border-color: var(--kante);
}
button.sekundaer:hover, a.knopf.sekundaer:hover { border-color: var(--tinte-leise); background: var(--flaeche); }

button[disabled], button[disabled]:hover {
  opacity: 0.45; cursor: not-allowed;
  background: var(--tinte); border-color: var(--tinte);
}
button.sekundaer[disabled], button.sekundaer[disabled]:hover {
  background: transparent; border-color: var(--kante);
}

button.klein {
  padding: 0.1rem 0.6rem; font-size: var(--spur);
  min-height: 1.6rem;
}

/* --- Der gedrueckte Zustand -----------------------------------------------
   Die Aufzaehlung ueber diesem Abschnitt nannte fuenf Zustaende und das
   Stylesheet kannte vier: «gedrueckt» stand als Absicht da, aber keine Regel
   trug ihn. Zwischen Niederdruecken und der neuen Maske lag deshalb nichts —
   auf einer Maske, die serverseitig gerendert wird, sind das je nach Abfrage
   ein paar hundert Millisekunden ohne jede Antwort, und der zweite Klick ist
   die Folge.

   Nur Farbe, nie Mass. Ein Knopf, der sich beim Druecken um ein Pixel senkt
   (`transform: translateY`), ist genau die Bewegung, die CLAUDE.md §1
   ausschliesst; hier verdunkelt sich die Flaeche und nichts wandert.       */
button:active, a.knopf:active { background: #0f1d2b; border-color: #0f1d2b; }
button.sekundaer:active, a.knopf.sekundaer:active { background: var(--mint-tief); border-color: var(--tinte-leise); }
button[disabled]:active { background: var(--tinte); border-color: var(--tinte); }
button.sekundaer[disabled]:active { background: transparent; border-color: var(--kante); }
input[type="file"]::file-selector-button:active { background: var(--mint-tief); }

/* --- Anmeldung ------------------------------------------------------------ */

.anmeldung { max-width: 23rem; margin: 3.5rem auto; }
.anmeldung label {
  display: block; margin: 1rem 0 0.1rem;
  font-size: var(--spur); text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 700; color: var(--tinte-leise);
}
.anmeldung input { width: 100%; }
.anmeldung button { margin-top: 1.5rem; width: 100%; }


/* --- Handlungen auf Anforderung ------------------------------------------
   Grundsatz: kein Platz auf Verdacht. Ein Formular, das man selten braucht,
   steht nicht dauerhaft auf der Maske — es liegt hinter einem unauffaelligen
   Ausloeser. Und wo es nichts zu tun gibt, steht auch kein Ausloeser: die
   Auswahl der Institutionen entfaellt ganz, sobald einem Benutzer keine mehr
   fehlt.

   Traeger ist <details>. Das kostet kein JavaScript (ADR-007) und funktioniert
   mit der Tastatur, ohne dass wir etwas dafuer tun.                        */

.maskenkopf {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  margin-bottom: 1.2rem;
}
.maskenkopf h1, .maskenkopf h2 { margin: 0; }
/* Zwei Handlungen im Kopf bleiben ein Block, sonst zoege der
   Zwischenraum sie an die beiden Enden der Zeile. */
.kopfaktionen { display: flex; align-items: flex-start; gap: 0.5rem; }

/* Der Ausloeser sieht aus wie eine Umrissschaltflaeche und verhaelt sich auch
   so; das Dreieck der Vorgabedarstellung waere hier ein fremdes Zeichen. */
.handlung > summary,
.zuordnen > summary {
  list-style: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 0.35rem;
}
.handlung > summary::-webkit-details-marker,
.zuordnen > summary::-webkit-details-marker { display: none; }

.handlung > summary {
  padding: 0.3rem 0.75rem 0.3rem 0.6rem;
  font-size: 0.82rem; font-weight: 500;
  color: var(--tinte); background: transparent;
  border: 1px solid var(--kante); border-radius: 3px;
}
.handlung > summary:hover { border-color: var(--tinte-leise); background: var(--flaeche); }
.handlung > summary:focus-visible { outline: 2px solid var(--rot); outline-offset: 2px; }
/* Gedrueckt wie ein Knopf — er sieht aus wie einer und soll sich auch so
   anfuehlen. Nur die Flaeche wechselt; die Kante liegt schon da. */
.handlung > summary:active { background: var(--mint-tief); border-color: var(--tinte-leise); }
.handlung > summary .sym { width: 13px; height: 13px; }
.handlung[open] > summary { background: var(--flaeche); }
/* Aufgeklappt legt sich die Tafel ueber die Seite, statt sie
   auseinanderzuschieben. Wer auf «Anlegen» drueckt, will ein Formular sehen —
   nicht die Tabelle darunter wegrutschen sehen. */
.handlung { position: relative; }
.handlung > form {
  position: absolute; right: 0; top: calc(100% + 0.4rem); z-index: 30;
  width: max-content; max-width: min(26rem, 90vw);
  padding: 0.9rem 1rem 1.1rem;
  background: var(--papier);
  border: 1px solid var(--kante-zart); border-radius: 4px;
  box-shadow: 0 6px 20px rgb(31 51 72 / 0.14);
}

/* Auf schmalen Schirmen darf der Kopf umbrechen; das Formular soll dann die
   ganze Breite bekommen und nicht am rechten Rand kleben.
   `max-width: none` allein genuegte nicht: mit `width: max-content` legte die
   Tafel jede Feldhilfe auf eine Zeile und wuchs dabei nach LINKS aus dem Bild
   heraus (gemessen bei 700 px: linke Kante bei −807 px, also unerreichbar,
   ohne dass die Seite seitlich rollte). Sie spannt jetzt zwischen den Kanten
   ihres Ausloesers, und der Text bricht um. */
@media (max-width: 46rem) {
  /* Zwei Handlungen nebeneinander teilen sich hier nur noch Restbreite — und
     die Tafel darunter waere so breit wie ihr Ausloeser, also ein Schacht.
     Sie stehen deshalb untereinander, jede ueber die ganze Breite. */
  .kopfaktionen { flex-wrap: wrap; width: 100%; }
  .handlung { width: 100%; }
  .handlung > .stammform { left: 0; right: 0; width: auto; max-width: none; }
}

/* Dieselbe Tafel, aber in einer Tabellenzeile: die Spalte soll nur so breit
   sein wie ihr Ausloeser, und die Tafel legt sich beim Aufklappen ueber die
   Zeilen darunter — nicht dazwischen. */
.zeilenhandlung { width: 1%; white-space: nowrap; }
.zeilenhandlung .handlung > form { min-width: 18rem; white-space: normal; }

/* --- Formularbeiwerk ------------------------------------------------------
   Erklaerungen unter dem Feld, Ja/Nein-Schalter und der Fuss mit Speichern
   und Abbrechen. Der Fuss traegt beide Wege nebeneinander: ein Formular ohne
   sichtbaren Rueckweg zwingt zum Browserknopf.                            */
.feldhilfe {
  margin: 0.2rem 0 0; font-size: 0.72rem; color: var(--tinte-leise);
}
.schalterzeile { display: flex; flex-direction: column; gap: 0.35rem; margin: 1rem 0 0; }
/* Der Schalter ist die Beschriftung des Kaestchens, nicht die eines Feldes
   darueber — also keine gesperrten Versalien wie bei .stammform label. */
.schalterzeile .schalter {
  display: flex; align-items: baseline; gap: 0.45rem; margin: 0;
  font-size: 0.88rem; font-weight: 400; color: var(--tinte);
  text-transform: none; letter-spacing: normal;
}
/* Nur die Textfelder der Schalterzeile fuellen die Breite; das Kaestchen
   behaelt die Zielgroesse aus dem Grundsatzabschnitt. Vorher stand hier
   `width: auto` fuer alle Felder — und `auto` heisst beim Kaestchen 13 px,
   also unter der Zielgroesse aus WCAG 2.2 SC 2.5.8. */
.schalterzeile .schalter input:not([type="checkbox"]):not([type="radio"]) { width: auto; }
.formfuss {
  display: flex; align-items: center; gap: 1.2rem;
  margin-top: 1.3rem;
}
.formfuss button { margin-top: 0; }

/* --- Feldkatalog ----------------------------------------------------------
   Ein Katalog je Erhebungstyp. Die Wahl sieht aus wie die Ansichtswahl der
   Ueberwachung, weil sie dasselbe tut: zwischen gleichrangigen Sichten
   umschalten.                                                             */
.typwahl {
  display: flex; flex-wrap: wrap; gap: 0.15rem;
  margin: 0.8rem 0 0.6rem;
  border: 1px solid var(--kante); border-radius: 3px; padding: 0.15rem;
  width: max-content; max-width: 100%;
}
.typwahl a {
  padding: 0.28rem 0.85rem; border-radius: 2px;
  font-size: var(--spur); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--tinte-leise); text-decoration: none;
  border: 1px solid transparent;
}
.typwahl a:hover { background: var(--flaeche); }
.typwahl a[aria-current="page"] { background: var(--tinte); color: var(--papier); }

/* Die Werte einer kategorialen Variable stehen in der Zeile ihres Merkmals,
   nicht in einer zweiten Maske: wer wissen will, was «2» bedeutet, fragt beim
   Merkmal. Links der Bestand, rechts das Feld, in dem er fortgeschrieben
   wird — nebeneinander, damit man beim Tippen sieht, was gilt. */
.wertelink {
  display: inline-flex; align-items: center; gap: 0.35rem;
  min-height: 1.5rem; text-decoration: none;
}
.wertelink:hover { text-decoration: underline; text-underline-offset: 0.15em; }
/* Die aufgeschlagene Zeile traegt nichts im Fluss: sie ist nur der Ort, an
   dem die Tafel haengt. Vorher stand der Block als gewoehnliche Tabellenzeile
   da und schob alles darunter um 448 px nach unten (gemessen am 25.08.2026,
   61 sichtbare Elemente, dreizehn Ausloeser auf der Maske) — CLAUDE.md §1
   verlangt Ueberlagern statt Schieben. Der Server-Rundlauf bleibt, wie er
   war; ohne JavaScript aendert sich nichts an der Bedienung. */
.werte-zeile > td {
  position: relative;
  height: 0; padding: 0; border: 0; background: transparent;
}
.datentabelle tbody tr.werte-zeile:hover > td { background: transparent; }
.werte {
  position: absolute; left: 0; right: 0; top: 0; z-index: 30;
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: 1.4rem 2.4rem;
  padding: 0.9rem 1rem 1.1rem;
  background: var(--papier);
  border: 1px solid var(--kante-zart); border-radius: 4px;
  box-shadow: 0 6px 20px rgb(31 51 72 / 0.14);
  /* Der Deckel gilt der Tafel selbst, nicht einem Vorfahren: ein `overflow`
     weiter oben wuerde sie an der Kante abschneiden (CLAUDE.md §1), ihr
     eigenes Rollfeld haelt eine lange Codeliste dagegen im Rahmen. */
  max-height: 30rem; overflow-y: auto;
}
.werte-bestand { flex: 1 1 18rem; min-width: 0; }
.werte-bestand > .marke-spur { display: block; margin-bottom: 0.35rem; }
.werteform { flex: 1 1 22rem; min-width: 0; max-width: 30rem; }
.werteform label {
  display: block; margin: 0 0 0.15rem;
  font-size: var(--spur); text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 700; color: var(--tinte-leise);
}
.werteform label + input { margin-top: 0; }
.werteform input[type="date"] { width: 100%; }
.werteform textarea {
  width: 100%; font: inherit;
  font-size: 0.84rem; line-height: 1.5;
  padding: 0.4rem 0.55rem;
  border: 1px solid var(--kante); border-radius: 3px;
  background: var(--papier); color: var(--tinte);
  resize: vertical;
}
.werteform textarea:focus-visible { outline: 2px solid var(--rot); outline-offset: -1px; }
.werteform .feldhilfe { margin-bottom: 0.7rem; }
.werteform button { margin-top: 1rem; }

.wertetafel { border-collapse: collapse; width: 100%; font-size: 0.86rem; }
.wertetafel th {
  text-align: left; font-size: 0.66rem; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--tinte-leise);
  padding: 0 0.8rem 0.25rem 0; border-bottom: 1px solid var(--kante-zart);
}
.wertetafel td {
  padding: 0.18rem 0.8rem 0.18rem 0; vertical-align: baseline;
  border-bottom: 1px solid var(--linie-zart);
}
.wertetafel td.kennung { white-space: nowrap; }

/* Ein befristetes Merkmal steht weiter im Katalog — es gilt fuer aeltere
   Lieferungen und Extrakte. Ruhig, aber lesbar: die Gueltigkeitsspalte nennt
   das Enddatum, die Farbe ist nur die Bestaetigung (WCAG 1.4.1). */
.datentabelle tbody tr.ist-beendet td { color: var(--tinte-leise); }
.datentabelle tbody tr.ist-beendet td.kennung { color: var(--tinte-still); }

/* Der Leerzustand des Datenblatts sagt, was als naechstes zu tun ist, statt
   nur zu melden, dass nichts da ist. */
.blatt-leer {
  margin: 0.8rem 0;
  padding: 0.9rem 1rem;
  color: var(--tinte-leise);
  background: var(--flaeche);
  border-left: 2px solid var(--kante-zart);
  max-width: 46rem;
}

/* --- Zuordnungen ---------------------------------------------------------- */

.zuordnungen { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; }

/* Die Marke traegt ihr Entfernen selbst. Als <form> gesetzt, weil ein
   Formular im <span> nicht erlaubt waere. */
.marke-loesbar {
  display: inline-flex; align-items: center; gap: 0.15rem;
  padding-right: 0.1rem;
}
.loesen {
  display: inline-flex; align-items: center; justify-content: center;
  /* 20px: klein genug, um die Marke nicht zu sprengen, gross genug fuer die
     24px-Regel aus WCAG 2.2 SC 2.5.8 samt der Abstaende ringsum. */
  width: 1.25rem; height: 1.25rem; padding: 0;
  color: var(--tinte-leise); background: transparent;
  border: 0; border-radius: 2px; cursor: pointer;
}
.loesen .sym { width: 11px; height: 11px; stroke-width: 1.6; }
.loesen:hover { color: var(--rot-tief); background: rgb(225 0 26 / 0.1); }
.loesen:focus-visible { outline: 2px solid var(--rot); outline-offset: 1px; }
.loesen:active { background: rgb(225 0 26 / 0.22); }

/* Das Plus in der Zeile: nur ein Zeichen, kein Kasten — es soll die Marken
   daneben nicht ueberstimmen. */
.zuordnen > summary {
  justify-content: center;
  width: 1.4rem; height: 1.4rem; border-radius: 3px;
  color: var(--tinte-still);
  border: 1px dashed var(--kante);
}
.zuordnen > summary:hover { color: var(--tinte); border-style: solid; background: var(--flaeche); }
.zuordnen > summary:focus-visible { outline: 2px solid var(--rot); outline-offset: 1px; }
.zuordnen > summary:active { background: var(--mint-tief); border-style: solid; }
.zuordnen > summary .sym { width: 11px; height: 11px; }
.zuordnen[open] > summary { color: var(--tinte); border-style: solid; }
.zuordnen { position: relative; }
.zuordnen > form {
  position: absolute; left: 0; top: calc(100% + 0.3rem); z-index: 30;
  width: max-content; max-width: min(24rem, 70vw);
  padding: 0.5rem 0.6rem;
  background: var(--papier);
  border: 1px solid var(--kante-zart); border-radius: 4px;
  box-shadow: 0 6px 20px rgb(31 51 72 / 0.14);
}
.zuordnen select { max-width: min(20rem, 55vw); }

/* --- Listen und Marken ---------------------------------------------------- */

.bereichsliste { list-style: none; padding: 0; margin: 1.25rem 0; max-width: 34rem; }
.bereichsliste li { border-bottom: 1px solid var(--linie-zart); }
.bereichsliste a {
  display: block; padding: 0.7rem 0.2rem;
  font-weight: 500; text-decoration: none;
}
.bereichsliste a:hover { color: var(--rot); }
.bereichsliste a::after { content: " →"; color: var(--tinte-still); }

.marke {
  display: inline-block;
  background: var(--mint); border: 1px solid var(--mint-tief);
  padding: 0.05rem 0.45rem; border-radius: 2px;
  font-size: var(--spur);
}

.aktionen { white-space: nowrap; }
.aktionen a { margin-right: 0.9rem; }

.treffer {
  margin: 1.25rem 0; padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--linie-zart);
}
.treffer .zeilenform input[type="text"] { width: 11rem; }

/* --- Nahtstelle I --------------------------------------------------------- */

.nahtstelle { display: flex; gap: 2.5rem; align-items: flex-start; }
.klassenbaum {
  min-width: 12rem; padding-right: 1.25rem;
  border-right: 1px solid var(--linie);
}
.klassenbaum h2 {
  margin-top: 0; font-size: var(--spur); text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--tinte-leise);
}
.klassenbaum ul { list-style: none; margin: 0; padding: 0; }
.klassenbaum li { margin: 0.1rem 0; }
.klassenbaum a {
  display: block; padding: 0.3rem 0.5rem;
  text-decoration: none; border-left: 3px solid transparent;
}
.klassenbaum a:hover { background: var(--flaeche); }
.klassenbaum a[aria-current="page"] {
  border-left-color: var(--rot); background: var(--mint); font-weight: 700;
}
.klassenbaum a.unvollstaendig .stand { color: var(--rot); }
.klassenbaum .stand {
  float: right; font-size: var(--spur); color: var(--tinte-leise);
}
.klassenliste { flex: 1; min-width: 0; }

/* --- Rücksicht ------------------------------------------------------------

   Unterhalb von 64rem trägt der Bildschirm keine vier Spalten mehr. Dort
   fällt die Werkbank auseinander und die Seite rollt wieder als Ganzes: die
   Spur legt sich als Zeile über den Inhalt, die Seitenleiste zeigt nur noch
   die Bereichstitel, die Kontextspalte rückt unter das Blatt. Umbrechen,
   nicht scrollen — eine seitlich scrollende Navigation versteckt ihre
   hinteren Einträge.

   Zugleich stapelt der Klassenbaum. Unter dieser Schwelle gibt es damit nie
   mehr als eine senkrechte Schiene auf einmal.                             */

@media (max-width: 64rem) {
  /* Weiter ein Raster, nur einspaltig — damit die Reihenfolge gesetzt bleibt
     und nicht der Auszeichnung folgt: der Kontext gehört über das Blatt, denn
     [DA 4.3.2.2] verlangt ihn «deutlich», nicht unter dem Falz. */
  body.werkbank {
    grid-template-columns: minmax(0, 1fr);
    /* «reiter» MUSS in der Liste stehen. Ohne das Feld faellt die
       Reiterleiste aus dem benannten Raster, der Browser legt fuer sie eine
       eigene Spalte an — und aus einer Spalte wurden drei: gemessen bei
       390 px «32.27px 0px 357.73px», das Blatt also 32 Pixel breit. Die
       Anwendung war unterhalb von 64rem unbedienbar. */
    grid-template-rows: 3px auto auto auto 1fr auto;
    grid-template-areas:
      "marke" "titel" "leiste" "reiter" "inhalt" "status";
    height: auto; min-height: 100dvh; overflow: visible;
  }
  /* Zugeklappt bleibt am Schreibtisch zugeklappt — hier nicht: die
     Klappschalter sind auf schmalen Schirmen ausgeblendet, und wer die Leiste
     zugeklappt hat, stünde unterwegs sonst ohne Navigation da. */
  body.werkbank.leiste-zu .seitenleiste { display: flex; }
  main { overflow: visible; }
  .bogen { max-width: none; }

  .kantonsmarke { height: 3px; }
  .titelleiste { flex-wrap: wrap; padding: 0.45rem 0.6rem; }
  .kopfsuche { order: 3; width: 100%; margin: 0.3rem 0 0; }

  .seitenleiste {
    border-right: 0; border-bottom: 1px solid var(--kante-zart);
    padding: 0.55rem 0.6rem;
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 1.4rem;
  }
  .navgruppe {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 1rem;
  }
  .navgruppe + .navgruppe { margin-top: 0; }
  /* Die Fuge unter der ersten Gruppe trennt zwei Bloecke uebereinander. Hier
     stehen sie nebeneinander, und ein Strich mittendrin traefe nichts. */
  .navgruppe:first-child {
    border-bottom: 0; padding-bottom: 0; margin-bottom: 0;
  }
  /* Kein Klapper in der Zeile: die Bereichsgruppen geben hier ohnehin alle
     ihre Ziele ab, es gaebe also nichts zuzuklappen. Wo es nichts zu tun
     gibt, steht auch kein Bedienelement (CLAUDE.md §1). */
  .navklapper { display: none; }
  /* Und ebenso wenig der Schalter der Leiste: unterhalb dieser Schwelle steht
     sie erzwungenermassen offen, er koennte also nichts bewirken. Wo es
     nichts zu tun gibt, steht auch kein Bedienelement (CLAUDE.md §1). */
  .klappschalter { display: none; }
  .navgruppe-kopf { display: contents; }
  .navgruppe-titel { margin: 0; padding: 0; }
  .navliste { display: flex; flex-wrap: wrap; gap: 0.2rem 1rem; }
  /* Die Bereichsgruppen geben ihre Ziele ab; ihr Titel ist der Verweis auf
     die Bereichsseite und wird hier zur Navigation selbst. */
  .navgruppe-bereich > .navliste { display: none; }
  .navgruppe-bereich > .navgruppe-titel {
    font-family: inherit; font-size: 0.92rem; text-transform: none;
    letter-spacing: -0.01em; font-weight: 500; color: var(--tinte);
  }
  /* In der Zeile trägt nicht die linke Kante den aktuellen Ort, sondern die
     untere — sonst stünde ein senkrechter Strich zwischen zwei Wörtern. */
  .navliste a {
    margin: 0; padding: 0.2rem 0; border-radius: 0;
    border-bottom: 2px solid transparent;
    /* In der Zeile darf umgebrochen werden: hier ist Platz nach unten, aber
       keiner nach rechts — genau umgekehrt zur senkrechten Leiste. */
    white-space: normal;
  }
  .navliste a:has(.sym) { padding-left: 0; }
  .navwort { overflow: visible; }
  .navliste a:hover { background: none; color: var(--rot); }
  .navliste a[aria-current="page"] {
    background: none; box-shadow: none; border-bottom-color: var(--rot);
  }
  .navzusatz { margin-left: 0.3rem; }

  /* Der Kontext bricht mit der Titelleiste um, statt sie zu sprengen. */
  .kontextmarke { order: 2; width: 100%; gap: 0 1rem; }

  .statusleiste { padding-left: 0.6rem; flex-wrap: wrap; white-space: normal; }
  /* Der Herstellernachweis ist Beiwerk. Auf dem schmalen Schirm kostete er
     eine zweite Zeile in der Statusleiste — Flaeche, die dem Erfassungsraster
     fehlt. Kein Platz auf Verdacht (CLAUDE.md §1). */
  .statusfeld-hersteller { display: none; }

  .nahtstelle { flex-direction: column; }
  .klassenbaum {
    min-width: 0; width: 100%; border-right: 0;
    border-bottom: 1px solid var(--linie); padding: 0 0 0.75rem;
  }
}

@media (max-width: 46rem) {
  .konto { display: none; }
  .datentabelle { font-size: 0.88rem; }
  .datentabelle th, .datentabelle td { padding: 0.45rem 0.4rem; }
}

/* Breite Tabellen dürfen scrollen, statt die Seite zu sprengen — aber erst
   auf schmalen Schirmen. Auf breiten rollt ohnehin das Blatt, und eine
   Tabelle mit overflow wäre dort ein Beschneidungsrahmen: eine aufgeklappte
   Tafel in einer der letzten Zeilen würde an der Tabellenkante abgeschnitten. */
@media (max-width: 64rem) {
  .bogen > table, .klassenliste > table { display: block; overflow-x: auto; }
  /* Die Mindestbreite der schmalen Tabelle (26rem = 416 px) sprengte bei
     390 px die Seite: der Kasten wuchs ueber den Rand, statt in sich zu
     rollen. Gemessen — main 390 px breit, Inhalt 432. Auf schmalen Schirmen
     gilt die Mindestbreite fuer den Inhalt, nicht fuer den Rahmen. */
  .datentabelle.schmal { min-width: 0; }
  .datentabelle.schmal > tbody, .datentabelle.schmal > thead { min-width: 26rem; }
  /* Dasselbe fuer den Abschlussbericht: beschneiden darf er nicht — er ist
     die letzte Pruefung, und eine halb sichtbare Zahl prueft niemand. */
  .datentabelle.bericht { min-width: 0; }
  .datentabelle.bericht > tbody, .datentabelle.bericht > thead { min-width: 24rem; }
  /* In diesem Rahmen kann eine überlagernde Tafel nicht bestehen; dort klappt
     sie im Fluss auf. Auf dem Schirm, auf dem gearbeitet wird, tut sie das
     nicht. */
  .zuordnen > form { position: static; width: auto; max-width: none;
                     box-shadow: none; margin-top: 0.4rem; }
}

/* --- Die Seite bricht um, sie schiebt nicht -------------------------------
   Rasterfelder haben von sich aus min-width:auto und können deshalb nicht
   unter ihren längsten Inhalt schrumpfen: «Kompetenzzentrum kantonale
   Schulverwaltung Sek II» schöbe sonst die ganze Seite über den Rand. Jedes
   Feld, das einen fremden Namen aufnimmt, muss schrumpfen und umbrechen
   dürfen.                                                                  */

.tafel-kopf, .tafel-spalte, .tafel-spalten > *, .filterleiste > * { min-width: 0; }
.tafel-kopf h2, .tafelkarte-schule, .tafel-fertig,
.befund-werte > *, .muster-regel { overflow-wrap: anywhere; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  /* Ausgenommen die rollgesteuerten Verlaeufe. Sie sind keine Bewegung — sie
     laufen nicht ab, sondern stehen am Rollstand. Mit erzwungenen 0.01 ms
     waere die Zeitachse im ersten Augenblick fertig und der Schleier stuende
     dauerhaft auf seinem Endwert: oben ein Vorhang schon am Seitenanfang,
     quer ein Verlauf ueber der ersten Spalte. */
  main, .seitenleiste, .blatt-rahmen, .cm-liste, .schnell-liste,
  .bogen > table, .klassenliste > table { animation-duration: auto !important; }
  /* Der Stern trifft keine Pseudoelemente: der Seitenuebergang braucht seine
     eigene Zeile, sonst blendet er weiter, wo niemand blenden will. */
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) {
    animation-duration: 0.01ms !important;
  }
}

@media print {
  .seitenleiste, .statusleiste,
  .kopfsuche, button, .abmeldeform, .klappschalter, .navklapper { display: none; }
  body { font-size: 11pt; }
  body.werkbank { display: block; height: auto; overflow: visible; }
  main { overflow: visible; }
  .bogen { max-width: none; padding: 0; }
  /* Die Titelleiste bleibt im Ausdruck stehen: sie traegt Schule und
     Erhebung, und ein Blatt ohne beide ist nicht zuzuordnen. */
  .titelleiste { border: 0; padding: 0 0 0.5rem; background: none; min-height: 0; }

}

/* Nur fuers Vorlesegeraet. Die Klasse wurde in den Schablonen schon benutzt
   («— erledigt» am erledigten Schritt), war aber nie definiert — der Zusatz
   stand deshalb sichtbar neben dem Titel und sagte dort zum zweiten Mal, was
   der gruene Haken schon sagt. `clip-path` statt `display: none`: ein
   ausgeblendetes Feld nimmt kein Fokusziel mehr an und wird von manchen
   Vorlesegeraeten uebersprungen. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* --- Weicher Anfang statt harter Kante -------------------------------------
   Wo etwas weggerollt ist, hoert es nicht mit einer geraden Kante auf, sondern
   verschwindet. Die Kante sagt «hier ist Schluss»; der Verlauf sagt «hier geht
   es weiter» — genau die Auskunft, die ein Rollfeld schuldig bleibt, seit die
   Bildlaufleisten so schmal und so blass geworden sind.

   Bis 0.41.0 lag der Verlauf unten. Das war die falsche Kante: unten steht,
   was man noch nicht gelesen hat, und der Schleier legte sich ausgerechnet
   ueber den naechsten Satz — auf der Lieferungsmaske ueber den Hinweis, dass
   die gelieferte Datei unveraendert aufbewahrt wird. Oben steht, was man
   schon hinter sich hat; dort ist der Verlauf eine Auskunft und kein Vorhang.
   Seit 0.42.0 zieht er also oben auf, sobald gerollt ist, und ist am
   Seitenanfang nicht da.

   Ein Verlauf oben darf aber nicht als `mask-image` kommen. Eine Maske trifft
   den ganzen Kasten, und am oberen Rand von `main` und `.blatt-rahmen` klebt
   der Spaltenkopf (`position: sticky; top: 0`); die Maske loeschte genau ihn,
   und er sagt, was in der Spalte steht. Deshalb ein eigener Streifen als
   `::before`: er klebt selbst oben, traegt aber `z-index: 0` und liegt damit
   unter dem Kopf (z-index 1 bzw. 3), der ihn schlicht ueberdeckt. Sein
   negativer unterer Rand nimmt ihm den Platz im Fluss wieder ab — er legt
   sich ueber den Inhalt, statt ihn zu schieben (CLAUDE.md §1).

   Kennt ein Browser die Rollzeitachse nicht, bleibt es bei der geraden Kante.
   Ein Schleier, der nicht weiss, wo oben ist, waere schlimmer als keiner. */
/* `inherits: true` ist hier keine Bequemlichkeit, sondern Bedingung. Die
   Rollzeitachse muss am Rollfeld selbst haengen: `scroll(nearest)` an einem
   ::before sucht das naechste rollende Vorfahrenelement des Traegers, und
   `main` hat keines — die Achse blieb tot, der Schleier stand auf 0, obwohl
   die Maske ihn richtig zeichnete. (Genau so gemessen.) Also rollt `main` den
   Wert und vererbt ihn an sein ::before.

   Die Vererbung reicht aber weiter als an das eigene ::before: `.blatt-rahmen`
   und `.cm-liste` sind Rollfelder *innerhalb* von `main`. Rollen sie nicht —
   ein kurzes Spaltenmenue, ein kurzes Raster —, ist ihre Zeitachse untaetig,
   die Animation nicht in Kraft, und der Wert faellt zurueck. Nicht auf den
   Anfangswert 0, wie es `inherits: false` taete, sondern auf den geerbten
   Stand des gerollten `main`: gemessen 1, also ein voll deckender Schleier
   ueber den ersten Zeilen, dauerhaft. Deshalb traegt jedes Rollfeld unten
   `--schleier: 0` als Rueckfallwert. */
@property --schleier {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}
/* Zwei Enden, zwei Werte: die waagrechte Fassung muss links und rechts
   getrennt aufziehen, sonst verblasste am Anfang die erste Spalte. */
@property --schleier-links {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
@property --schleier-rechts {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

@supports (animation-timeline: scroll()) {
  /* Der Streifen traegt den Grund seines Rollfelds — sonst verliefe Weiss
     ueber der grauen Werkbank und saehe aus wie ein Fehler. */
  main, .blatt-rahmen, .cm-liste, .schnell-liste { --schleier-grund: var(--papier); }
  .seitenleiste { --schleier-grund: var(--werkbank); }

  /* Der Rollstand wird am Rollfeld genommen und an den Streifen vererbt. */
  main, .seitenleiste, .blatt-rahmen, .cm-liste, .schnell-liste {
    /* Der Rueckfallwert fuer den Fall, dass hier nichts zu rollen ist: die
       Animation schlaegt ihn, solange die Zeitachse laeuft, und nur wenn sie
       untaetig ist, gilt er — statt des geerbten Stands des Vorfahren. Ohne
       ihn schleierte ein kurzes Menue in einem gerollten `main` voll zu. */
    --schleier: 0;
    /* `animation-duration: auto` ist Pflicht. In der Kurzform steht die Dauer
       sonst auf 0s, und eine Rollzeitachse mit Dauer null ist im ersten
       Augenblick fertig — der Schleier stuende dann dauerhaft auf seinem
       Endwert, also immer da, auch am Seitenanfang. (Genau so gemessen,
       bevor diese Zeile hier stand.) */
    animation-name: schleier-oben;
    animation-duration: auto;
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: scroll(self block);
  }
  main::before, .seitenleiste::before, .blatt-rahmen::before,
  .cm-liste::before, .schnell-liste::before {
    content: "";
    position: sticky; top: 0; z-index: 0;
    display: block; height: 2.4rem; margin-bottom: -2.4rem;
    pointer-events: none;
    background: linear-gradient(to bottom, var(--schleier-grund), transparent);
    opacity: var(--schleier);
  }
  /* Gleich nach dem ersten Rollen voll da und dann bis unten stehenbleiben:
     oben liegt Gelesenes, das darf verblassen — anders als unten, wo der
     naechste Satz steht. */
  @keyframes schleier-oben {
    from   { --schleier: 0; }
    6%, to { --schleier: 1; }
  }
  /* Im Menue reicht weniger — es ist selbst nur fuenfzehn Zeilen hoch. */
  .cm-liste::before, .schnell-liste::before {
    height: 1.1rem; margin-bottom: -1.1rem;
  }

  /* Dasselbe zur Seite. Auf schmalen Schirmen rollt eine breite Tabelle in
     sich (siehe «Die Seite bricht um»), und dort endete sie mit einer
     geraden Kante mitten in einer Spalte: der Feldkatalog schnitt bei 720 px
     hinter «ÜBERSTEU…» ab, und dass rechts noch GÜLTIG und «Befristen»
     stehen, war der Maske nicht anzusehen. Waagrechte Bildlaufleisten
     erscheinen auf dem Zeigegeraet erst beim Rollen — die Auskunft fehlte
     also genau, bevor man sie braucht.

     Nur in demselben Medienbereich, in dem die Tabelle ueberhaupt rollt: auf
     breiten Schirmen hat sie kein overflow, und eine Maske ohne Rollstand
     bekaeme sonst einen Schleier ueber ihre letzte Spalte.

     Beide Enden, nicht nur rechts: anders als beim Blatt gibt es hier kein
     «oben», das stehenbleiben muss — wer nach rechts gerollt hat, soll auch
     sehen, dass links etwas liegt.

     Quer bleibt es bei der Maske: an der linken und rechten Kante klebt kein
     Spaltenkopf, den sie loeschen koennte. */
  @media (max-width: 64rem) {
    .bogen > table, .klassenliste > table {
      mask-image: linear-gradient(to right,
        transparent 0, #000 var(--schleier-links),
        #000 calc(100% - var(--schleier-rechts)), transparent 100%);
      animation-name: schleier-quer;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: scroll(self inline);
    }
    @keyframes schleier-quer {
      from      { --schleier-links: 0px;    --schleier-rechts: 1.6rem; }
      4%, 96%   { --schleier-links: 1.6rem; --schleier-rechts: 1.6rem; }
      to        { --schleier-links: 1.6rem; --schleier-rechts: 0px; }
    }
  }
}

/* Sprungmarke: sichtbar erst bei Tastaturbedienung. */
.sprungmarke {
  position: absolute; left: -9999px;
  background: var(--tinte); color: var(--papier);
  padding: 0.6rem 1rem; z-index: 10; text-decoration: none;
}
.sprungmarke:focus { left: 0; top: 0; }


/* --- Tafel ----------------------------------------------------------------
   Die Statuspipeline als Spalten [DA 4.1.5.1]. Vier Spalten, eine Karte je
   Schule, die Zahl über der Spalte ist die Länge der Arbeit darunter. Kein
   Ziehen und Ablegen: den Status bewegt allein die Schule (siehe tafel.go).
   Der Balken im Kopf ist die einzige Grafik der Anwendung — er zeigt genau
   eine Zahl, die sonst nirgends auf einen Blick abzulesen wäre.           */

.filterleiste {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 1rem; flex-wrap: wrap; margin: 0 0 1.5rem;
}
.filterleiste .suchleiste { margin: 0; }

.ansichtswahl { display: flex; border: 1px solid var(--kante); border-radius: 999px; overflow: hidden; }
.ansichtswahl a {
  padding: 0.3rem 1rem; font-size: var(--spur); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--tinte-leise); text-decoration: none;
}
.ansichtswahl a:hover { background: var(--flaeche); }
.ansichtswahl a:active { background: var(--mint-tief); }
.ansichtswahl a[aria-current="page"] { background: var(--tinte); color: var(--papier); }

.tafel { margin: 0 0 2.5rem; }
.tafel-kopf {
  display: grid; grid-template-columns: 1fr auto; align-items: baseline;
  gap: 0.2rem 1rem; margin-bottom: 0.7rem;
}
.tafel-kopf h2 { margin: 0; font-size: 1.15rem; }
.tafel-jahr {
  font-weight: 400; color: var(--tinte-leise); font-size: 0.9em;
}
.tafel-stand { margin: 0; font-size: 0.9rem; color: var(--tinte-leise); }
.tafel-stand strong { color: var(--tinte); }
/* <progress> bringt in jeder Maschine eine eigene Erscheinung mit; sie wird
   abgelegt und durch die zwei Flaechen des Hauses ersetzt. Die Angabe steht
   ohnehin daneben als «3/4 abgegeben» — der Balken ist die zweite, schnelle
   Lesart derselben Zahl, nie die einzige (Farbe traegt nie allein). */
.tafel-balken {
  grid-column: 1 / -1; display: block; width: 100%; height: 3px;
  appearance: none; border: 0; background: var(--linie-zart);
}
.tafel-balken::-webkit-progress-bar   { background: var(--linie-zart); }
.tafel-balken::-webkit-progress-value { background: var(--gruen); }
.tafel-balken::-moz-progress-bar      { background: var(--gruen); }

/* Vier Spalten, aber nicht vier gleiche: eine Spalte ohne Karten faellt auf
   einen schmalen Streifen zusammen, und die Breite geht an die Spalte, in der
   etwas steht. Bei 335 Schulen in einer Spalte ist das der Unterschied
   zwischen zwei und vier lesbaren Zeilen je Karte.

   Die leere Spalte verschwindet nicht — eine Tafel, deren Spalten je nach
   Bestand wegfallen, laesst sich nicht wiedererkennen, und «hier ist nichts»
   ist eine Aussage ueber den Stand der Erhebung. Sie behaelt ihre
   Beschriftung und ihre Null.

   Flex statt Grid, weil `grid-template-columns` alle Spalten gleich zaehlt;
   der Streifen soll aber nur so breit sein wie seine Beschriftung. */
.tafel-spalten {
  display: flex; flex-wrap: wrap;
  gap: 0.6rem; align-items: start;
}
.tafel-spalte { flex: 1 1 11rem; min-width: 0; }
.tafel-spalte.ist-leer { flex: 0 0 auto; }
/* Der Streifen traegt Beschriftung und Null untereinander — nebeneinander
   waere er so breit wie eine halbe Spalte. */
.tafel-spalte.ist-leer .tafel-spalte-kopf {
  flex-direction: column; align-items: flex-start; gap: 0.3rem;
  margin-bottom: 0;
}
.tafel-spalte.ist-leer .tafel-zahl { margin-left: 0; }
/* Auf schmalen Schirmen stehen die Spalten ohnehin untereinander; dann waere
   ein Streifen nur eine Zeile mit einer Null und darf die volle Breite
   nehmen wie die anderen. */
@media (max-width: 46rem) {
  .tafel-spalte, .tafel-spalte.ist-leer { flex: 1 1 100%; }
  .tafel-spalte.ist-leer .tafel-spalte-kopf {
    flex-direction: row; align-items: center; gap: 0.5rem;
  }
  .tafel-spalte.ist-leer .tafel-zahl { margin-left: auto; }
}
.tafel-spalte {
  background: var(--flaeche); padding: 0.5rem; min-height: 5rem;
}
.tafel-spalte-kopf {
  display: flex; justify-content: space-between; align-items: center;
  margin: 0 0 0.5rem; font-size: 1rem;
}
.tafel-zahl {
  font-size: 0.95rem; font-weight: 500; color: var(--tinte-leise);
}
.tafel-leer {
  margin: 0; font-size: var(--spur); color: var(--tinte-still);
  text-transform: uppercase; letter-spacing: 0.06em;
}

.tafelkarte {
  display: block; background: var(--papier); border: 1px solid var(--linie);
  border-left: 3px solid var(--linie);
  padding: 0.45rem 0.6rem; margin-bottom: 0.4rem; text-decoration: none;
}
.tafelkarte:hover { border-color: var(--tinte-still); border-left-color: var(--rot); }
/* Die Karte ist ein Verweis ueber ihre ganze Flaeche. Beim Druecken faerbt
   sich der Grund — nicht die Kante, denn die traegt schon den Saeumigen. */
.tafelkarte:active { background: var(--flaeche); }
.tafelkarte-saeumig { border-left-color: var(--rot); }
.tafelkarte-schule { display: block; font-weight: 500; font-size: 0.92rem; line-height: 1.3; }
.tafelkarte-fuss {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.5rem;
  margin-top: 0.25rem; font-size: 0.78rem; color: var(--tinte-leise);
}

.marke {
  font-size: 0.68rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; padding: 0.05rem 0.4rem; border-radius: 2px;
}
.marke-rot   { background: #fdeaec; color: var(--rot-tief); }
.marke-ocker { background: #fbf1e0; color: var(--ocker); }

@media (max-width: 60rem) {
  .tafel-spalten { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 34rem) {
  .tafel-spalten { grid-template-columns: 1fr; }
}

/* Spalten gleich hoch: die Tafel ist ein Feld, keine vier Stapel. */
.tafel-spalten { align-items: stretch; }
.tafel-spalte { display: flex; flex-direction: column; }

/* Eine abgeschlossene Erhebung braucht keine vier Spalten mehr — eine Zeile
   genügt. Sonst wächst die Tafel mit jedem Jahrgang um einen leeren Block. */
.tafel-fertig {
  display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap;
  margin: 0 0 0.5rem; padding: 0.5rem 0.7rem;
  background: var(--flaeche); font-size: 0.95rem;
}
.tafel-fertig .tafel-stand { margin-left: auto; }
.marke-gruen { background: var(--mint-tief); color: var(--gruen); }

/* Der Klappschalter ist ein Verweis, kein Knopf: er fuehrt auf dieselbe Seite
   mit einem `offen`-Wert mehr und arbeitet deshalb ohne JavaScript. Er traegt
   die 24 px Zielflaeche (WCAG 2.2 SC 2.5.8) ueber `min-height`, nicht ueber
   Polsterung, die beim Zustandswechsel das Kastenmass aendern koennte. */
.klapplink {
  display: inline-flex; align-items: center; min-height: 1.5rem;
  margin-left: 1rem; font-size: var(--spur);
  text-transform: uppercase; letter-spacing: 0.06em;
  white-space: nowrap;
}

/* Die Kopfzeile einer abgeschlossenen Erhebung in der Liste. Sie sieht aus
   wie die Sammelzeile der Tafel, weil es dieselbe Sache ist — nur eben in
   einer Tabelle, wo sie ueber die ganze Breite laeuft.

   Eine einzige Zelle ueber alle Spalten: zwei Zellen nebeneinander trugen
   zwei Grundflaechen mit einer sichtbaren Naht dazwischen, und ein <th> zog
   ausserdem den klebenden Spaltenkopf mit (position: sticky). */
/* Die Zelle bleibt Zelle. `display: flex` darauf nahm ihr genau das —
   und mit der Zelleneigenschaft auch die Wirkung von colspan="5": das Band
   stand zusammengedraengt unter der ersten Spalte, nicht ueber der Breite
   der Tabelle. Das Reihen uebernimmt deshalb ein Kasten IN der Zelle. */
.listengruppe-fertig td {
  padding: 0;
  background: var(--flaeche);
  border-top: 1px solid var(--linie);
  border-bottom: 1px solid var(--linie);
}
.listengruppe-band {
  display: flex; align-items: baseline; gap: 0.2rem 0.7rem; flex-wrap: wrap;
  padding: 0.5rem 0.7rem;
}
/* Aufgeklappt gehoeren die Zeilen darunter zur Gruppe — dann trennt keine
   Linie sie ab, sonst saehe der Kopf wie eine eigene Zeile aus. */
.listengruppe-fertig.ist-offen td { border-bottom: 0; }
.listengruppe-name { font-weight: 500; }
.listengruppe-stand { margin-left: auto; white-space: nowrap; }
/* Der Grund liegt schon auf der Zeile; das Ueberfahren darf ihn nicht
   nochmals wechseln — sonst flackert die Sammelzeile beim Vorbeiziehen. */
.datentabelle tbody tr.listengruppe-fertig:hover td { background: var(--flaeche); }

/* Vorlagen standen frueher als blasse Zeile UNTER den Knoepfen — und als
   Schritt 1 im Ablauf, der nach dem ersten Upload fuer immer abgehakt war.
   Beides ging an der Sache vorbei: die Schablone ist der Anfang jeder
   Lieferung und wird bei jeder Korrektur wieder gebraucht. Sie liegt darum
   jetzt offen ueber dem Ablauf, mit Rahmen und eigener Flaeche, damit sie
   ohne Suchen gefunden wird. Kein Kasten auf Verdacht: auf dieser Maske
   fuehrt kein Weg an ihr vorbei. */
.vorlagen {
  margin: 0 0 0.9rem; padding: 0.5rem 0.7rem 0.6rem;
  border: 1px solid var(--linie); border-radius: 3px;
  background: var(--flaeche);
}
.vorlagen p { margin: 0; }
/* Beschriftung und Bezuege auf EINER Zeile: die drei Verweise sind das, was
   die Beschriftung ankuendigt, und gehoeren daneben, nicht darunter. Der
   erklaerende Satz steht als einzige zweite Zeile — er erklaert die Vorlage,
   nicht die Leiste. */
.vorlagen-zeile {
  display: flex; align-items: center; gap: 0.2rem 1.1rem; flex-wrap: wrap;
}
.vorlagen-hilfe {
  margin-top: 0.15rem; font-size: 0.74rem; color: var(--tinte-leise);
}

/* Ein Bezug ist ein Verweis mit einem Zeichen davor. Das Zeichen sagt, was
   passiert — Pfeil: eine Datei kommt heraus; Winkel: es klappt hier auf.
   `min-height` haelt die 24 px Zielflaeche (WCAG 2.2 SC 2.5.8), ohne dass
   ein Kasten daraus wird. */
.vorlage-bezug {
  display: inline-flex; align-items: center; gap: 0.35rem;
  min-height: 1.5rem; font-size: 0.9rem;
}
.vorlage-bezug .sym { width: 13px; height: 13px; flex: none; color: var(--tinte-still); }
.vorlage-bezug:hover .sym { color: var(--tinte); }
.vorlage-bezug:active { color: var(--rot-tief); }
.vorlage-bezug:active .sym { color: var(--rot-tief); }
/* Die Excel-Schablone ist der Regelfall — CSV und Spezifikation die
   Ausnahmen daneben. Nur Gewicht trennt sie, keine Farbe: Rot ist
   Kennzeichen und Fehler, sonst nichts. */
.vorlage-haupt { font-weight: 700; }
.vorlage-haupt .sym { color: var(--tinte); }
/* Die Tafel klappt auf, statt eine Datei zu liefern — der Winkel dreht sich
   mit dem Zustand, das Wort bleibt dasselbe. */
.vorlage-tafel[aria-expanded="true"] .sym { color: var(--tinte); }

/* --- Lieferspezifikation auf der Maske ------------------------------------
   Sie gab es bisher nur als Datei. Wer wissen wollte, welche Codes ein Feld
   erlaubt, lud eine Textdatei herunter — fuer eine Frage, die in der Maske
   entsteht. Jetzt steht sie hier, in zwei Stufen: erst die Spalten, dann auf
   Wunsch die Werte einer davon. Eine Codeliste fuehrt bis ueber zweihundert
   Werte; alle siebzehn Spalten gleichzeitig ausgeschrieben ergaebe eine
   Seite, die niemand liest.                                                */
.spezifikation {
  margin: 0.7rem 0 0; padding-top: 0.6rem;
  border-top: 1px solid var(--linie);
}
/* Kopf und Liste teilen dieselbe Breite: stand «Zuklappen» am rechten
   Seitenrand, gehoerte es dem Auge nach zu nichts. */
.spezifikation-kopf {
  display: flex; align-items: center; gap: 0.2rem 1.1rem; flex-wrap: wrap;
  margin: 0 0 0.2rem; max-width: 54rem;
}
.spezifikation-kopf .klapplink { margin-left: auto; }
.spezifikation-hilfe { margin: 0 0 0.5rem; max-width: 54rem; font-size: 0.72rem; color: var(--tinte-leise); }

.spezliste { list-style: none; margin: 0; padding: 0; max-width: 54rem; }
.spezzeile { border-bottom: 1px solid var(--linie-zart); }
.spezzeile:last-child { border-bottom: 0; }

/* Dasselbe Raster fuer Schalter und stumme Zeile, damit die Spalten auch
   dann untereinander stehen, wenn ein Merkmal nichts aufzuklappen hat. */
.spezkopf {
  display: grid; align-items: baseline;
  grid-template-columns: 0.9rem 1.8rem minmax(8rem, 12rem) minmax(6rem, 1fr) auto;
  gap: 0.4rem 0.8rem;
  margin: 0; padding: 0.26rem 0.5rem 0.26rem 0.35rem;
  font-size: 0.86rem; color: inherit; text-decoration: none;
  border: 1px solid transparent; border-radius: 3px;
}
a.spezkopf:hover { background: var(--papier); }
a.spezkopf:focus-visible { outline: 2px solid var(--rot); outline-offset: -2px; }
.spezkopf.ist-stumm { color: var(--tinte-leise); }
.spez-nummer { text-align: right; font-size: 0.74rem; color: var(--tinte-still); }
.spez-kopfname { font-weight: 500; }
.spez-text { color: var(--tinte-leise); }
.spez-anzahl { color: var(--tinte-still); font-size: 0.74rem; white-space: nowrap; }
a.spezkopf .spez-kopfname { text-decoration: underline; text-underline-offset: 0.15em; }
.spezzeile.ist-offen { background: var(--papier); }
.spezzeile.ist-offen .musterpfeil { color: var(--tinte); }

/* Die Werte in zwei Spalten: Code links, mit Tabellenziffern aus `body`,
   damit die fuehrende Null nicht in einer schmaleren Ziffer verschwindet.
   Geschuetzt wird sie beim Einlesen, nicht von der Schrift (docs/04, G4). */
.spezwerte { border-collapse: collapse; margin: 0 0 0.5rem 2.1rem; font-size: 0.84rem; }
.spezwerte td { padding: 0.1rem 1.1rem 0.1rem 0; vertical-align: baseline; }
.spezwerte td.kennung { white-space: nowrap; }

@media (max-width: 60rem) {
  .spezkopf { grid-template-columns: 0.9rem 1.8rem 1fr; row-gap: 0.1rem; }
  .spez-text, .spez-anzahl { grid-column: 3; }
}

/* Beschriftungsspalte im Befund fest, damit die Werte über alle Karten
   hinweg untereinander stehen und vergleichbar bleiben. */
.befund-werte { grid-template-columns: 9.5rem 1fr; }

/* --- Verdichtung ----------------------------------------------------------
   Deutet Excel eine Spalte um, ist nicht eine Zeile falsch, sondern jede.
   Diese Tabelle nennt das Muster, bevor sie die Zeilen nennt — eine
   Korrektur statt zehntausend (siehe befundsicht.go).                     */

/* Die Liste ist schmaler als frueher (32 statt 54 rem) und gibt den Platz
   rechts an die Befunde ab. Sie brauchte die Breite nie: fuenf kurze Stuecke
   je Zeile, der Rest war Luft. Jetzt steht die Arbeitsliste links und der
   aufgeschlagene Befund daneben — beide gleichzeitig sichtbar. */
.muster { list-style: none; margin: 0 0 1.2rem; padding: 0; max-width: 36rem; }
/* Der Anker der Befundtafel. Ohne ihn haenge sie am naechsten positionierten
   Vorfahren und stuende ueber der falschen Zeile. */
.musterzeile { position: relative; border-bottom: 1px solid var(--linie-zart); }

/* Die ganze Zeile ist der Schalter, nicht nur der Regelname. Wer auf ein
   Muster zeigt, meint das Muster — nicht die vier Zeichen, die zufaellig
   verlinkt waren. Deshalb ein <a> ueber die volle Zeile, innen ein Raster,
   damit die Spalten so untereinander stehen wie in der Tabelle vorher. */
.musterkopf {
  display: grid; align-items: baseline;
  /* Der freie Platz gehoert dem Regelnamen, nicht der Feldspalte: Feldnamen
     sind kurz und bekannt, Regelnamen lang und der eigentliche Gegenstand.
     Vorher nahm sich die Feldspalte bis 11rem, und «SchulartProgrammjahr»
     brach mitten im Wort um. `max-content` gibt ihr, was sie braucht. */
  grid-template-columns: 0.9rem 2.6rem 5.4rem minmax(11rem, 1fr) minmax(6rem, max-content) auto;
  gap: 0.5rem 0.8rem;
  padding: 0.34rem 0.5rem 0.34rem 0.35rem;
  font-size: 0.92rem; color: inherit; text-decoration: none;
  /* Der Rahmen liegt von Anfang an da, transparent: eine Kante, die erst
     beim Ueberfahren entsteht, schoebe die Nachbarzeilen. */
  border: 1px solid transparent; border-radius: 3px;
}
.musterkopf:hover { background: var(--flaeche); }
.musterkopf:focus-visible { outline: 2px solid var(--rot); outline-offset: -2px; }
.musterpfeil { color: var(--tinte-still); align-self: center; }
/* Die Marke haengt an ihrem Wort, nicht an der Rasterspalte: gedehnt sah sie
   aus wie eine Flaeche und nicht wie eine Marke. */
.musterkopf .grad { justify-self: start; }
.muster-anzahl { text-align: right; font-weight: 700; }
.muster-regel { text-decoration: underline; text-underline-offset: 0.15em; }
.muster-offen { color: var(--tinte-leise); white-space: nowrap; }

/* Aufgeschlagen: die Zeile faerbt sich, und der Pfeil zeigt nach unten. Die
   Karten darunter gehoeren sichtbar zu ihr — eingerueckt bis unter den
   Regelnamen, damit die Zuordnung ohne Linie klar ist. */
.musterzeile.ist-offen { background: var(--mint); border-bottom-color: var(--mint-tief); }
.musterzeile.ist-offen .musterkopf:hover { background: transparent; }
.musterzeile.ist-offen .musterpfeil { color: var(--tinte); }
.musterzeile.ist-offen .muster-regel { font-weight: 700; }
/* Die Befunde eines Musters liegen NEBEN der Liste, nicht in ihr. Vorher
   standen sie als gewoehnlicher Block in der Zeile: ein Klick auf das erste
   Muster schob die uebrigen sechs, den Abschlussbericht und das Datenblatt
   um 369.6 px nach unten (gemessen am 25.08.2026, 172 sichtbare Elemente).
   Das verstiess gegen CLAUDE.md §1 und kostete zugleich bei [DA 4.3.2.1]
   «minimale Klickzahl»: die Arbeitsliste verlor genau dann ihre Uebersicht,
   wenn jemand sie abarbeitete. Absolut gelegt nimmt die Tafel im Fluss
   keinen Platz ein — die Zeile bleibt hoch wie zuvor, und was darunter
   steht, bleibt liegen. */
.musterdetail {
  position: absolute; top: 0; left: calc(100% + 1rem); z-index: 30;
  /* 36 + 1 + 35 = 72rem, zwei rem unter der Lesebreite (--breite: 74rem).
     Knapper gerechnet stuende die Tafel bei 1440 px auf der Kante. */
  width: 35rem;
  padding: 0.9rem 1rem 1rem;
  background: var(--papier);
  border: 1px solid var(--kante-zart); border-radius: 4px;
  box-shadow: 0 6px 20px rgb(31 51 72 / 0.14);
  /* Der Deckel gilt der Tafel selbst: ein `overflow` an einem Vorfahren
     schnitte sie an der Kante ab (CLAUDE.md §1). Fuenfzig Karten eines
     Musters rollen hier, statt die Maske zu sprengen. */
  max-height: 34rem; overflow-y: auto;
}
.musterdetail .befund:last-of-type { margin-bottom: 0; }
.musterdetail .hinweis { margin: 0.6rem 0 0; }

/* Zu schmal fuer zwei Spalten: dann legt sich die Tafel ueber die Zeilen
   darunter statt neben sie. Ueberlagern bleibt es in beiden Faellen — nur
   die Richtung wechselt, nie der Grundsatz. */
/* Unter 88rem Schirmbreite bleiben nach Leiste und Rand keine 72rem Inhalt
   mehr uebrig — dann legt sich die Tafel ueber die Zeilen darunter statt
   neben sie. Ueberlagern bleibt es in beiden Faellen; nur die Richtung
   wechselt, nie der Grundsatz. */
@media (max-width: 88rem) {
  .muster { max-width: 54rem; }
  .musterdetail { top: 100%; left: 0; width: 100%; }
}

@media (max-width: 60rem) {
  /* Schmal reicht das Raster nicht: dann steht das Muster in zwei Zeilen,
     statt seitlich wegzulaufen. */
  .musterkopf {
    grid-template-columns: 0.9rem 2.6rem 5.4rem 1fr;
    row-gap: 0.15rem;
  }
  .muster-feld, .muster-offen { grid-column: 4; }
}

.grad {
  font-size: 0.7rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; padding: 0.05rem 0.45rem; border-radius: 2px;
  white-space: nowrap;
}
.grad-fehler  { background: #fdeaec; color: var(--rot-tief); }
.grad-warnung { background: #fbf1e0; color: var(--ocker); }
.grad-hinweis { background: var(--flaeche); color: var(--tinte-leise); }

.erledigt-marke { color: var(--gruen); font-size: 0.85rem; }
.abschnitt { margin-top: 1rem; }

/* Der Grad in der Karte ersetzt das graue Wort — die Karte trägt ihre
   Schwere zweimal: als Balken links und als Marke im Kopf. */
.befund-kopf .grad { text-transform: uppercase; }

/* --- Statusleiste ---------------------------------------------------------
   Nach dem Vorbild der Entwicklungsumgebung: was den Zustand der Arbeit
   ausmacht, steht dauerhaft am unteren Rand, statt im Fliesstext gesucht zu
   werden. Zeichensatz (Grundsatz G4), Datensätze, Fehler, Warnungen —
   und rechts die Umgebung.

   Sie ist die einzige dunkle Fläche der Anwendung und schliesst sie unten
   ab. Eine zweite, senkrechte Spur mit denselben drei Bereichen stand hier
   einmal links — sie zeigte, was die Leiste daneben schon zeigte, und ist
   wieder verschwunden. Die Sinnbilder stehen jetzt bei ihren Beschriftungen.
                                                                            */

.statusleiste {
  grid-area: status;
  background: var(--ruecken);
  color: var(--ruecken-text);
  display: flex; align-items: center; gap: 0 1.4rem;
  padding: 0.22rem var(--rand) 0.28rem 0.85rem;
  font-size: 0.72rem;
  /* Umbrechen statt abschneiden: auf einem schmalen Schirm verschwand sonst
     rechts die Umgebung, und die Leiste behauptete einen Zustand, den sie
     nicht mehr ganz zeigte. Sie hat nur eine Mindesthoehe. */
  flex-wrap: wrap;
}
.statusfeld .marke-spur {
  font-size: 0.62rem; margin-right: 0.35rem; color: var(--ruecken-still);
}
.statuswert {
  font-variant-numeric: tabular-nums;
  color: var(--ruecken-text);
}
.statusfeld-fehler  .statuswert { color: var(--ruecken-fehler);  font-weight: 500; }
.statusfeld-warnung .statuswert { color: var(--ruecken-warnung); }
.statusfeld-gut     .statuswert { color: var(--ruecken-gut); }
.statusfeld-rechts  { margin-left: auto; }
/* Zwei rechte Felder teilen sich sonst den freien Raum: zwei auto-Raender
   verteilen ihn haelftig, und der Herstellernachweis stuende in der Mitte
   statt neben der Version. Nur das erste Feld schiebt. */
.statusfeld-rechts + .statusfeld-rechts { margin-left: 0; }
.statusfeld-rechts .statuswert,
.statusfeld-rechts .marke-spur { color: var(--ruecken-still); }
.statusversion { margin-left: 1rem; }

/* --- Der Fehler an der Zelle ----------------------------------------------
   Die Wellenlinie der Entwicklungsumgebung, übersetzt: die beanstandete
   Zelle trägt ihre Beanstandung selbst, statt sie einer Liste weiter oben zu
   überlassen. Unterstrich statt Flächenfarbe, damit der Wert lesbar bleibt
   und die Markierung auch bei Farbschwäche als Form erkennbar ist.        */

.zeile-befund > td:first-child { border-left: 3px solid var(--rot); }
.zeile-befund:target { background: var(--mint); }

.zelle-fehler, .zelle-warnung, .zelle-hinweis, .zelle-erledigt {
  text-decoration: underline wavy;
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
  font-weight: 500;
}
.zelle-fehler   { color: var(--rot-tief);   text-decoration-color: var(--rot); }
.zelle-warnung  { color: var(--ocker);      text-decoration-color: var(--ocker); }
.zelle-hinweis  { text-decoration-color: var(--tinte-still); }
.zelle-erledigt { color: var(--gruen); text-decoration: line-through; font-weight: 400; }

.befund-sprung { font-size: var(--spur); text-transform: none; letter-spacing: 0; }

/* --- Schnellöffnen --------------------------------------------------------
   Quick Open. Der Dialog ist das native <dialog>-Element: Fokusfang,
   Escape und die Abdunklung kommen vom Browser, nicht aus unserem Code.  */

.schnell {
  width: min(38rem, 92vw); max-height: 70vh;
  margin-top: 12vh; padding: 0;
  border: 1px solid var(--kante); border-radius: 3px;
  box-shadow: 0 12px 40px rgb(31 51 72 / 0.22);
  color: var(--tinte); background: var(--papier);
}
.schnell::backdrop { background: rgb(31 51 72 / 0.35); }
.schnell-kopf { margin: 0; padding: 0.7rem 1rem; border-bottom: 1px solid var(--linie); }
.schnell-kopf input {
  width: 100%; font-size: 1.05rem; border: 0; padding: 0.15rem 0;
}
.schnell-kopf input:focus-visible { outline: 0; box-shadow: none; border: 0; }

.schnell-liste { list-style: none; margin: 0; padding: 0.3rem 0; overflow-y: auto; max-height: 46vh; }
.schnell-treffer {
  display: flex; align-items: baseline; gap: 0.7rem;
  padding: 0.35rem 1rem; text-decoration: none; color: var(--tinte);
}
.schnell-treffer:hover { background: var(--flaeche); }
.schnell-treffer[aria-selected="true"] { background: var(--mint); box-shadow: inset 3px 0 0 var(--rot); }
.schnell-art {
  flex: 0 0 auto; min-width: 5.5rem;
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--tinte-leise);
}
.schnell-bezeichnung { font-weight: 500; }
.schnell-zusatz { color: var(--tinte-leise); font-size: 0.85rem; }
.schnell-leer, .schnell-fuss {
  margin: 0; padding: 0.6rem 1rem; font-size: var(--spur); color: var(--tinte-still);
}
.schnell-fuss { border-top: 1px solid var(--linie-zart); }

/* Der Auslöser im Kopf: die Tastenkappe ist der Hinweis, der Klick der Weg
   für alle, die das Kürzel nicht kennen. */
/* 23 px waren einen Bildpunkt zu wenig fuer SC 2.5.8. */
.schnell-ausloeser {
  display: inline-flex; align-items: center; min-height: 1.5rem;
  margin-left: 0.5rem; text-decoration: none;
}
.schnell-ausloeser kbd {
  font-size: 0.66rem; color: var(--tinte-leise);
  border: 1px solid var(--linie); border-radius: 3px;
  padding: 0.08rem 0.35rem; white-space: nowrap;
}
.schnell-ausloeser:hover kbd { border-color: var(--kante); color: var(--tinte); }

/* Eine leere Zelle kann nichts unterstreichen. Das fehlende Pflichtfeld ist
   aber genau der Befund — also bekommt sie eine Fläche, die es zeigt. */
.zelle-fehler:empty, .zelle-warnung:empty, .zelle-hinweis:empty {
  background: #fdeaec; box-shadow: inset 0 -2px 0 var(--rot);
}

/* --- Zeichen am Zeilenende ------------------------------------------------
   Eine schmale Spalte mit den Handgriffen, die zur Zeile gehören. Bewusst
   Zeichen statt Wörter: bei hundert Zeilen wären hundertmal «Berichtigen»
   und «Jetzt senden» mehr Text als die Daten selbst. Jedes Zeichen trägt
   title und aria-label, damit es benannt bleibt.                          */

.zeichenspalte { width: 1%; white-space: nowrap; text-align: right; }
.zeichenspalte form { display: inline; }

.zeichen {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.75rem; height: 1.75rem; padding: 0;
  color: var(--tinte-leise); background: transparent;
  border: 1px solid transparent; border-radius: 3px;
  text-decoration: none; cursor: pointer; vertical-align: middle;
}
.zeichen svg {
  width: 1rem; height: 1rem;
  fill: none; stroke: currentColor; stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round;
}
.zeichen:hover { color: var(--tinte); background: var(--flaeche); border-color: var(--linie); }
.zeichen:focus-visible { outline: 2px solid var(--rot); outline-offset: 1px; }
.zeichen.kopiert { color: var(--gruen); border-color: var(--mint-tief); background: var(--mint); }
.zeichen.kopiert::after {
  content: attr(data-kopiert);
  position: absolute; margin-top: -2.4rem;
  font-size: var(--spur); background: var(--tinte); color: var(--papier);
  padding: 0.1rem 0.45rem; border-radius: 2px; white-space: nowrap;
}

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

/* --- Bearbeiten an Ort und Stelle ----------------------------------------- */

.bearbeitbar { cursor: text; }
/* Ein Unterstrich, kein Kasten. Der volle Ring (`inset 0 0 0 1px`) zeichnete
   um jede ueberfahrene Zelle ein geschlossenes Rechteck — in einer Zeile, die
   im selben Augenblick ihren Grund wechselt, war das der lauteste Strich auf
   dem Blatt, und er sah aus wie ein bereits geoeffnetes Eingabefeld, obwohl
   noch nichts offen ist. Der Unterstrich sagt dasselbe leiser und ist
   ausserdem die Form des Hauses: Eingabefelder stehen hier als Unterstrich
   und nicht als Kasten (Kopf dieser Datei, so.ch).

   Der Kasten bleibt der Bearbeitung vorbehalten — `.zellfeld` traegt ihn in
   Rot, sobald wirklich getippt wird. So heisst Unterstrich «hier koennte man»
   und Kasten «hier tut man gerade».

   Das Mass aendert sich dabei nicht: `box-shadow` malt innerhalb der Zelle
   und liegt ausserhalb des Flusses. --kante statt --linie, weil dieser Strich
   ein Bedienelement begrenzt und dafuer 3:1 braucht (WCAG 1.4.11); --linie
   erreicht 1.36:1 und ist eine Flaechenfuge. */
.bearbeitbar:hover {
  box-shadow: inset 0 -1px 0 0 var(--kante);
  background: var(--flaeche);
}
/* Beim Bearbeiten darf sich nichts verschieben.
   Die Zelle behaelt ihre Groesse, weil der bisherige Wert stehen bleibt und
   nur unsichtbar wird (bearbeiten.js); das Eingabefeld liegt absolut darueber
   und nimmt deshalb ueberhaupt keinen Platz im Fluss ein. */
.in-bearbeitung, .in-bearbeitung:hover {
  position: relative; background: var(--mint); box-shadow: none;
}
.zellwert-verdeckt { visibility: hidden; }
.bearbeiten-fehlte { box-shadow: inset 0 0 0 2px var(--rot); }
.zellfeld {
  position: absolute; inset: 0;
  font: inherit; font-size: 0.92rem; width: 100%; height: 100%;
  padding: 0 0.2rem; color: var(--tinte); background: var(--papier);
  border: 1px solid var(--rot); border-radius: 2px;
}
.zellfeld:focus-visible { outline: 0; box-shadow: 0 0 0 2px rgb(225 0 26 / 0.2); }
/* Schmales Zahlenfeld in den Formularen der Schularten — nicht zu verwechseln
   mit dem Zelleneditor darueber. */
.zahlfeld { width: 4.5rem; }
tr.in-arbeit { background: var(--mint); }
.abbrechen { font-size: 0.88rem; margin-left: 0.4rem; color: var(--tinte-leise); }

/* --- Das Datenblatt -------------------------------------------------------
   Die gelieferten Daten als Arbeitsfläche. Angelehnt an eine
   Tabellenkalkulation, weil genau das die Vorstellung ist, die jede Schule
   von ihren Daten mitbringt: Raster, feste Kopfzeile, Zeilennummer links,
   Filter über jeder Spalte, jede Zelle beschreibbar.

   Was bewusst fehlt: Formeln, Summenzeile, freies Sortieren. Die
   Zeilennummer ist der Anker zur gelieferten Datei — wer sie umsortiert,
   verliert den Bezug, auf den sich jeder Befund beruft.

   Dieser Abschnitt bricht aus der Textbreite aus. Achtzehn Spalten in
   78rem zu pressen ergäbe Spalten, in denen kein Geburtsdatum steht.      */

/* Der Ausbruch bezieht sich auf den Bogen, nicht auf das Fenster. Vorher
   stand hier calc(50% - 50vw + var(--rand)) — richtig, solange der Inhalt im
   Fenster zentriert war. Seit die Spur und die Seitenleiste links stehen,
   zöge dieselbe Rechnung den Abschnitt unter sie und die ersten Spalten
   wären abgeschnitten. */
.blatt-abschnitt {
  width: auto; max-width: none;
  margin-top: 2.5rem;
}

/* --- Die Reiterleiste --------------------------------------------------------
   Offene Lieferungen, wie Blätter in einer Mappe. Sie gehört zur Schale: auf
   einer anderen Maske treten die Reiter nur zurück, sie verschwinden nicht.
   Der aktive sitzt auf dem Papier und hat unten keine Kante — er geht in die
   Seite über; die übrigen liegen auf der Fläche darunter. */
.reiterleiste {
  grid-area: reiter;
  display: flex; align-items: flex-end; gap: 0.15rem; flex-wrap: wrap;
  padding: 0.3rem var(--rand) 0;
  background: var(--flaeche);
  border-bottom: 1px solid var(--linie);
}
/* Kein `overflow-x` mehr. Es beschnitt genau das eine Pixel, mit dem sich der
   aktive Reiter ueber die Trennlinie legt (`margin-bottom: -1px`) — die Linie
   lief deshalb durch, und der Reiter stand losgeloest ueber der Seite, statt
   mit ihr verbunden zu sein. Mehr als sechs Reiter gibt es nicht
   (hoechsteReiter in reiter.go); passen sie nicht nebeneinander, brechen sie
   um. Das ist ohnehin die bessere Loesung als ein Rollfeld ohne sichtbaren
   Bildlauf. */
.reiter {
  display: inline-flex; align-items: center; gap: 0.15rem; flex: 0 1 auto;
  /* 21rem statt 16: seit der Reiter den Namen der Erhebung ausschreibt statt
     ihn auf zwei Buchstaben einzudampfen, braucht er die Breite. Sechs
     Reiter passen bei 1440 px weiterhin nebeneinander; sonst brechen sie um
     (kein overflow-x, siehe oben). */
  min-width: 0; max-width: 21rem;
  border: 1px solid transparent; border-bottom: 0;
  border-radius: 3px 3px 0 0;
  margin-bottom: -1px;
}
.reiter-aktiv {
  background: var(--papier);
  border-color: var(--linie);
}
/* Unterwegs: nur Deckkraft, kein Kastenmass. Ein Zustand, der die Masse
   aendert, schoebe beim Anfassen die Nachbarn (CLAUDE.md §1). Der Zeiger
   sagt schon beim Ueberfahren, dass sich hier etwas schieben laesst. */
.reiter { cursor: grab; }
.reiter-zieht { opacity: 0.45; cursor: grabbing; }
/* Der Titel bleibt ein gewoehnlicher Verweis — der Griff ist der ganze
   Reiter, und geklickt wird weiterhin normal. */
.reiter-titel { cursor: pointer; }
.reiter-titel {
  display: flex; align-items: baseline; gap: 0.3rem; min-width: 0;
  padding: 0.3rem 0.2rem 0.35rem 0.6rem;
  font-size: 0.76rem; font-weight: 400;
  color: var(--tinte-leise); text-decoration: none;
  white-space: nowrap;
}
/* Gekuerzt wird der Name der Erhebung — und nur er. Der Ort steht dahinter
   immer ganz da: er ist genau das Stueck, an dem sich «Statistik der
   Lernenden · Olten» und «... · Trimbach» unterscheiden, und ein Reiter, der
   beim Unterschied endet, unterscheidet nichts mehr. Frueher brach der ganze
   Name um, weil er ohnehin ein Kuerzel war; ausgeschrieben waere das ein
   dreizeiliger Reiter. */
.reiter-erhebung { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.reiter-ort { flex: none; font-weight: 500; color: var(--tinte); }
.reiter:not(.reiter-aktiv) .reiter-ort { color: var(--tinte-leise); }
.reiter-aktiv .reiter-titel { color: var(--tinte); font-weight: 500; }
.reiter:hover .reiter-titel { color: var(--tinte); }
/* 24px, nicht 20.8px: WCAG 2.2 SC 2.5.8 setzt 24 an, und der Schliesser war
   mit 1.3rem das einzige Bedienelement der Anwendung darunter (gemessen am
   25.08.2026 auf zwoelf Masken). Gewachsen ist die Flaeche, nicht das
   Sinnbild — ein groesseres Kreuz waere lauter, ohne besser zu treffen. */
.reiter-zu {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.5rem; height: 1.5rem; margin-right: 0.25rem; border-radius: 2px;
  text-decoration: none; color: var(--tinte-still);
  border: 1px solid transparent;
}
.reiter-zu .sym { width: 11px; height: 11px; }
.reiter-zu:hover { background: var(--linie-zart); color: var(--rot); }
.reiter-zu:active { background: var(--mint-tief); }

/* Werkzeuge als Sinnbild, nicht als Wort: über der Tabelle zählt jede Zeile,
   und «Klartext · Vollsicht · Spalten» nebeneinander wäre eine Wortleiste, wo
   ein Griff genügt. Das Wort geht dabei nicht verloren — es steht in title
   und aria-label und kommt aus dem Katalog [ADR-008]. Die Fläche ist
   34x30px und damit über der Mindestgrösse für Zeigegeräte. */
/* Sie stehen in der Kopfzeile des Blatts, nicht in einer eigenen Zeile
   darunter: Titel links, Sinnbilder rechts, eine Hoehe. Dazwischen lag sonst
   eine Zeile Luft, die nichts sagte.
   `align-self: center`, weil die uebrigen Stuecke der Kopfzeile auf der
   Grundlinie stehen — ein Sinnbild hat keine. */
.blatt-werkzeuge {
  display: inline-flex; align-items: center; gap: 0.15rem;
  align-self: center; flex: none;
}
.werkzeug {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 30px; border-radius: 3px;
  color: var(--tinte-leise); text-decoration: none; cursor: pointer;
  border: 1px solid transparent; list-style: none;
}
.werkzeug::-webkit-details-marker { display: none; }
.werkzeug:hover { background: var(--flaeche); color: var(--tinte); }
.werkzeug:focus-visible { outline: 2px solid var(--rot); outline-offset: 1px; }
/* Eingeschaltet: der Zustand darf nicht nur Farbe sein — die Fläche trägt ihn
   mit, damit er auch bei Farbschwäche sichtbar bleibt (WCAG 1.4.1). */
.werkzeug.an {
  color: var(--rot); background: var(--flaeche);
  border-color: var(--linie);
}
.werkzeug-menue { position: relative; }
.werkzeug-menue > summary { list-style: none; cursor: pointer; display: inline-flex; }
.werkzeug-menue > summary::-webkit-details-marker { display: none; }
.werkzeug-menue .colmenu { top: 1.9rem; }

/* Vollsicht: das Blatt nimmt die ganze Arbeitsfläche ein — alles ausser
   Seitenleiste und Kopf. Die anderen Abschnitte zeichnet die Schablone gar
   nicht erst, deshalb genügt hier die Höhe. `--rand` faellt weg, damit der
   Rahmen an den Kanten der Fläche steht und nicht in ihr schwimmt. */
.blatt-voll {
  margin: 0; padding: 0;
  display: flex; flex-direction: column;
  height: 100%; min-height: 0;
}
.blatt-voll .blatt-kopfzeile { padding: 0.5rem var(--rand) 0; margin-bottom: 0.4rem; }
/* Der Rahmen liegt IM Reiter, nicht an den Fensterkanten. Vorher fielen seine
   Seiten- und Fusskante weg, damit das Blatt bis an den Rand lief — es sah
   dann aber abgeschnitten aus statt eingefasst, und rechts stand die Tabelle
   ohne Abschluss im Nichts. Jetzt hat sie ihre vier Kanten wie im eingebetteten
   Fall, nur eben ueber die ganze Hoehe. */
.blatt-voll .blatt-rahmen {
  flex: 1; min-height: 0; max-height: none;
  margin: 0 var(--rand) 0.7rem;
  border: 1px solid var(--linie); border-radius: 3px;
}
/* Der volle Name der Lieferung ueber der Tabelle — der Reiter traegt nur ein
   Kuerzel. Die Schule daneben, der Stand rechts, dann die Werkzeuge. */
.blatt-kopfzeile-voll h2 { font-size: 1.05rem; }
.blatt-schule { margin: 0; font-size: 0.9rem; color: var(--tinte-leise); }
.blatt-voll .blatt-fuss,
.blatt-voll > .hinweis { padding-inline: var(--rand); }
.blatt-voll > .hinweis { margin-bottom: 0.5rem; }

/* Der Bogen gibt in der Vollsicht seine Innenabstände ab und wird zur
   Fläche selbst — sonst bliebe rundherum der Rand des Lesebogens stehen. */
.bogen:has(.blatt-voll) {
  max-width: none; padding: 0; height: 100%;
  display: flex; flex-direction: column; min-height: 0;
}
/* Das Rollfeld gibt in der Vollsicht das Rollen ab: es rollt der Rahmen des
   Blatts. Zwei Bildläufe übereinander wären einer zu viel. */
main:has(.blatt-voll) { overflow: hidden; }
/* Und der Bogen gibt für diesen einen Abschnitt seine Lesebreite auf:
   achtzehn Spalten in 74rem zu pressen ergäbe Spalten, in denen kein
   Geburtsdatum steht. Kennt ein Browser :has() nicht, bleibt es bei der
   Lesebreite — der Rahmen rollt dann eben früher. */
.bogen:has(.blatt-abschnitt) { max-width: none; }
.blatt-kopfzeile {
  display: flex; align-items: baseline; gap: 0.3rem 1rem; flex-wrap: wrap;
  margin-bottom: 0.5rem; min-height: 2rem;
}
.blatt-kopfzeile h2 { margin: 0; }
/* Der Stand rueckt an den rechten Rand, die Werkzeuge schliessen an. Beide
   mit `margin-left: auto` waeren einer zu viel — der zweite bekaeme dann die
   ganze Luecke und stuende in der Mitte. */
.blatt-kopfzeile .blatt-stand { margin-left: auto; }
.blatt-stand { margin: 0; font-size: 0.9rem; color: var(--tinte-leise); }
.blatt-stand strong {
  color: var(--tinte);
}

/* Der Rahmen rollt in sich. Damit das auch zu sehen ist, muss seine rechte
   Kante im Bild liegen: vorher zog ein negativer Aussenabstand den Abschnitt
   bis an den Fensterrand, dort lag der 1px-Strich ausserhalb und die Tabelle
   sah abgeschnitten aus statt rollbar. `scrollbar-gutter` haelt den Platz
   frei, damit die Spalten beim Rollen nicht springen (CLAUDE.md §1). */
.blatt-rahmen {
  overflow: auto; scrollbar-gutter: stable; max-height: 70vh;
  border: 1px solid var(--linie);
  background: var(--papier);
}
.blatt {
  border-collapse: separate; border-spacing: 0;
  font-size: 0.85rem; white-space: nowrap;
}
.blatt th, .blatt td {
  border-right: 1px solid var(--linie-zart);
  border-bottom: 1px solid var(--linie-zart);
  padding: 0;
}

/* Kopfzeile und Filterzeile bleiben beim Rollen stehen, die Zeilennummer
   ebenso — sonst weiss man nach zwanzig Zeilen nicht mehr, welche Spalte
   man vor sich hat. */
/* Die Spaltenköpfe stehen hier so, wie sie in der gelieferten Datei stehen —
   ohne text-transform. Der Import gleicht schreibweisengenau ab (idperson ≠
   IDPERSON, «ae ≠ ä»), und die Lieferschablone trägt genau diese Schreibweise.
   Ein grossgeschriebener Kopf auf dem Bildschirm wäre eine dritte Fassung
   neben Schablone und Feldkatalog: wer ihn abschreibt, baut sich eine Datei,
   die der Import zurückweist, und sieht nicht warum (G1). */
/* Der Kopf trägt dieselbe Schrift wie die Spalte darunter: er ist kein
   Etikett, sondern die Zeichenkette aus der Datei. In --spur (0.72rem, laut
   Stilblatt «Beschriftungsgrösse») stand er in einem Fach, das für kurze
   gesperrte Versalien gedacht war — «idbildungsstaette» wirkte darin gedrängt
   und beziehungslos zu den Werten. Fira Sans 500 gegen Fira Sans 400 in den
   Zellen: dieselbe Schrift, ein Hauch mehr Gewicht. Mehr als 500 wird hier
   nicht genommen — 700 zoege den Kopf vor die Daten. */
.blatt thead th {
  position: sticky; top: 0; z-index: 3;
  background: var(--mint); color: var(--tinte);
  font-size: 0.78rem; font-weight: 500;
  text-align: left; padding: 0.4rem 0.5rem;
  border-bottom: 1px solid var(--mint-tief);
}
/* --- Spaltenmenü: der Winkel im Kopf ----------------------------------------
   Der Winkel sitzt im rechten Innenabstand des Kopfes (absolut), damit er die
   Spalte weder breiter macht noch mit der nächsten zusammenstösst. Sichtbar
   wird er beim Überfahren — und dauerhaft, sobald die Spalte filtert oder
   sortiert: dann ist er kein Bedienelement mehr, sondern eine Anzeige.
   «Kein Platz auf Verdacht» (CLAUDE.md §1). */
.kopf-spalte { position: relative; padding-right: 1.4rem !important; }
/* Der Spaltenkopf traegt den exakten Namen aus der Lieferdatei — abgeschnitten
   waere er der falsche Name, und genau danach schlaegt man im Handbuch nach. */
.kopf-name { display: block; }

.kopf-menue { position: absolute; right: 0.15rem; top: 0; }
.kopf-winkel {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.1rem; height: 1.6rem; cursor: pointer;
  color: var(--tinte-leise); opacity: 0; transition: opacity .12s;
  list-style: none; font-size: 0.7rem;
}
.kopf-winkel::-webkit-details-marker { display: none; }
.kopf-spalte:hover .kopf-winkel,
.kopf-menue[open] .kopf-winkel,
.kopf-gefiltert .kopf-winkel,
.kopf-auf .kopf-winkel, .kopf-ab .kopf-winkel { opacity: 1; }
.kopf-winkel:focus-visible { opacity: 1; outline: 2px solid var(--rot); outline-offset: 1px; }
.kopf-gefiltert .kopf-winkel { color: var(--rot); }
.kopf-auf .kopf-name::after { content: " \2191"; color: var(--rot); }
.kopf-ab  .kopf-name::after { content: " \2193"; color: var(--rot); }

/* --- Was die Spalte über sich selbst sagt -----------------------------------
   Der Spaltenname ist ein Verweis in die Lieferspezifikation; die Tafel
   darüber ist nur die Abkürzung dorthin. Deshalb traegt die Marke ihre
   gepunktete Unterlinie DAUERHAFT und nicht erst beim Ueberfahren: eine
   Linie, die beim Ueberfahren erscheint, ist fuer den, der nicht ueberfaehrt,
   gar nicht da — und auf dem Tastschirm faehrt niemand ueber. Beim Ueberfahren
   aendert sich nur die Farbe, keine Kastenmasse (CLAUDE.md §1).

   Die Tafel haengt an .kopf-name, nicht am ganzen Kopf: sonst spraenge sie
   auch auf, wenn man nur den Filterwinkel ansteuert. Weil sie ein Kind der
   Marke ist, bleibt sie offen, solange der Zeiger IN ihr steht — sie ist
   damit ueberfahrbar und bestaendig (WCAG 1.4.13); das Wegdruecken mit Esc
   liefert kopftafel.js nach. */
.kopf-marke {
  color: inherit; text-decoration: underline dotted;
  text-underline-offset: 2px; text-decoration-color: var(--tinte-leise);
}
.kopf-marke:hover { color: var(--tinte-leise); }
.kopf-marke:focus-visible { outline: 2px solid var(--rot); outline-offset: 1px; }

.kopf-tafel {
  position: absolute; left: 0; top: 100%; z-index: 40; width: 15rem;
  visibility: hidden;
  background: var(--papier); color: var(--tinte);
  border: 1px solid var(--linie); border-radius: 3px;
  box-shadow: 0 6px 20px rgb(16 42 67 / 0.16);
  font-size: 0.78rem; font-weight: 400; letter-spacing: 0;
  text-transform: none; white-space: normal; text-align: left;
  padding: 0.45rem 0.55rem;
  display: flex; flex-direction: column; gap: 0.15rem;
}
.kopf-name:hover > .kopf-tafel,
.kopf-name:focus-within > .kopf-tafel { visibility: visible; }
/* Das Blatt rollt in sich (overflow: auto) — an der rechten Kante wuerde eine
   linksbuendige Tafel abgeschnitten. Die letzten drei Koepfe klappen deshalb
   nach links auf. Ohne Skript, weil die Spaltenzahl im Stilblatt zaehlbar
   ist und ein Skript hier nichts wuesste, was das Blatt nicht schon sagt. */
.blatt thead th:nth-last-child(-n+3) .kopf-tafel { left: auto; right: 0; }
/* Esc raeumt die Tafeln weg, bis der Zeiger sich wieder bewegt. */
.blatt.tafeln-still .kopf-tafel { visibility: hidden; }

.kt-klartext { font-weight: 500; }
.kt-schranke { color: var(--tinte-still); }
.kt-trenner { padding: 0 0.2rem; }
.kt-pflicht { color: var(--tinte); font-weight: 500; }
/* Farbe traegt nie allein (CLAUDE.md §3): die Nachsicht steht als Satz da,
   nicht als Tonwert. */
.kt-nachsicht { color: var(--tinte-still); font-style: italic; }
.kt-werte { color: var(--tinte-leise); }
.kt-frei { font-style: italic; }

/* --- Das Menü selbst --------------------------------------------------------
   Es überlagert, statt zu schieben: beim Klicken bewegt sich nichts
   (CLAUDE.md §1). Der Rahmen des Blatts rollt in sich; ein Menü darin würde
   an seiner Kante abgeschnitten. `blattmenue.js` hebt das offene Menü darum
   an den Körper und setzt es fest — ohne Skript bleibt es hier stehen und
   rollt mit, was zwar enger, aber bedienbar ist. */
.colmenu {
  position: absolute; z-index: 60; width: 15rem;
  left: auto; right: 0; top: 1.7rem;
  background: var(--papier); color: var(--tinte);
  border: 1px solid var(--linie); border-radius: 3px;
  box-shadow: 0 6px 20px rgb(16 42 67 / 0.16);
  font-family: "Fira Sans", "Segoe UI", system-ui, sans-serif; font-size: 0.8rem;
  font-weight: 400; letter-spacing: 0; text-transform: none;
  white-space: normal; text-align: left;
  display: flex; flex-direction: column;
}
.cm-sortieren { display: flex; flex-direction: column; }
.cm-sortieren a {
  padding: 0.4rem 0.7rem; text-decoration: none; color: var(--tinte);
  border: 1px solid transparent;
}
.cm-sortieren a:hover { background: var(--flaeche); }
.cm-sortieren a.an { color: var(--rot); font-weight: 500; }
.cm-trenner { border-top: 1px solid var(--linie-zart); }
.cm-suche { padding: 0.45rem 0.6rem 0.3rem; }
.cm-suche input {
  width: 100%; font: inherit; font-size: 0.8rem;
  padding: 0.2rem 0.4rem; background: var(--flaeche);
  border: 1px solid var(--linie); border-radius: 2px;
}
.cm-suche input:focus-visible {
  outline: 0; border-color: var(--rot); box-shadow: 0 0 0 2px rgb(225 0 26 / 0.15);
}
.cm-liste { max-height: 15rem; overflow-y: auto; padding: 0.15rem 0; }
.cm-eintrag {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.25rem 0.7rem; cursor: pointer;
  text-decoration: none; color: var(--tinte);
  border: 1px solid transparent;
}
.cm-eintrag:hover { background: var(--flaeche); }
.cm-eintrag input { flex: none; accent-color: var(--rot); }
.cm-text {
  flex: 1; min-width: 0; overflow-wrap: anywhere;
  font-size: 0.75rem;
}
.cm-zahl {
  font-size: 0.7rem; color: var(--tinte-leise); font-variant-numeric: tabular-nums;
}
.cm-haken { width: 0.8rem; color: var(--rot); }
.cm-eintrag-fest { opacity: 0.55; cursor: default; }
.cm-leer { margin: 0; padding: 0.5rem 0.7rem; color: var(--tinte-leise); }
.cm-titel {
  padding: 0.45rem 0.7rem; font-size: var(--spur);
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--tinte-leise); border-bottom: 1px solid var(--linie-zart);
}
.cm-fuss {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.5rem; padding: 0.45rem 0.6rem; border-top: 1px solid var(--linie-zart);
}

.blatt-ecke, .blatt tbody .zeilennummer {
  position: sticky; left: 0; z-index: 4;
  background: var(--flaeche);
  font-variant-numeric: tabular-nums; font-weight: 400;
  text-align: right; padding: 0.25rem 0.5rem;
  color: var(--tinte-leise); border-right: 1px solid var(--linie);
}
/* «Zeile» ist die einzige Spalte, die nicht aus der Lieferung stammt, sondern
   von BiStat. Sie darf darum als Beschriftung auftreten — und macht damit
   sichtbar, wo die eigene Zutat aufhört und die Datei der Schule anfängt. */
.blatt-ecke {
  z-index: 5;
  text-transform: uppercase; letter-spacing: 0.05em;
}
.blatt-filterzeile td:first-child { left: 0; z-index: 4; }

.blatt tbody td { background: var(--papier); }

/* Die Zelle traegt ihren Innenabstand im Eingabefeld — steht dort keines
   (abgegebene Lieferung, Klartext), muss ihn der Wert selbst mitbringen.
   Genau dieselben Masse wie am Feld: sonst ruecken beim Umschalten alle
   Spalten, und «beim Klicken bewegt sich nichts» (CLAUDE.md §1). */
.blatt tbody .zellwert {
  display: block; padding: 0.25rem 0.5rem;
  font-size: 0.85rem; font-variant-numeric: tabular-nums;
  min-width: 5rem;
}
/* Im Klartext steht eine Bezeichnung, kein Code — Tabellenziffern haetten
   daran nichts auszurichten und sperrten nur die Wortbilder. */
.blatt tbody .zellwert-klar { font-variant-numeric: normal; }
.blatt tbody tr:hover td { background: #fbfcfd; }

/* Die Zelle ist das Eingabefeld — kein Kasten im Kasten. Erst der Fokus
   zeichnet sie aus, wie in einer Tabellenkalkulation. */
.blatt tbody input {
  font: inherit; font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  width: 100%; min-width: 5rem;
  padding: 0.25rem 0.5rem;
  color: inherit; background: transparent;
  border: 0; border-radius: 0;
}
.blatt tbody input:focus-visible {
  outline: 0; background: var(--papier);
  box-shadow: inset 0 0 0 2px var(--rot);
}
.blatt tbody .geaendert input,
.blatt tbody input.geaendert { background: #fff8e1; box-shadow: inset 0 0 0 1px var(--ocker); }

/* Die beanstandete Zelle traegt Farbe UND Form. Im Blatt ist die Zelle ein
   Eingabefeld; die Wellenlinie aus der Befundsicht greift daran nicht, also
   uebernimmt die Flaeche das Zeigen. Der eingelegte Strich links bleibt
   trotzdem — Farbe allein waere die einzige Auskunft und damit fuer
   Farbschwaeche keine (WCAG 1.4.1). box-shadow statt Rahmen, damit die
   Zelle ihre Masse behaelt (CLAUDE.md §1). */
.blatt tbody td.zelle-fehler {
  background: #fdeaec; box-shadow: inset 3px 0 0 var(--rot);
}
.blatt tbody td.zelle-warnung {
  background: #fff6e5; box-shadow: inset 3px 0 0 var(--ocker);
}
.blatt tbody td.zelle-hinweis {
  background: var(--flaeche); box-shadow: inset 3px 0 0 var(--tinte-still);
}
.blatt tbody td.zelle-erledigt {
  background: #eef7ef; box-shadow: inset 3px 0 0 var(--gruen);
}
/* Beim Ueberfahren bleibt die Beanstandung stehen: sonst verschwaende genau
   die Zelle, auf die man zeigt. */
.blatt tbody tr:hover td.zelle-fehler   { background: #fce0e4; }
.blatt tbody tr:hover td.zelle-warnung  { background: #fff0d6; }
.blatt tbody tr:hover td.zelle-erledigt { background: #e6f2e8; }

.blatt .zelle-fehler  input { color: var(--rot-tief); font-weight: 500; }
.blatt .zelle-warnung input { color: var(--ocker); font-weight: 500; }
.blatt .zelle-erledigt input { color: var(--gruen); }
.blatt .zeile-befund .zeilennummer { border-left: 3px solid var(--rot); }

.blatt-fuss {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  margin: 0.8rem 0 0.4rem;
}
#blatt-uebernehmen[disabled] { opacity: 0.4; }

@media (max-width: 60rem) {
  .blatt-abschnitt {
    width: auto; max-width: 100%; margin-inline: 0;
  }
}

/* --- Der Ablauf -------------------------------------------------------------
   Fuenf Schritte, waagrecht. Die Schule bedient BiStat ein- bis zweimal im
   Jahr; sie muss sehen koennen, wo sie steht, ohne es sich herzuleiten.
   Bewusst keine Grafik, kein Fortschrittsbalken — eine Liste mit Zustand,
   wie das Altsystem sie zeigte (Folie 7: technisches Arbeitsinstrument).

   Der gesperrte Schritt nennt seinen Grund, statt nur blass zu sein: «Erst
   nach der Pruefung moeglich» beantwortet die Frage, die ein ausgegrauter
   Knopf aufwirft. */
.ablauf {
  display: flex; flex-wrap: wrap;
  list-style: none; margin: 0 0 1.2rem; padding: 0;
  border: 1px solid var(--linie); border-radius: 3px;
  background: var(--flaeche);
}
.ablaufschritt {
  display: flex; align-items: stretch; gap: 0.5rem;
  flex: 1 1 12rem; min-width: 0;
  padding: 0.55rem 0.7rem;
  border-right: 1px solid var(--linie-zart);
  border-left: 3px solid transparent;
}
/* Gleich breite Karten — die Leiste ist eine Folge, keine Rangordnung.
   Ausnahme: solange die Dateiwahl in der Karte steht, braucht sie mehr Lauf,
   sonst bricht «Datei waehlen · keine Datei gewaehlt · Datei hochladen» um
   und zieht die ganze Leiste in die Hoehe. Das Mehr ist knapp bemessen: mit
   den 1.8 aus der Zeit der vier Karten war die erste bei drei Karten 555 px
   breit und die uebrigen 314 — die Leiste kippte nach links. */
.ablauf-hochladen.ist-weit { flex: 1.3 1 21rem; }
.ablaufschritt:last-child { border-right: 0; }
.ablaufmarke {
  /* Die Marke bleibt auf Zeilenhoehe des Titels, auch wenn die Karte durch den
     Handgriff waechst — `align-items: stretch` streckte sie sonst mit. */
  align-self: flex-start;
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.35rem; height: 1.35rem; flex: none; border-radius: 50%;
  font-size: 0.72rem;
  background: var(--papier); border: 1px solid var(--linie);
  color: var(--tinte-leise);
}
.ablauftext { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; flex: 1 1 auto; }
.ablauftitel { font-size: 0.82rem; font-weight: 500; }
.ablaufhilfe { font-size: 0.72rem; color: var(--tinte-leise); }

/* Der Handgriff sitzt im Schritt, nicht in einer Leiste darunter. Er steht
   immer an derselben Stelle der Karte — unter der Hilfe, linksbuendig zum
   Text —, damit das Auge ihn nicht suchen muss.

   `margin-top: auto` schiebt ihn an den Fuss der Karte: sonst haengt der
   Knopf einer kurzen Hilfe hoeher als der einer langen, und die Zeile
   franst aus. */
.ablaufwerk {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.6rem;
  margin-top: auto; padding-top: 0.45rem;
}
.ablaufwerk form { margin: 0; }
/* Die Knoepfe im Schritt sind knapper als die im Fliesstext: in einer Karte
   von rund zwanzig Zeichen Breite wirkt die volle Groesse wie ein Block. Die
   24 px Zielflaeche bleiben gehalten (WCAG 2.2 SC 2.5.8) — 0.52rem Polsterung
   plus 1.4 Zeilenhoehe auf 0.78rem ergeben rund 28 px. */
.ablaufwerk button, .ablaufwerk a.knopf, .ablaufwerk .dateiwahl-knopf, .ablaufsprung {
  font-size: 0.78rem; padding: 0.26rem 0.7rem;
  /* Kein Umbruch: «Datei hochladen» ueber zwei Zeilen macht aus einem Knopf
     einen Kasten, und die ganze Leiste waechst mit. Passt die Zeile nicht,
     rutscht der Knopf ganz nach unten — aber er bleibt einzeilig. */
  white-space: nowrap;
}
/* Der Platz fuer den Dateinamen ist reserviert, damit der Knopf daneben beim
   Waehlen nicht zur Seite rueckt. Reserviert heisst Mindestmass, nicht
   Obergrenze: ein laengerer Name bricht um, statt abgeschnitten zu werden. */
.ablaufwerk .dateiwahl-name { min-width: 7.4rem; font-size: 0.8rem; }
.ablaufwerk .dateiwahl-marke { gap: 0.45rem; font-size: 0.8rem; }
.ablaufwerk .zeilenform { gap: 0.35rem 0.5rem; flex-wrap: wrap; }
/* Die Rueckstellung [DA 4.1.4.5] ist kein einzelner Knopf, sondern Marke,
   Begruendungsfeld und Erklaerung. Untereinander statt in einer Reihe: in
   einer Karte von rund zwanzig Zeichen Breite stuenden die drei sonst
   verschachtelt nebeneinander, und die Erklaerung landete neben dem Feld,
   auf das sie sich bezieht. Sie erscheint ohnehin nur dem Administrator an
   einer abgegebenen Lieferung — sonst ist sie nicht gezeichnet. */
.ablaufwerk-rueckstellen {
  flex-direction: column; align-items: stretch; gap: 0.3rem;
  margin-top: 0.6rem; padding-top: 0.5rem;
  border-top: 1px solid var(--linie);
}
.ablaufwerk-rueckstellen .zeilenform { flex-wrap: nowrap; }
.ablaufwerk-rueckstellen input[type="text"] { flex: 1; min-width: 0; }
/* Berichtigt wird im Blatt weiter unten — ein Sprung, keine Handlung. Er
   traegt trotzdem dasselbe Kastenmass wie ein Knopf: in einer Reihe von
   Schaltflaechen saehe ein nackter Verweis wie ein vergessenes Stueck aus.
   Kante in `--kante` (3.33:1, WCAG 1.4.11), Hoehe ueber die Polsterung wie
   bei `button` — 24 px sind gehalten (SC 2.5.8). */
.ablaufsprung {
  display: inline-flex; align-items: center;
  font-weight: 500; line-height: 1.4;
  border: 1px solid var(--kante); border-radius: 3px;
  text-decoration: none;
}
.ablaufsprung:hover { border-color: var(--tinte-leise); background: var(--flaeche); }

/* Erledigt: Haken und ruhige Farbe. Der Haken ist die Form, die Farbe nur
   die Bestaetigung — bei Farbschwaeche bleibt er lesbar (WCAG 1.4.1). */
.ist-erledigt .ablaufmarke { background: var(--gruen); border-color: var(--gruen); color: #fff; }
.ist-erledigt .ablauftitel { color: var(--tinte-leise); }

/* Jetzt dran: die Kante links traegt es, nicht die Flaeche. */
.ist-dran { border-left-color: var(--rot); background: var(--papier); }
.ist-dran .ablaufmarke { border-color: var(--rot); color: var(--rot); }
.ist-dran .ablauftitel { color: var(--tinte); font-weight: 700; }
.ablaufjetzt {
  /* Auf Titelhoehe, nicht mittig: seit der Handgriff in der Karte steht, ist
     die Karte hoeher als ihr Text — mittig schwebte die Marke neben nichts. */
  margin-left: auto; align-self: flex-start; flex: none; padding-top: 0.15rem;
  font-size: var(--spur); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--rot);
}
.ist-gesperrt .ablauftitel, .ist-gesperrt .ablaufmarke { color: var(--tinte-still); }
.ist-gesperrt .ablaufhilfe { font-style: italic; }

/* Verwerfen loescht Daten — die Schaltflaeche sagt das, ohne zu schreien. */
button.gefahr { color: var(--rot-tief); }
button.gefahr:hover { border-color: var(--rot); }

@media (max-width: 60rem) {
  .ablauf { flex-direction: column; }
  .ablaufschritt { border-right: 0; border-bottom: 1px solid var(--linie-zart); }
  .ablaufschritt:last-child { border-bottom: 0; }
}

/* --- Startseite der Schule: Tafel und Termine --------------------------------
   Dieselben Lieferungen in zwei Ordnungen. Die Stilklassen der Tafel kommen
   aus der Ueberwachung (.tafel-spalten, .tafelkarte) — dort ist eine Karte
   eine Schule, hier eine Erhebung, aber es ist dieselbe Art Tafel und soll
   auch so aussehen. */
.tafel-kopfzeile {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
}
.tafel-kopfzeile h1 { margin-bottom: 0.4rem; }
.sichtwahl { display: flex; gap: 0.9rem; margin: 0; font-size: 0.82rem; }
.sichtwahl a {
  color: var(--tinte-leise); text-decoration: none;
  padding-bottom: 0.15rem; border-bottom: 2px solid transparent;
}
.sichtwahl a:hover { color: var(--tinte); }
.sichtwahl a[aria-current="page"] {
  color: var(--tinte); border-bottom-color: var(--rot); font-weight: 500;
}

/* Die Karte nennt den naechsten Handgriff mit. Wer die Tafel ansieht, will
   nicht wissen, in welchem Zustand etwas ist, sondern was er tun muss. */
.karte-zeile { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.karte-frist {
  font-size: 0.72rem; color: var(--tinte-leise); font-variant-numeric: tabular-nums;
}
.karte-naechstes { font-size: 0.72rem; color: var(--rot); font-weight: 500; }
.tafelkarte-saeumig .karte-naechstes { color: var(--rot-tief); }

.tafel-spalte-kopf {
  display: flex; align-items: center; gap: 0.5rem;
  margin: 0 0 0.5rem; font-size: 0.82rem; font-weight: 400;
}
.tafel-spalte-kopf .tafel-zahl {
  margin-left: auto;
  font-size: 0.75rem; color: var(--tinte-leise);
}

/* Termine: eine Liste nach Wochen, kein Monatsraster. Ein Raster zeigt vor
   allem leere Tage; eine Liste zeigt, was ansteht. */
.terminblock { margin: 0 0 1.6rem; }
.terminblock-kopf {
  margin: 0 0 0.5rem; font-size: var(--spur);
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--tinte-leise);
  border-bottom: 1px solid var(--linie-zart); padding-bottom: 0.3rem;
}
.terminliste { display: flex; flex-direction: column; gap: 0.35rem; }
.terminliste .tafelkarte { max-width: 34rem; }

/* Die Schule auf der Karte: leise, aber da. Ohne sie stehen zwei Erhebungen
   desselben Namens nebeneinander und sind nicht zu unterscheiden. */
.karte-schule {
  display: block; font-size: 0.72rem; color: var(--tinte-still);
  overflow-wrap: anywhere;
}

/* Abgegeben heisst fertig. «Nichts mehr zu tun» in Rot las sich wie ein
   Alarm ueber erledigter Arbeit — Rot ist der Ton fuer «das braucht dich». */
.karte-fertig { color: var(--tinte-still); font-weight: 400; }

/* Eine leere Spalte braucht keine Hoehe auf Verdacht (CLAUDE.md §1). */
.tafel-spalte:empty, .tafel-spalte > :only-child { min-height: 0; }


/* --- Die Abgabetafel [DA 4.1.4.4] ------------------------------------------

   «Die Datenabgabe kann zu diesem Moment noch abgebrochen werden.» Der
   Abschlussbericht legt sich als Fenster ueber die Maske — er schiebt nichts
   weg, und dahinter bleiben die Daten sichtbar (CLAUDE.md §1).

   `position: fixed` und nicht `absolute`: die Maske darunter ist lang, und
   eine absolut gesetzte Tafel haenge am Blattanfang, waehrend die
   Schulleitung unten am Ablauf geklickt hat. Fest im Sichtfeld liegt sie
   dort, wo hingeschaut wird.

   Der Grund darunter ist gedeckt, nicht schwarz: die Zahlen dahinter sollen
   erkennbar bleiben. Kein Ueberblenden — `prefers-reduced-motion` muesste es
   ohnehin wieder ausnehmen, und ohne JavaScript entsteht die Tafel mit dem
   Seitenaufbau, nicht mit einem Klick. */
.tafelgrund {
  position: fixed; inset: 0; z-index: 80;
  display: flex; align-items: center; justify-content: center;
  padding: 1.5rem;
  background: rgba(20, 22, 26, 0.55);
}
.abgabetafel {
  background: var(--papier); color: var(--tinte);
  border: 1px solid var(--kante);
  box-shadow: 0 0.5rem 2rem rgba(0, 0, 0, 0.35);
  padding: 1.25rem 1.5rem;
  width: min(44rem, 100%);
  /* Nur die Tafel rollt, nie das Blatt dahinter: eine Kreuztabelle mit
     zwanzig Schularten passt sonst auf einem 13-Zoll-Schirm nicht. */
  max-height: 100%; overflow-y: auto;
}
.abgabetafel h2 { margin-top: 0; }
.abgabetafel .datentabelle { margin-bottom: 1.1rem; }
/* Der Bestaetigungstext steht ueber den Zahlen und traegt sie: er ist die
   Aussage, die die Schulleitung mit dem Klick unterschreibt. */
.abgabetafel-satz { margin: 0 0 1.1rem; font-weight: 600; }
.abgabetafel-werk {
  display: flex; gap: 0.7rem; align-items: center; flex-wrap: wrap;
  margin-top: 1.2rem; padding-top: 0.9rem;
  border-top: 1px solid var(--linie);
}
.abgabetafel-werk form { margin: 0; }



/* --- Kontaktdaten und Funktion in der Benutzerliste [DA 4.1.1.3] ------------

   Die Kontaktspalte traegt zwei Zeilen und dahinter das Formular zum
   Berichtigen. Ein dauerhaft offenes Formular je Benutzer waere vier Felder
   mal Zeilenzahl an Flaeche fuer etwas, das man selten tut (CLAUDE.md §1). */
.kontaktspalte .kontaktzeile { display: block; }
.kontakt-leise { color: var(--tinte-leise); font-size: var(--spur); }
/* Die Handlung in einer Tabellenzelle bleibt schmal: ohne Begrenzung zieht das
   aufgeklappte Formular die Spalte auf und die Zuordnungen daneben werden
   gequetscht. */
.handlung-schmal > summary { font-size: var(--spur); }
.handlung-schmal .stammform { min-width: 13rem; }
/* Die Funktion steht in der Marke neben dem Schulnamen — leiser als er, weil
   sie ihn beschreibt und nicht ersetzt. Sie traegt ihr Wort und nicht nur
   eine Faerbung (CLAUDE.md §3). */
.marke-funktion {
  color: var(--tinte-leise); font-size: var(--spur);
  text-transform: uppercase; letter-spacing: 0.04em;
}


/* --- Information zur laufenden Erhebung [DA 4.1.4.1] ------------------------

   Sie steht ueber der Arbeit und ist als Mitteilung erkennbar, ohne sie zu
   verdraengen: eine ruhige Flaeche mit einer Kante links, kein Alarm. Rot ist
   Kennzeichen und Fehler, sonst nichts (CLAUDE.md §7) — eine Mitteilung ist
   weder das eine noch das andere.

   Nur gezeichnet, wenn es etwas mitzuteilen gibt; die Schablone laesst den
   Block sonst ganz weg (kein Platz auf Verdacht). */
.information {
  margin: 0 0 1.5rem;
  padding: 0.8rem 1rem;
  background: var(--werkbank);
  border-left: 3px solid var(--kante);
}
.information h2 { margin: 0 0 0.4rem; }
.informationszeile { margin: 0.35rem 0 0; }
.informationszeile:first-of-type { margin-top: 0; }
/* Die Erhebung vor dem Text: eine Schule kann mehrere gleichzeitig offen
   haben, und ein Text ohne seine Erhebung liesse offen, worauf er sich
   bezieht. */
.informationsquelle {
  display: block;
  font-size: var(--spur); color: var(--tinte-leise);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.informationstext { display: block; }


/* --- Institutionsblatt: Schalter und Notizen [DA 4.1.1.4] ------------------- */

.schalterliste, .notizliste { list-style: none; margin: 0 0 1.5rem; padding: 0; }
.schalterzeile { padding: 0.5rem 0; border-bottom: 1px solid var(--linie); }
.schalterzeile:last-child { border-bottom: none; }
.schalterzeile label { display: inline-flex; align-items: center; }
/* Erklaerung und Spur unter der Zeile, leiser als sie: sie beschreiben den
   Schalter, sie sind nicht er. Beide stehen fest da — nichts klappt auf,
   nichts schiebt (CLAUDE.md §1). */
.schaltererklaerung, .schalterspur {
  margin: 0.2rem 0 0 1.7rem;
  font-size: var(--spur); color: var(--tinte-leise);
}

.notiz {
  padding: 0.6rem 0 0.7rem;
  border-bottom: 1px solid var(--linie);
}
.notiz:last-child { border-bottom: none; }
.notizkopf {
  display: flex; gap: 0.7rem; margin: 0 0 0.25rem;
  font-size: var(--spur); color: var(--tinte-leise);
  text-transform: uppercase; letter-spacing: 0.05em;
}
/* Der Text bricht um, statt die Spalte aufzuziehen: eine Notiz ist ein Satz
   und kein Feld, und lange Woerter (eine Adresse etwa) sollen die Maske nicht
   waagrecht rollen lassen. */
.notiztext { margin: 0; overflow-wrap: anywhere; }


/* --- Dublettenfall [DA 4.1.6.2] ---------------------------------------------

   Ein Fall ist eine Karte und keine Tabellenzeile: «Jeder Fall zeigt beide
   Datensaetze nebeneinander», und zwei Datensaetze mit je einem Dutzend
   Merkmalen passen in keine Zeile. Nebeneinander heisst woertlich
   nebeneinander — nur nebeneinander laesst sich vergleichen, was sich
   unterscheidet. */
.dublettenfall {
  margin: 0 0 1.5rem; padding: 0.9rem 1rem;
  border: 1px solid var(--linie);
}
.dublettenkopf {
  display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap;
  margin: 0 0 0.8rem;
}
.dublettenseiten { display: flex; gap: 1.5rem; flex-wrap: wrap; align-items: flex-start; }
/* Gleiche Breite, damit die Zeilen der beiden Seiten auf gleicher Hoehe
   stehen. `min-width: 0` statt einer festen Breite: die Seite darf schmaler
   werden als ihr laengster Wert, statt die Karte waagrecht zu rollen. */
.dublettenseite { flex: 1 1 18rem; min-width: 0; }
.dublettenseite h3 { margin: 0 0 0.4rem; font-size: 0.95rem; }
.dublettenfuss { margin-top: 0.9rem; padding-top: 0.7rem; border-top: 1px solid var(--linie); }
/* Auf schmalen Schirmen untereinander — nebeneinander waeren beide Seiten so
   eng, dass keine mehr lesbar ist. */
@media (max-width: 40rem) {
  .dublettenseiten { flex-direction: column; }
}

/* --- Parameter einer Pruefregel -------------------------------------------
   Die neun Parametersaetze liegen im selben Kasten uebereinander; sichtbar ist
   der des gewaehlten Regeltyps. Der Kasten behaelt dabei seine Masse: waechst
   und schrumpft er, wandern Handlungsanweisung und Schaltflaeche unter dem
   Zeiger weg. Gestaltungsgrundsatz 2 verlangt genau das Gegenteil — was
   Inhalt ersetzt, behaelt dessen Masse.

   Die reservierte Hoehe ist gemessen, nicht geschaetzt. Der erste Ansatz
   nahm T7 als groessten Satz an (vier Parameter, zweispaltig zwei Zeilen,
   9.6rem) — im Headless-Chromium brauchte T7 dann 168.91 px und T8 mit seiner
   vierzeiligen Mehrfachauswahl 182.05 px, und die Schaltflaeche wanderte bei
   genau diesen zwei Typen um 15 beziehungsweise 28 px. 11.5rem (184 px) traegt
   auch T8. Einspaltig waeren es vier Zeilen, und fuer T2 stuende alles leer. */
.parameterbereich {
  margin-top: 1.1rem;
  padding: 0.6rem 0.75rem 0.75rem;
  border: 1px solid var(--kante-zart);
  border-radius: 4px;
  min-height: 11.5rem;
}
.parameterkopf {
  margin: 0 0 0.45rem;
  font-size: 0.72rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--tinte-leise);
}
.parametersatz { display: grid; grid-template-columns: 1fr 1fr; gap: 0.1rem 0.7rem; }
.parametersatz .parameterfeld > label { margin-top: 0.35rem; }
.parameterleer { grid-column: 1 / -1; margin: 0.7rem 0 0; }

/* :has() steht bewusst in eigenen Zeilen und nicht in einer Sammelregel:
   ein Browser, der den Selektor nicht kennt, wirft die GANZE Gruppe weg —
   dann blendete nichts mehr, nirgends. Faellt :has() aus, bleiben hier alle
   neun Saetze sichtbar; das Formular wird laenger, bleibt aber vollstaendig
   bedienbar, denn der Server liest ohnehin nur die Parameter des gewaehlten
   Typs (parameterAusFormular). Deshalb blendet die erste Regel aus, statt
   dass die Schablone die acht anderen von Anfang an verbirgt. */
.stammform:has(#r-rtyp option:checked) .parametersatz { display: none; }
.stammform:has(#r-rtyp option[value="T1"]:checked) .parametersatz[data-typ="T1"] { display: grid; }
.stammform:has(#r-rtyp option[value="T2"]:checked) .parametersatz[data-typ="T2"] { display: grid; }
.stammform:has(#r-rtyp option[value="T3"]:checked) .parametersatz[data-typ="T3"] { display: grid; }
.stammform:has(#r-rtyp option[value="T4"]:checked) .parametersatz[data-typ="T4"] { display: grid; }
.stammform:has(#r-rtyp option[value="T5"]:checked) .parametersatz[data-typ="T5"] { display: grid; }
.stammform:has(#r-rtyp option[value="T6"]:checked) .parametersatz[data-typ="T6"] { display: grid; }
.stammform:has(#r-rtyp option[value="T7"]:checked) .parametersatz[data-typ="T7"] { display: grid; }
.stammform:has(#r-rtyp option[value="T8"]:checked) .parametersatz[data-typ="T8"] { display: grid; }
.stammform:has(#r-rtyp option[value="T9"]:checked) .parametersatz[data-typ="T9"] { display: grid; }

/* --- Wissensdatenbank [DA 3.5] -------------------------------------------
   Artikel als Abschnitte, nicht als Tabellenzeilen: hier stehen Fliesstexte
   unterschiedlicher Laenge, und eine Tabelle zwaenge sie in eine Zeilenhoehe,
   die entweder abschneidet oder Luft verschenkt. */
.wissensartikel {
  max-width: 46rem;
  padding: 0.9rem 0 1.1rem;
  border-bottom: 1px solid var(--linie);
}
.wissensartikel:last-of-type { border-bottom: none; }
.wissensartikel h2 { margin: 0 0 0.4rem; font-size: 1rem; font-weight: 600; }
/* white-space: pre-line laesst die Absaetze des Administrators stehen, ohne
   dass er Auszeichnung schreiben muesste — der Text wird maskiert gesetzt
   (siehe hilfe.html), Zeilenumbrueche sind das einzige Gestaltungsmittel,
   das ihm bleibt, und sie sollen wirken. */
.wissensrumpf {
  margin: 0; white-space: pre-line;
  line-height: 1.55; color: var(--tinte);
}
.wissensfuss {
  margin: 0.5rem 0 0;
  font-size: 0.78rem; color: var(--tinte-leise);
}

/* Mehrere Handlungen im Maskenkopf nebeneinander [DA 4.1.1.1]. Bis 0.63.0
   trug der Kopf hoechstens eine; das Institutionsblatt braucht Aendern UND
   Schliessen. Die Tafeln bleiben `position: relative` je Handlung, ueberlagern
   also weiterhin einzeln und schieben nichts (CLAUDE.md §1). */
.handlungen {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  gap: 0.5rem;
}
.handlungen > form { margin: 0; }

/* Ein stillgelegtes Konto [DA 4.1.1.3]. Die Farbe nimmt die Zeile zurueck,
   die Auskunft traegt die Marke daneben — Farbe steht nie allein
   (CLAUDE.md §3). Kein Kastenmass aendert sich. */
.stillgelegt > td { color: var(--tinte-leise); }
