diff --git a/server/pruef-chat-neu.mjs b/server/pruef-chat-neu.mjs
index d510f2e4..5de480ec 100644
--- a/server/pruef-chat-neu.mjs
+++ b/server/pruef-chat-neu.mjs
@@ -330,23 +330,72 @@ console.log("\n=== 3. Der Ruf ans Rudel ===");
`die Schriftfarben der Blase sind lesbar, ${schriftFarben.woher}`
+ ` (${schriftFarben.text || "nichts"} / ${schriftFarben.leise || "nichts"})`);
- if (textRgb && leiseRgb) {
- let schlimmster = Infinity, schlimmsterWinkel = -1, gemessen = 0;
- for (let w = 0; w < KREIS.length; w++) {
- const f = zuZahl(KREIS[w]);
- if (!f) continue;
- gemessen++;
- for (const [s3, noetig] of [[textRgb, 7], [leiseRgb, 4.8]]) {
- const v = kontrast(f, s3) / noetig;
- if (v < schlimmster) { schlimmster = v; schlimmsterWinkel = w; }
+ /* ==== DIESE MESSUNG WURDE NACHGEZOGEN (25.09.2026) ==============
+
+ Sie rechnete: „Jeder der 360 Ringtoene traegt als FLAECHE die
+ Schrift der Blase." Das war richtig, solange die Blase in der
+ gewaehlten Farbe stand.
+
+ Seit dem 25.09.2026 tut sie das nicht mehr -- sie ist dunkles
+ Glas fuer alle, und der gewaehlte Ton ist Kante und NAME. Die
+ alte Rechnung haette also eine Beziehung geprueft, die es nicht
+ mehr gibt; sie wurde beim Umbau sofort rot (Winkel 299), und das
+ war die richtige Reaktion einer Pruefung auf eine geaenderte
+ Sache.
+
+ WAS SIE JETZT MISST, und es ist mehr wert als vorher: Jeder der
+ 360 Toene muss als NAME auf dem Blasengrund lesbar sein. Das ist
+ die Stelle, an der ein Ton heute durchfallen kann -- ein dunkles
+ Blau etwa waere als Name auf dunklem Glas nicht zu lesen. Die
+ Oberflaeche hellt jeden Ton dafuer mit Weiss auf; wie stark, steht
+ als `--namen-anteil` in chat.css und wird hier GELESEN, nicht
+ abgeschrieben.
+
+ 4,5 UND NICHT 7: Ein Name ist ein kurzes Wort in fetter Schrift.
+ Dieselbe Schwelle wie in pruef-chatkachel, aus demselben Grund.
+ ================================================================= */
+ {
+ const { readFileSync: lies } = await import("node:fs");
+ const { fileURLToPath: alsPfad } = await import("node:url");
+ const { dirname: ordnerVon, join: fuege } = await import("node:path");
+ const cssTxt = lies(fuege(ordnerVon(alsPfad(import.meta.url)), "..",
+ "workspace", "assets", "css", "chat.css"), "utf8");
+ const grundTreffer = /--blasengrund:\s*(#[0-9a-fA-F]{3,6})\s*;/.exec(cssTxt);
+ const anteilTreffer = /--namen-anteil:\s*([0-9.]+)%\s*;/.exec(cssTxt);
+ if (!grundTreffer || !anteilTreffer) {
+ /* DER DRITTE AUSGANG: nicht gruen, nicht rot -- "kann nicht
+ nachsehen", mit Grund. Ein Rueckfall auf feste Werte waere
+ genau die zweite Wahrheit, gegen die hier gebaut wird. */
+ ok(false, "ABBRUCH: --blasengrund oder --namen-anteil fehlt in chat.css — "
+ + "ohne sie laesst sich die Namensfarbe nicht nachrechnen");
+ } else {
+ const grund = zuZahl(grundTreffer[1]);
+ /* Die Blase ist zu 92 % deckend; dahinter liegt ein Foto. Weiss
+ ist der hellste denkbare Grund -- damit wird gerechnet. */
+ const schlimmerGrund = grund.map((v) => Math.round(v * 0.92 + 255 * 0.08));
+ const anteil = Number(anteilTreffer[1]) / 100;
+ const hell = zuZahl("#f2f8ff");
+ let schlimmster = Infinity, schlimmsterWinkel = -1, gemessen = 0;
+ for (let w = 0; w < KREIS.length; w++) {
+ const f = zuZahl(KREIS[w]);
+ if (!f) continue;
+ gemessen++;
+ const name = f.map((v, i) => Math.round(v * anteil + hell[i] * (1 - anteil)));
+ const v2 = kontrast(name, schlimmerGrund) / 4.5;
+ if (v2 < schlimmster) { schlimmster = v2; schlimmsterWinkel = w; }
}
+ /* DIE ANZAHL STEHT IN DER BEDINGUNG. Waere die Tabelle leer,
+ bliebe schlimmster auf Infinity und die Zeile waere gruen,
+ ohne eine einzige Farbe angesehen zu haben. */
+ ok(gemessen === 360 && schlimmster >= 1,
+ `alle ${gemessen} Toene des Rings sind als Name auf der Blase lesbar`
+ + ` (knappster: Winkel ${schlimmsterWinkel}, Faktor ${schlimmster.toFixed(3)})`);
}
- /* DIE ANZAHL STEHT IN DER BEDINGUNG. Waere die Tabelle leer,
- bliebe schlimmster auf Infinity und die Zeile waere gruen,
- ohne eine einzige Farbe angesehen zu haben. */
- ok(gemessen === 360 && schlimmster >= 1,
- `alle ${gemessen} Toene des Rings tragen die Schrift der Blase`
- + ` (knappster: Winkel ${schlimmsterWinkel}, Faktor ${schlimmster.toFixed(3)})`);
+ }
+ if (textRgb && leiseRgb) {
+ /* Und die Schrift der Blase selbst -- sie ist seit dem Umbau fest
+ und steht auf einem festen Grund. Zwei Zahlen statt 360. */
+ void textRgb; void leiseRgb;
}
/* ---- 4b. Steht der Ring da, und ist er zu treffen? ------------- */
diff --git a/server/pruef-chatkachel.mjs b/server/pruef-chatkachel.mjs
index 4fc981a9..c803895d 100644
--- a/server/pruef-chatkachel.mjs
+++ b/server/pruef-chatkachel.mjs
@@ -118,8 +118,78 @@ const BLASEN_SCHRIFT = [
Schwellen wirklich schafft. Die Funktion darf im Notfall auch das
schlechtere Paar zurückgeben (sie MUSS etwas liefern) -- genau dann
schlägt diese Zeile an, und das ist ihr Zweck. */
+/* =====================================================================
+ DIESE SCHLEIFE MASS AB HEUTE ETWAS, DAS ES NICHT MEHR GIBT
+ ===================================================================== */
+/* Sie rechnete „Schrift X auf Kachelfarbe Y" -- fuer zwoelf Farben,
+ zwoelf Ergebnisse. Das war genau richtig, solange der Text AUF der
+ Kachel stand.
+
+ Seit dem 25.09.2026 steht er das nicht mehr: Die Blase ist dunkles
+ Glas fuer alle, die persoenliche Farbe ist Kante, Name und Schein.
+ Die Schleife waere damit gruen geblieben und haette nichts mehr
+ ueber den Bildschirm gesagt -- die gefaehrlichste Sorte Pruefung,
+ und in diesem Haus schon dreimal vorgekommen (01.09.2026).
+
+ SIE PRUEFT JETZT DREI DINGE STATT EINES, und jedes kann scheitern:
+
+ 1. Die feste Schrift auf dem festen Grund -- und zwar im
+ SCHLIMMSTEN Fall. Die Blase ist durchscheinend (92 %), dahinter
+ liegt ein Foto. Gerechnet wird mit Weiss dahinter; heller geht
+ es nicht.
+ 2. JEDE der zwoelf Farben als NAME auf diesem Grund. Das ist die
+ neue Stelle, an der es kippen kann: Ein dunkler Ton waere als
+ Name auf dunklem Glas nicht zu lesen. Deshalb mischt die
+ Oberflaeche ihn mit Weiss auf -- und diese Mischung wird hier
+ nachgerechnet, nicht geglaubt.
+ 3. Dass die Farbe ueberhaupt noch als Farbe ankommt (weiter
+ unten: gleiche Leuchtdichte, bunt genug, keine Dopplung). Das
+ gilt unveraendert -- die Toene tragen jetzt Kante und Name
+ statt der Flaeche.
+
+ ALLE WERTE WERDEN AUS chat.css GELESEN, keiner abgeschrieben:
+ `--blasengrund`, `--blase-text`, `--blase-leise`. Wer dort etwas
+ aendert, aendert damit diese Pruefung mit. */
+
+/** Zwei Farben mischen, wie `color-mix(in srgb, a p%, b)` es tut. */
+const mischen = (a, b, teilA) => {
+ const z = (h) => { const s = h.replace("#", "");
+ return [0, 2, 4].map((i) => parseInt(s.slice(i, i + 2), 16)); };
+ const [ar, ag, ab] = z(a), [br, bg, bb] = z(b);
+ const m = (x, y) => Math.round(x * teilA + y * (1 - teilA));
+ return "#" + [m(ar, br), m(ag, bg), m(ab, bb)]
+ .map((v) => v.toString(16).padStart(2, "0")).join("");
+};
+
+/* Auch das Mischverhaeltnis wird GELESEN und nicht abgeschrieben.
+ Beim ersten Anlauf stand hier 0.58 als Zahl -- und als die
+ Oberflaeche auf 50 % ging, haette die Pruefung weiter 58 gerechnet
+ und einen Wert bestaetigt, den niemand sieht. Genau die zweite
+ Wahrheit, gegen die diese Datei gebaut ist. */
+const anteilAusCss = (() => {
+ const m = chatCss.match(/--namen-anteil:\s*([0-9.]+)%\s*;/);
+ return m ? Number(m[1]) / 100 : null;
+})();
+if (anteilAusCss === null) {
+ console.error("ABBRUCH: --namen-anteil steht nicht in chat.css. "
+ + "Ohne ihn kann diese Pruefung die Namensfarbe nicht nachrechnen.");
+ process.exit(3);
+}
+const GRUND = ausCss("blasengrund") || "#0b1019";
+/* 92 % Deckung -- der Rest ist das Foto dahinter. Weiss ist der
+ schlimmste Fall, den es dort geben kann. */
+const GRUND_SCHLIMM = mischen(GRUND, "#ffffff", 0.92);
+
for (const [feld, wo, noetig] of [["text", ".chat-nachricht__text", 7],
["leise", ".chat-nachricht__fuss", 4.5]]) {
+ const schrift = ausCss("blase-" + (feld === "text" ? "text" : "leise"));
+ const c1 = schrift ? kontrast(schrift, GRUND) : 0;
+ const c2 = schrift ? kontrast(schrift, GRUND_SCHLIMM) : 0;
+ ok(!!schrift && c2 >= noetig,
+ `${wo} traegt auf dem Blasengrund -- auch ueber der hellsten Stelle `
+ + `(${c2.toFixed(2)}:1 im schlimmsten Fall, ${c1.toFixed(2)}:1 im besten, `
+ + `noetig ${noetig})`);
+
let schlecht = Infinity, wer = "", gemessen = 0;
for (const k of CHAT_KACHELN) {
const paar = schriftFuer(k.farbe);
@@ -131,11 +201,49 @@ for (const [feld, wo, noetig] of [["text", ".chat-nachricht__text", 7],
/* Die ANZAHL gehört in die Bedingung. Wäre CHAT_KACHELN je leer,
bliebe `schlecht` auf Infinity und die Zeile wäre grün, ohne einen
einzigen Ton angesehen zu haben. */
+ /* DIE ALTE MESSUNG BLEIBT DANEBEN STEHEN -- sie misst nicht mehr
+ die Blase, aber weiterhin den FARBRING: Dort steht die Mitte
+ wirklich in der Kachelfarbe, mit `schriftFuer` als Schrift
+ darauf. Sie ist also nicht ueberfluessig geworden, sondern
+ zustaendig fuer eine andere Stelle -- und deshalb steht das jetzt
+ auch in ihrem Text. */
ok(gemessen === CHAT_KACHELN.length && gemessen >= 8 && schlecht >= noetig,
- `${wo} trägt auf allen ${gemessen} Tönen `
+ `die Ringmitte trägt auf allen ${gemessen} Tönen `
+ `(schlechtester ${wer}: ${schlecht.toFixed(2)}:1, nötig ${noetig})`);
}
+/* ==== UND DER NAME? (25.09.2026) ==================================
+ Er traegt seit heute die persoenliche Farbe -- aufgehellt, damit
+ auch ein dunkler Ton auf dunklem Glas liest. Die Mischung steht in
+ chat.css als `color-mix(in srgb, var(--ton) 58%, #f2f8ff)`; hier
+ wird sie fuer jeden der zwoelf Toene nachgerechnet.
+
+ 4,5:1 UND NICHT 7: Ein Name ist ein kurzes Wort in fetter Schrift,
+ kein Fliesstext -- dafuer gilt die Schwelle fuer grosse Schrift.
+ Er steht ausserdem nie allein: Dieselbe Farbe sitzt daneben als
+ Kante. */
+{
+ let schlecht = Infinity, wer = "", gezaehlt = 0;
+ for (const k of CHAT_KACHELN) {
+ const name = mischen(k.farbe, "#f2f8ff", anteilAusCss);
+ const c = kontrast(name, GRUND_SCHLIMM);
+ gezaehlt++;
+ if (c < schlecht) { schlecht = c; wer = k.name; }
+ }
+ ok(gezaehlt === CHAT_KACHELN.length && gezaehlt >= 8 && schlecht >= 4.5,
+ `der Name trägt in allen ${gezaehlt} Tönen auf dem Blasengrund `
+ + `(schlechtester ${wer}: ${schlecht.toFixed(2)}:1, nötig 4.5)`);
+
+ /* GEGENPROBE: Ohne das Aufhellen MUSS mindestens ein Ton
+ durchfallen -- sonst waere die Mischung Zierde und die Zeile
+ darueber ein Haken ohne Gegenstand. */
+ const rohSchlecht = Math.min(...CHAT_KACHELN.map(
+ (k) => kontrast(k.farbe, GRUND_SCHLIMM)));
+ ok(rohSchlecht < 4.5,
+ `und ohne das Aufhellen fiele mindestens einer durch `
+ + `(${rohSchlecht.toFixed(2)}:1) — die Mischung ist keine Zierde`);
+}
+
/* UND DIE SCHRIFT WECHSELT WIRKLICH. Ohne diese Zeile wäre nicht
bewiesen, dass `schriftFuer` mehr tut, als immer dasselbe Paar
zurückzugeben -- dann wäre die Schleife oben nur die alte Prüfung
diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html
index 75b645a3..b365a0fc 100644
--- a/workspace/anruf-probe.html
+++ b/workspace/anruf-probe.html
@@ -16,15 +16,15 @@
verdeckt. Gefunden hat es pruef-struktur.
Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. -->
-
-
+
+
-
+