diff --git a/server/pruef-css-klassen.mjs b/server/pruef-css-klassen.mjs
index 57e92de6..45a65aa7 100644
--- a/server/pruef-css-klassen.mjs
+++ b/server/pruef-css-klassen.mjs
@@ -899,5 +899,90 @@ function formfehler(text) {
: `Gegenprobe: alle ${proben.length} Faelle richtig eingeordnet`);
}
+/* ==== ZWEI GRUNDREGELN, EIN NAME, ZWEI BAUARTEN =====================
+
+ Gebaut am 28.09.2026 fuer `reaktion.css`, wo `.stufen` zweimal
+ stand: einmal fuer die Stufenleiter der Spenden (`display: grid`),
+ 600 Zeilen spaeter fuer die Chat-Leiste (`display: flex`). Die
+ spaetere gewinnt -- und die Stufenleiter stellte ihre drei Stufen
+ nebeneinander, 572 Pixel in einer 327 Pixel schmalen Spalte, mit
+ waagerechter Rollleiste. Auf dem Bildschirmfoto sah das aus wie
+ ein Platzproblem.
+
+ AB JETZT FUER ALLE STILVORLAGEN. Eine Wache, die nur eine Datei
+ ansieht, findet genau die Fehler dieser einen Datei -- und der
+ zweite Fund lag in gate.css.
+
+ GEZAEHLT WIRD NUR, WAS SICH WIDERSPRECHEN KANN:
+ - Die Auswahl muss GENAU die Klasse sein. `.saal .pult { … }`
+ ist dieselbe Klasse in einem Zusammenhang, kein zweites
+ Bauteil.
+ - Regeln in einer Medienabfrage zaehlen nicht -- sie sind per
+ Definition Varianten fuer eine andere Umgebung.
+ - `display: none` zaehlt nicht. „Erst versteckt, dann gezeigt"
+ ist das haeufigste Paar im Haus; wer es meldet, baut eine
+ Warnung, die immer kommt. */
+{
+ const ordner = join(HIER, "..", "workspace", "assets", "css");
+ const ohneMedien = (t) => t.replace(
+ /@media[^{]*\{(?:[^{}]|\{[^{}]*\})*\}/g,
+ (m) => m.replace(new RegExp("[^" + String.fromCharCode(10) + "]", "g"), " "));
+ const positiv = (w) => w && w !== "none" && !/^none\b/.test(w);
+ /* LOOKBEHIND STATT VERBRAUCHEN: Mit `(^|[};])` frisst der erste
+ Treffer die Klammer, die der naechste als Anfang braeuchte --
+ `.a { … } .a { … }` liefert dann nur EINEN Treffer, und genau
+ der Fall, um den es geht, faellt durch. */
+ const grundregeln = (roh) => {
+ const raus = {};
+ const text = ohneMedien(roh.replace(/\/\*[\s\S]*?\*\//g,
+ (m) => m.replace(new RegExp("[^" + String.fromCharCode(10) + "]", "g"), " ")));
+ for (const m of text.matchAll(
+ /(?:^|(?<=[};]))\s*(\.(-?[a-zA-Z_][a-zA-Z0-9_-]*))\s*\{([^{}]*)\}/g)) {
+ const w = (m[3].match(/(?:^|[\s;])display\s*:\s*([^;]+)/) || [, ""])[1].trim();
+ if (!positiv(w)) continue;
+ (raus[m[2]] ||= []).push(w);
+ }
+ return raus;
+ };
+ const streitIn = (roh) => Object.entries(grundregeln(roh))
+ .filter(([, was]) => new Set(was).size > 1)
+ .map(([name, was]) => `${name} (${[...new Set(was)].join(" vs. ")})`);
+
+ const dateien = readdirSync(ordner).filter((n) => n.endsWith(".css"));
+ const funde = [];
+ let geprueft = 0;
+ for (const name of dateien) {
+ const roh = readFileSync(join(ordner, name), "utf8");
+ geprueft += Object.keys(grundregeln(roh)).length;
+ for (const z of streitIn(roh)) funde.push(`${name}: ${z}`);
+ }
+ ok(geprueft > 100 && funde.length === 0,
+ funde.length ? `Namensstreit: ${funde.join(" | ")}`
+ : `${geprueft} Grundregeln in ${dateien.length} Stilvorlagen -- `
+ + "keine Klasse zweimal verschieden gebaut");
+
+ /* GEGENPROBEN IN BEIDE RICHTUNGEN. */
+ const faelle = [
+ ["der Fall vom 28.09. (zwei Grundregeln, anderes display)",
+ ".stufen { display: grid; } .stufen { display: flex; }", true],
+ ["verstecken und zeigen",
+ ".probe { display: none; } .probe { display: grid; }", false],
+ ["dieselbe Klasse in einem Zusammenhang",
+ ".pult { display: grid; } .saal .pult { display: flex; }", false],
+ ["eine Variante in einer Medienabfrage",
+ ".paar { display: grid; } @media (pointer: coarse) { .paar { display: flex; } }", false],
+ ["zweimal dasselbe display",
+ ".gleich { display: grid; } .gleich { display: grid; }", false],
+ ["eine Klasse im Kommentar",
+ "/* .a { display: flex; } */ .a { display: grid; }", false],
+ ];
+ const daneben = faelle
+ .filter(([, text, soll]) => (streitIn(text).length > 0) !== soll)
+ .map(([name]) => name);
+ ok(daneben.length === 0,
+ daneben.length ? `Gegenprobe daneben bei: ${daneben.join(", ")}`
+ : `Gegenprobe: alle ${faelle.length} Faelle richtig eingeordnet`);
+}
+
console.log(`\n${fehler === 0 ? "ALLES IN ORDNUNG" : `${fehler} FEHLER`}`);
process.exit(fehler ? 1 : 0);
diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html
index 8341cf27..7dec16d9 100644
--- a/workspace/anruf-probe.html
+++ b/workspace/anruf-probe.html
@@ -16,8 +16,8 @@
verdeckt. Gefunden hat es pruef-struktur.
Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. -->
-
-
+
+
-
+