diff --git a/server/pruef-support-bilder.mjs b/server/pruef-support-bilder.mjs
index d52bc5f5..3ad51714 100644
--- a/server/pruef-support-bilder.mjs
+++ b/server/pruef-support-bilder.mjs
@@ -656,6 +656,158 @@ melde("\n=== Eine Farbe je Runde ===");
await h.kontext.close();
}
+/* =====================================================================
+ 7. SIND DIE TEXTE IN DER KARTE ZU LESEN? (03.10.2026)
+ =====================================================================
+
+ Filipe: „nur die kacheln noch viel geiler. so dass ich die texte
+ auch besser erkenne."
+
+ GEMESSEN, BEVOR ETWAS ANGEFASST WURDE -- und die naheliegende
+ Vermutung war falsch:
+
+ Kontrast aller zehn Textarten 7,5 bis 15,7 : 1
+ ZEICHEN JE ZEILE 147
+
+ Am Kontrast lag es nicht. Es lag an der Zeilenlaenge: Auf 1280 px
+ lief der Meldungstext ueber 1120 px, also knapp 150 Zeichen.
+ Angenehm sind 45 bis 75; darueber findet das Auge beim
+ Zeilenwechsel die naechste Zeile nicht mehr sicher.
+
+ ZWEI ZAHLEN WERDEN DESHALB GEPRUEFT, und beide mussten vorher
+ gemessen werden, um sie ueberhaupt zu kennen. Eine Regel, die aus
+ einer Messung kommt, ist etwas anderes als eine, die man sich
+ ausdenkt.
+ ===================================================================== */
+melde("\n=== Sind die Texte zu lesen? ===");
+{
+ const { kontext, seite } = await neueSeite(1280, keksModi);
+ await seite.locator(".s-karte__text").first().waitFor({ timeout: 8000 });
+
+ const lesbar = await seite.evaluate(() => {
+ /* ==== ZWEI SCHREIBWEISEN, EINE EINHEIT ======================
+
+ `rgb()` zaehlt 0..255, `color(srgb …)` zaehlt 0..1 -- und
+ genau das liefert Chrome fuer jedes `color-mix()`. Beim Bauen
+ dieser Messung hat mich das getaeuscht: 0,77 als 0,77/255
+ gelesen ergibt fast Schwarz, und drei gut lesbare Texte
+ standen mit Kontrast 1,04 da. Fast haette ich Farben
+ repariert, die in Ordnung sind. */
+ const zahl = (t) => {
+ const n = (t.match(/[\d.]+/g) || []).map(Number);
+ if (/^color\(/.test(t)) {
+ return [n[0] * 255, n[1] * 255, n[2] * 255, n.length > 3 ? n[3] : 1];
+ }
+ return n;
+ };
+ const kanal = (c) => {
+ const x = c / 255;
+ return x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
+ };
+ const hell = ([r, g, b]) => 0.2126 * kanal(r) + 0.7152 * kanal(g) + 0.0722 * kanal(b);
+ const mischen = (v, h, a) => v.map((x, i) => x * a + h[i] * (1 - a));
+ /* DER GRUND WIRD NACH OBEN GESUCHT, bis eine undurchsichtige
+ Farbe kommt -- halbdurchsichtige Kaesten uebereinander sind
+ sonst nicht zu rechnen. */
+ const grundVon = (e) => {
+ let n = e; const stapel = [];
+ while (n && n !== document.documentElement) {
+ const bg = zahl(getComputedStyle(n).backgroundColor);
+ const a = bg.length === 4 ? bg[3] : 1;
+ if (a > 0) stapel.push([bg.slice(0, 3), a]);
+ if (a === 1) break;
+ n = n.parentElement;
+ }
+ let farbe = [11, 15, 22];
+ for (let i = stapel.length - 1; i >= 0; i--) {
+ farbe = mischen(stapel[i][0], farbe, stapel[i][1]);
+ }
+ return farbe;
+ };
+ const kontrast = (e) => {
+ const vg = zahl(getComputedStyle(e).color);
+ const a = vg.length === 4 ? vg[3] : 1;
+ const grund = grundVon(e);
+ const vorne = mischen(vg.slice(0, 3), grund, a);
+ const l1 = hell(vorne), l2 = hell(grund);
+ const [h, d] = l1 > l2 ? [l1, l2] : [l2, l1];
+ return (h + 0.05) / (d + 0.05);
+ };
+
+ const arten = [".s-karte__text", ".s-marke", ".s-karte__zeit", ".s-runde__nr",
+ ".s-runde__von", ".s-runde__zeit", ".s-schritt__schild", ".s-schritt__text",
+ ".s-rundenleiste__punkt"];
+ const werte = [];
+ for (const w of arten) {
+ const e = document.querySelector(w);
+ if (!e) continue;
+ const st = getComputedStyle(e);
+ const px = parseFloat(st.fontSize);
+ werte.push({
+ w, px: Math.round(px * 100) / 100,
+ k: Math.round(kontrast(e) * 100) / 100,
+ zeichen: Math.round(e.getBoundingClientRect().width / (px * 0.5)),
+ gross: px >= 24 || (px >= 18.66 && Number(st.fontWeight) >= 700),
+ });
+ }
+ /* GEGENPROBE IM BROWSER: Ein absichtlich zu blasser Text MUSS
+ auffallen. Ohne sie waere „alle ueber 4,5" auch dann gruen,
+ wenn die Rechnung kaputt ist -- und genau das war sie. */
+ const probe = document.createElement("p");
+ probe.textContent = "Probe";
+ probe.style.color = "#17202c";
+ document.querySelector(".s-karte").append(probe);
+ const blass = Math.round(kontrast(probe) * 100) / 100;
+ probe.remove();
+
+ return { werte, blass };
+ });
+
+ ok(lesbar.werte.length >= 8,
+ `${lesbar.werte.length} Textarten in der Karte gemessen`);
+
+ const text = lesbar.werte.find((x) => x.w === ".s-karte__text");
+ /* DER MELDUNGSTEXT IST DIE HAUPTSACHE DER SEITE. */
+ ok(text && text.px >= 16,
+ `der Meldungstext ist ${text?.px} px gross (mindestens 16)`);
+ ok(text && text.zeichen <= 85,
+ `und bricht nach etwa ${text?.zeichen} Zeichen um (hoechstens 85, `
+ + "vorher waren es 147)");
+
+ const verlauf = lesbar.werte.find((x) => x.w === ".s-schritt__text");
+ ok(verlauf && verlauf.px >= 15 && verlauf.zeichen <= 85,
+ `der Verlaufstext: ${verlauf?.px} px, etwa ${verlauf?.zeichen} Zeichen`);
+
+ /* UND KEIN TEXT UNTER DER KONTRASTSCHWELLE. */
+ const schwach = lesbar.werte.filter((x) => x.k < (x.gross ? 3 : 4.5));
+ ok(schwach.length === 0,
+ `jeder Text haelt die Kontrastschwelle (${schwach.length} darunter`
+ + (schwach.length ? `: ${schwach.map((x) => `${x.w} ${x.k}`).join(", ")}` : "")
+ + `) — schwaechster ${Math.min(...lesbar.werte.map((x) => x.k))} : 1`);
+ ok(lesbar.blass < 4.5,
+ `Gegenprobe: ein absichtlich blasser Text faellt auf (${lesbar.blass} : 1)`);
+
+ /* UND AUF DEM HANDY: kurze Zeilen sind dort von selbst kurz -- die
+ Frage ist, ob die groessere Schrift die Seite sprengt. */
+ await kontext.close();
+ const h = await neueSeite(390, keksModi);
+ await h.seite.locator(".s-karte__text").first().waitFor({ timeout: 8000 });
+ const handy = await h.seite.evaluate(() => {
+ const e = document.querySelector(".s-karte__text");
+ const px = parseFloat(getComputedStyle(e).fontSize);
+ return {
+ px: Math.round(px * 100) / 100,
+ zeichen: Math.round(e.getBoundingClientRect().width / (px * 0.5)),
+ ueber: document.documentElement.scrollWidth - document.documentElement.clientWidth,
+ };
+ });
+ ok(handy.ueber <= 0,
+ `auf 390 px ragt mit der groesseren Schrift nichts heraus (${handy.ueber} px)`);
+ ok(handy.zeichen >= 25 && handy.zeichen <= 60,
+ `und die Zeile bleibt lesbar lang (etwa ${handy.zeichen} Zeichen)`);
+ await h.kontext.close();
+}
+
await browser.close();
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html
index 40f89b83..0d0d8e79 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`. -->
-
-
+
+
-
+