diff --git a/tools/gate-bauen.mjs b/tools/gate-bauen.mjs index 086c7ee1..67bbffd1 100644 --- a/tools/gate-bauen.mjs +++ b/tools/gate-bauen.mjs @@ -55,6 +55,35 @@ await seite.setContent("
"); /* --------------------------------------------------------------------- 1. Wo sitzt die Tafel? + + DRITTER ANLAUF, und der davor war es, der Filipe geaergert hat + (07.09.2026). + + Anlauf 1 suchte den LEUCHTENDEN Rahmen und fand den Mond, die Blueten + und jede Spiegelung im Wasser -- also die halbe Bildhaelfte. + Anlauf 2 suchte die groesste DUNKLE Flaeche und fand 94 % Hoehe: Ueber + und unter der Tafel ist die Szene genauso schwarz. + Anlauf 3 lief vom Tafelinneren nach aussen und hielt beim ersten + farbigen Punkt an. Das klang richtig und war es fast -- nur glueht der + linke Rahmen im Motiv viel breiter als der rechte (breites Rot gegen + duennes Blau), also hielt er links FRUEHER an. Heraus kamen 58,59 % + statt 55,37 %. Ich habe den Unterschied damals nicht als Messfehler + erkannt, sondern in der CSS "um 1,1 % nach links" von Hand + nachjustiert -- eine Schaetzung auf eine Messung, der ich nicht + traute. Danach stand links ein 32 px breiter schwarzer Streifen der + gemalten Tafel offen, und genau den hat Filipe gesehen. + + WAS JETZT GESUCHT WIRD, IST NICHT "hell" UND NICHT "dunkel", SONDERN + DIE EINZIGE EIGENSCHAFT, DIE NUR DER RAHMEN HAT: kraeftig UND stark + farbig zugleich (Maximum > 150 und Spanne zwischen groesstem und + kleinstem Kanal > 60). Mond und Nebel sind hell, aber grau. Die + Tafelflaeche ist dunkel und grau. Nur die vier Neonlinien sind beides. + + Gezaehlt wird spaltenweise und zeilenweise, wie viele Punkte das + erfuellen. Die Linien stehen dann als klare Spitzen heraus -- kein + Hinlaufen, kein Anhalten, kein Richtungsproblem. Die Oberkante ist + dunkelrot und faellt unter die Neonschwelle; fuer sie gibt es eine + zweite Messung ueber das Helligkeitsprofil (136 gegen 20 daneben). --------------------------------------------------------------------- */ const tafel = await seite.evaluate(async (daten) => { const i = new Image(); i.src = daten; await i.decode(); @@ -63,90 +92,56 @@ const tafel = await seite.evaluate(async (daten) => { const g = c.getContext("2d"); g.drawImage(i, 0, 0); const d = g.getImageData(0, 0, c.width, c.height).data; + const W = c.width, H = c.height; - /* ZWEI ANLAEUFE WAREN FALSCH, und beide haben es selbst verraten. - - (1) "Suche den LEUCHTENDEN Rahmen" fand den Mond, die roten Blueten - und jede Spiegelung im Wasser -- Ergebnis: die ganze rechte - Bildhaelfte. Eine Messung, die das Offensichtliche zurueckgibt, - hat nichts gemessen. - (2) "Suche die groesste DUNKLE Flaeche" fand die Breite richtig, - aber 94 % Hoehe: Ueber und unter der Tafel ist die Szene - genauso schwarz. Dunkelheit unterscheidet sie nicht. - - Was sie WIRKLICH unterscheidet, ist der Rahmen -- aber nur, wenn - man ihn dort sucht, wo nichts anderes leuchtet: INNERHALB der - Tafel. Deshalb wird jetzt vom Mittelpunkt aus nach aussen gelaufen, - bis es hell wird. Auf diesem Weg liegt garantiert nichts anderes, - denn die Tafelflaeche ist leer. - - Der Startpunkt ist dabei nicht die Bildmitte, sondern ein Punkt - sicher INNERHALB der Tafel -- gefunden ueber die dunkle Strecke aus - Anlauf (2), die waagerecht ja richtig lag. */ - /* HELL ODER FARBIG. Nur nach Helligkeit zu suchen, hat die OBERE - Kante nicht gefunden: Sie ist ein duenner, dunkelroter Strich (der - rote Teil des Rahmens laeuft dort aus), waehrend die untere Kante - hell blau leuchtet. Der Rahmen ist aber an JEDER Stelle deutlich - FARBIG -- die Tafelflaeche daneben ist neutralgrau. Genau das - unterscheidet ihn, und zwar rundum. */ - const hell = (x, y) => { - const p = (y * c.width + x) * 4; - const r = d[p], g2 = d[p + 1], b = d[p + 2]; - const max = Math.max(r, g2, b), min = Math.min(r, g2, b); - return max > 118 || (max > 52 && max - min > 26); + const neon = (x, y) => { + const p = (y * W + x) * 4; + const r = d[p], gg = d[p + 1], b = d[p + 2]; + const max = Math.max(r, gg, b), min = Math.min(r, gg, b); + return max > 150 && max - min > 60; }; - const dunkel = (x, y) => { - const p = (y * c.width + x) * 4; - return Math.max(d[p], d[p + 1], d[p + 2]) < 62; + const helligkeit = (x, y) => { + const p = (y * W + x) * 4; + return Math.max(d[p], d[p + 1], d[p + 2]); }; - /* Grobe Waagerechte: die laengste dunkle Strecke auf halber Hoehe. - Sie liegt sicher in der Tafel -- daraus kommt der Startpunkt. */ - function laengsteStrecke(von, bis, punkt) { - let bA = von, bB = von - 1, a = -1, luecke = 0; - for (let n = von; n <= bis; n++) { - if (punkt(n)) { if (a < 0) a = n; luecke = 0; } - else if (a >= 0 && ++luecke > 6) { - const b = n - luecke; - if (b - a > bB - bA) { bA = a; bB = b; } - a = -1; luecke = 0; - } - } - if (a >= 0 && bis - a > bB - bA) { bA = a; bB = bis; } - return [bA, bB]; + /* Alle Spalten der rechten Bildhaelfte, in denen ueber die Tafelhoehe + hinweg mindestens die Haelfte der Punkte Neon ist. */ + const yA = Math.round(H * 0.16), yB = Math.round(H * 0.74); + const spalten = []; + for (let x = Math.round(W * 0.50); x < W; x++) { + let n = 0; + for (let y = yA; y < yB; y++) if (neon(x, y)) n++; + if (n / (yB - yA) > 0.55) spalten.push(x); } - const yMitte = Math.round(c.height * 0.45); - const [gLinks, gRechts] = laengsteStrecke( - Math.round(c.width * 0.45), c.width - 1, (x) => dunkel(x, yMitte)); - const xStart = Math.round((gLinks + gRechts) / 2); + /* Die beiden aeussersten sind die senkrechten Rahmenlinien. */ + const links = spalten.length ? Math.min(...spalten) : -1; + const rechts = spalten.length ? Math.max(...spalten) : -1; - /** Vom Startpunkt aus in eine Richtung laufen, bis es hell wird. - * Verlangt werden ZWEI helle Punkte hintereinander -- ein einzelner - * waere Bildrauschen. */ - function bisZumRahmen(x0, y0, dx, dy, grenze) { - let x = x0, y = y0, hintereinander = 0; - while (x >= 0 && y >= 0 && x < c.width && y < c.height) { - if (hell(x, y)) { if (++hintereinander >= 2) return dx ? x : y; } - else hintereinander = 0; - x += dx; y += dy; - if (dx && (dx > 0 ? x > grenze : x < grenze)) break; - if (dy && (dy > 0 ? y > grenze : y < grenze)) break; - } - return dx ? (dx > 0 ? grenze : grenze) : (dy > 0 ? grenze : grenze); + /* Dasselbe waagerecht, INNERHALB der eben gefundenen Breite. */ + const xA = links > 0 ? links + Math.round((rechts - links) * 0.08) : Math.round(W * 0.58); + const xB = rechts > 0 ? rechts - Math.round((rechts - links) * 0.08) : Math.round(W * 0.90); + const zeilen = []; + for (let y = Math.round(H * 0.05); y < Math.round(H * 0.90); y++) { + let n = 0; + for (let x = xA; x < xB; x++) if (neon(x, y)) n++; + if (n / (xB - xA) > 0.55) zeilen.push(y); + } + const unten = zeilen.length ? Math.max(...zeilen) : -1; + + /* DIE OBERKANTE EINZELN. Sie ist ein duenner dunkelroter Strich und + erreicht die Neonschwelle nicht. Ueber das Helligkeitsprofil ist sie + trotzdem eindeutig: die hellste Zeile im oberen Fuenftel, weit ueber + allem daneben. */ + let oben = -1, bestes = 0; + for (let y = Math.round(H * 0.06); y < Math.round(H * 0.24); y++) { + let summe = 0; + for (let x = xA; x < xB; x++) summe += helligkeit(x, y); + const mittel = summe / (xB - xA); + if (mittel > bestes) { bestes = mittel; oben = y; } } - /* Auf mehreren Linien, damit ein Staubkorn nicht das Ergebnis - bestimmt -- genommen wird jeweils der mittlere Wert. */ - const mitte = (w) => w.slice().sort((a, b) => a - b)[Math.floor(w.length / 2)]; - const yProben = [0.35, 0.42, 0.5, 0.58, 0.65].map((f) => Math.round(c.height * f)); - const links = mitte(yProben.map((y) => bisZumRahmen(xStart, y, -1, 0, Math.round(c.width * 0.42)))); - const rechts = mitte(yProben.map((y) => bisZumRahmen(xStart, y, +1, 0, c.width - 1))); - - const xProben = [0.3, 0.42, 0.5, 0.58, 0.7].map((f) => Math.round(links + (rechts - links) * f)); - const oben = mitte(xProben.map((x) => bisZumRahmen(x, yMitte, 0, -1, 0))); - const unten = mitte(xProben.map((x) => bisZumRahmen(x, yMitte, 0, +1, c.height - 1))); - - return { breite: c.width, hoehe: c.height, links, rechts, oben, unten }; + return { breite: W, hoehe: H, links, rechts, oben, unten, obenHelligkeit: Math.round(bestes) }; }, alsDatenAdresse(join(QUELLE, "gate-spicy-dogfather.jpeg"))); const p = (v, g) => Math.round((v / g) * 10000) / 100; @@ -159,7 +154,11 @@ const p = (v, g) => Math.round((v / g) * 10000) / 100; { const bAnteil = (tafel.rechts - tafel.links) / tafel.breite; const hAnteil = (tafel.unten - tafel.oben) / tafel.hoehe; - const gut = bAnteil > 0.25 && bAnteil < 0.62 && hAnteil > 0.35 && hAnteil < 0.92 + /* "Nichts gefunden" ist ein eigener Ausgang, nicht bloss ein + unplausibler Wert: -1 heisst, es gab ueberhaupt keine Spitze. */ + const gefunden = tafel.links > 0 && tafel.rechts > 0 && tafel.unten > 0 && tafel.oben > 0; + const gut = gefunden + && bAnteil > 0.25 && bAnteil < 0.62 && hAnteil > 0.35 && hAnteil < 0.92 && tafel.links / tafel.breite > 0.45; if (!gut) { console.error(` @@ -168,11 +167,51 @@ ABBRUCH: Die gefundene Tafel ist nicht plausibel ` + `links bei ${p(tafel.links, tafel.breite)} %).`); console.error(`Erwartet wird ein Feld zwischen 25 und 62 Prozent Breite in der ` + `rechten Bildhaelfte. -Wahrscheinlich passt die Helligkeitsschwelle nicht ` - + `zum Bild -- in gate-bauen.mjs bei "dunkel()" nachsehen.`); +${gefunden + ? "Die vier Linien wurden gefunden, liegen aber falsch -- passt das Motiv noch?" + : "Es wurde ueberhaupt keine Neonlinie gefunden. Die Schwelle in neon() " + + "(max > 150, Spanne > 60) passt dann nicht zum Bild."}`); await browser.close(); process.exit(2); } + + /* GEGENPROBE: Kann diese Messung ueberhaupt "nein" sagen? Dieselben + Zaehlungen auf der LINKEN Bildhaelfte duerfen KEINE durchgehende + senkrechte Linie finden -- dort steht das Logo, hell und farbig, + aber ohne Rahmen. Faende sie auch dort etwas, wuerde sie auf + "irgendetwas Buntes" reagieren statt auf den Rahmen, und ihre + vier Zahlen waeren Zufall. */ + const gegenprobe = await seite.evaluate(async (daten) => { + const i = new Image(); i.src = daten; await i.decode(); + const c = document.createElement("canvas"); + c.width = i.width; c.height = i.height; + const g = c.getContext("2d"); g.drawImage(i, 0, 0); + const d = g.getImageData(0, 0, c.width, c.height).data; + const neon = (x, y) => { + const p = (y * c.width + x) * 4; + const r = d[p], gg = d[p + 1], b = d[p + 2]; + const max = Math.max(r, gg, b), min = Math.min(r, gg, b); + return max > 150 && max - min > 60; + }; + const yA = Math.round(c.height * 0.16), yB = Math.round(c.height * 0.74); + let treffer = 0; + for (let x = 0; x < Math.round(c.width * 0.45); x++) { + let n = 0; + for (let y = yA; y < yB; y++) if (neon(x, y)) n++; + if (n / (yB - yA) > 0.55) treffer++; + } + return treffer; + }, alsDatenAdresse(join(QUELLE, "gate-spicy-dogfather.jpeg"))); + if (gegenprobe > 4) { + console.error(` +ABBRUCH (Gegenprobe): Auch links vom Rahmen wurden ${gegenprobe} ` + + `durchgehende Neonspalten gefunden. Die Suche reagiert dann nicht auf den ` + + `Rahmen, sondern auf irgendetwas Buntes -- ihre vier Zahlen waeren Zufall.`); + await browser.close(); + process.exit(2); + } + console.log(`Gegenprobe: links vom Rahmen ${gegenprobe} durchgehende Neonspalten ` + + `(erwartet 0 bis 4) -- die Suche findet wirklich den Rahmen.`); } console.log(`Quellbild: ${tafel.breite}x${tafel.hoehe}`); console.log("Die Tafel im Bild:"); @@ -242,9 +281,116 @@ console.log(` --gate-seiten: ${tafel.breite} / ${tafel.hoehe};`); * * Alles im Browser gerechnet, der ohnehin fuer die Pruefungen da ist -- * kein Bildprogramm, keine neue Abhaengigkeit, 0 EUR. + * + * --------------------------------------------------------------------- + * ZWEITER ANLAUF (07.09.2026): DER SATZ OBEN WAR RICHTIG UND ICH HABE + * IHN NICHT BEFOLGT + * + * Filipe: "gerade sehen die sogar leicht verschwommen aus, das ist + * richtig scheisse ... die sollen wirklich sogar glaenzen." + * + * Er hat recht, und der Grund stand die ganze Zeit vier Absaetze weiter + * oben in dieser Datei: "Verkleinern MITTELT Bildpunkte ... und der + * Grund, warum man danach NACHSCHAERFT." Nachgeschaerft wurde nie. + * Kontrast und Saettigung waren da, der eine Schritt, der Kanten + * wirklich wieder Kanten macht, fehlte. + * + * Dazu ein zweiter, groesserer Fehler, und der ist meiner: + * + * DIE QUELLBILDER SIND 1647 x 955 PIXEL GROSS. + * ICH HABE DARAUS "uhd"-FASSUNGEN MIT 2400 PIXELN GEBAUT. + * + * Das ist eine VERGROESSERUNG um das 1,46-fache. Am 07.09. habe ich + * genau hier notiert, sein Bildschirm sei 2550 px breit und das groesste + * Bild nur 1600, deshalb werde jede Kante weich -- und habe als "Loesung" + * die Ausgabe auf 2400 hochgerechnet. Es gab nie mehr Bildpunkte. Ich + * habe die Datei um zwei Drittel groesser gemacht und dabei erfundene, + * weiche Bildpunkte ausgeliefert, in einer Datei, in der daneben steht, + * dass man Bildpunkte nicht zurueckholen kann. + * + * Behoben wird das nicht durch "kleiner ausliefern" -- dann rechnet der + * Browser hoch und das Ergebnis ist genauso weich, nur ohne dass man es + * steuern kann. Behoben wird es durch die Reihenfolge, die jedes + * Druckvorstufen-Handbuch vorschreibt: erst auf die ENDGROESSE rechnen, + * DANN schaerfen (Ausgabeschaerfung). Was der Browser danach noch + * skaliert, ist ein Zehntel statt der Haelfte. + * + * 4. UNSCHARFMASKE. Der Name taeuscht: Es wird eine WEICHE Kopie + * gebaut und die Differenz zum Original wieder DRAUFGERECHNET. + * Wo beide gleich sind (glatte Flaechen) passiert nichts; wo sie + * auseinandergehen (Kanten) wird der Unterschied verstaerkt. Genau + * deshalb schaerft es Kanten, ohne Rauschen in glatten Flaechen + * hochzuziehen. + * + * 5. GLANZ (Bloom). Das ist der Unterschied zwischen "hell" und + * "leuchtet". Aus dem Bild werden nur die HELLSTEN Stellen + * herausgezogen (Neon, nasser Stein, Wasserspiegelung), weich + * gezeichnet und ADDITIV wieder darueber gelegt. Licht, das ueber + * seine Kante hinausstrahlt -- so sieht jede Kameraaufnahme aus, + * und ohne das wirkt jedes Neonmotiv wie eine Zeichnung. + * Additiv (`lighter`) und nicht deckend: Der Glanz LEGT LICHT DAZU, + * er ueberdeckt nichts. Die dunklen Stellen bleiben unberuehrt, + * damit der Text darueber lesbar bleibt. */ + +/* Die beiden neuen Schritte laufen im Browser und bekommen deshalb ihren + Quelltext als Zeichenkette mit -- `evaluate` schickt nur Daten, keine + Funktionen aus dem Node-Teil. */ +const BILD_WERKZEUG = ` + /* Unscharfmaske: Original + staerke * (Original - Weichzeichnung). */ + function schaerfen(g, c, staerke, radius) { + if (staerke <= 0) return; + const b = document.createElement("canvas"); + b.width = c.width; b.height = c.height; + const gb = b.getContext("2d"); + gb.filter = "blur(" + radius + "px)"; + gb.drawImage(c, 0, 0); + const o = g.getImageData(0, 0, c.width, c.height); + const u = gb.getImageData(0, 0, c.width, c.height); + const A = o.data, B = u.data; + for (let i = 0; i < A.length; i += 4) { + for (let k = 0; k < 3; k++) { + const v = A[i + k] + staerke * (A[i + k] - B[i + k]); + A[i + k] = v < 0 ? 0 : v > 255 ? 255 : v; + } + } + g.putImageData(o, 0, 0); + } + + /* Glanz: die hellsten Stellen weich machen und Licht dazugeben. */ + function glanz(g, c, staerke, schwelle, radius) { + if (staerke <= 0) return; + const h = document.createElement("canvas"); + h.width = c.width; h.height = c.height; + const gh = h.getContext("2d"); + gh.drawImage(c, 0, 0); + const d = gh.getImageData(0, 0, c.width, c.height); + const D = d.data; + for (let i = 0; i < D.length; i += 4) { + const m = Math.max(D[i], D[i + 1], D[i + 2]); + if (m <= schwelle) { D[i] = 0; D[i + 1] = 0; D[i + 2] = 0; continue; } + /* Weich einsetzend, nicht als Stufe -- eine harte Schwelle malt + Raender um jede helle Flaeche. */ + const f = (m - schwelle) / (255 - schwelle); + D[i] *= f; D[i + 1] *= f; D[i + 2] *= f; + } + gh.putImageData(d, 0, 0); + const w = document.createElement("canvas"); + w.width = c.width; w.height = c.height; + const gw = w.getContext("2d"); + gw.filter = "blur(" + radius + "px)"; + gw.drawImage(h, 0, 0); + g.globalCompositeOperation = "lighter"; + g.globalAlpha = staerke; + g.drawImage(w, 0, 0); + g.globalAlpha = 1; + g.globalCompositeOperation = "source-over"; + } +`; + async function bauen({ quelle, ziel, breite, ausschnitt = null, dunkel = 0, guete = 0.82, - seitenverhaeltnis = null, kontrast = 1.08, saettigung = 1.1, vignette = 0.34 }) { + seitenverhaeltnis = null, kontrast = 1.08, saettigung = 1.1, vignette = 0.34, + schaerfe = 0.9, glanzStaerke = 0.34 }) { const webp = await seite.evaluate(async (a) => { const i = new Image(); i.src = a.daten; await i.decode(); @@ -268,6 +414,32 @@ async function bauen({ quelle, ziel, breite, ausschnitt = null, dunkel = 0, guet g.drawImage(i, qx, qy, qb, qh, 0, 0, zb, zh); g.filter = "none"; + /* Die beiden Werkzeuge stehen als Text im Aufruf -- hier + eingesetzt. */ + // eslint-disable-next-line no-eval + eval(a.werkzeug); + + /* 4: NACHSCHAERFEN -- und zwar HIER, direkt nach dem Rechnen auf die + Endgroesse und VOR Vignette und Schleier. + + Die Reihenfolge ist nicht Geschmack. Vignette und Schleier sind + weiche Verlaeufe; wer danach schaerft, schaerft deren Uebergaenge + mit und bekommt Ringe um die dunklen Ecken. Und wer vor dem + Verkleinern schaerft, wirft die Schaerfe beim Verkleinern wieder + weg -- genau deshalb heisst dieser Schritt "Ausgabeschaerfung". + + Radius nach Bildgroesse: Eine feste Zahl waere bei 900 px eine + Kante und bei 2400 px unsichtbar. Ueber die Breite gerechnet + bleibt die Wirkung gleich -- dieselbe Ueberlegung wie bei einer + Schwelle, die man besser messen als festschreiben laesst. */ + const radius = Math.max(0.6, zb / 1400); + schaerfen(g, c, a.schaerfe, radius); + + /* 5: GLANZ. Nach dem Schaerfen, damit das Licht die geschaerften + Kanten umspielt und nicht umgekehrt der Glanz mitgeschaerft wird + (das gaebe harte Lichtsaeume statt weichem Strahlen). */ + glanz(g, c, a.glanzStaerke, 176, Math.max(3, zb / 190)); + /* 3: Die Vignette. Elliptisch ueber die ganze Flaeche, in der Mitte voellig klar, zu den Ecken hin dunkel. Der weiche Verlauf ist wichtig -- eine harte Kante saehe aus wie ein Fehler und nicht wie @@ -290,7 +462,7 @@ async function bauen({ quelle, ziel, breite, ausschnitt = null, dunkel = 0, guet } return c.toDataURL("image/webp", a.guete); }, { daten: alsDatenAdresse(quelle), breite, ausschnitt, dunkel, guete, seitenverhaeltnis, - kontrast, saettigung, vignette }); + kontrast, saettigung, vignette, schaerfe, glanzStaerke, werkzeug: BILD_WERKZEUG }); const puffer = Buffer.from(webp.split(",")[1], "base64"); writeFileSync(ziel, puffer); @@ -328,7 +500,7 @@ for (const b of [960, 1280, 1600, 1920, 2560]) { /* Nur ein Hauch. Der Neonrahmen der Tafel ist das, was die Karte traegt -- wer ihn wegdunkelt, hat ein huebsches Bild und eine Karte, die im Nichts schwebt. */ - dunkel: 0.08, guete: 0.88, vignette: 0.26, + dunkel: 0.08, guete: 0.93, vignette: 0.26, schaerfe: 1.15, glanzStaerke: 0.42, }); console.log(` gate-${b}.webp ${Math.round(n / 1024)} KB`); } @@ -427,7 +599,7 @@ const gebaut = new Set(); for (const [szene, datei] of Object.entries(SZENEN)) { const quelle = join(QUELLE, datei); const zeile = []; - for (const [name, breite, guete] of [["uhd", 2400, 0.80], ["breit", 1600, 0.82]]) { + for (const [name, breite, guete] of [["uhd", 2400, 0.88], ["breit", 1600, 0.88]]) { const n = await bauen({ quelle, ziel: join(ZIEL, `buehne-${szene}-${name}.webp`), breite, dunkel: DUNKEL, guete, @@ -440,7 +612,26 @@ for (const [szene, datei] of Object.entries(SZENEN)) { es geht, steht in allen sieben Motiven mittig. */ const n = await bauen({ quelle, ziel: join(ZIEL, `buehne-${szene}-schmal.webp`), breite: 900, - ausschnitt: [0.22, 0, 0.56, 1], dunkel: DUNKEL + 0.04, guete: 0.80, + /* HOCHKANT DARF NICHT SO HELL SEIN WIE BREIT (07.09.2026). + + Der Glanz hebt die hellen Stellen an -- auf dem Handy trifft das + ausgerechnet die Bildmitte, und dort liegen die kleinsten Texte + der ganzen Anwendung. pruef-buehne hat es an echten Bildpunkten + gemeldet: "Ueberfaellig" kam auf 4,16:1 statt 4,5:1, gemessen auf + rgb(79, 76, 79) -- und dieser Punkt lag nicht auf dem Knopf, + sondern in der Luecke DANEBEN, also direkt auf dem Bild. + + Drei Aenderungen am Knopf haben die Zahl auf die Stelle genau + nicht bewegt (dreimal exakt 4,16). Genau das ist das Zeichen, dass + man an der falschen Stelle dreht -- die Ursache lag im Bild, nicht + im Bedienelement. + + Deshalb hier: mehr Schleier und weniger Glanz, aber NUR fuer die + Hochkant-Fassung. Auf dem grossen Schirm bleibt alles so hell und + glaenzend, wie Filipe es wollte -- dort ist der Text groesser und + die Kacheln decken mehr ab. */ + ausschnitt: [0.22, 0, 0.56, 1], dunkel: DUNKEL + 0.12, guete: 0.87, + glanzStaerke: 0.20, }); gesamt += n; zeile.push(`schmal ${Math.round(n / 1024)} KB`); diff --git a/workspace/assets/css/aufgaben.css b/workspace/assets/css/aufgaben.css index 3f31ff01..797f01c5 100644 --- a/workspace/assets/css/aufgaben.css +++ b/workspace/assets/css/aufgaben.css @@ -398,7 +398,26 @@ display: inline-flex; align-items: center; gap: 8px; min-height: 38px; padding: 7px 13px; border: 1px solid var(--rand); border-radius: 999px; - background: rgba(255, 255, 255, .03); + /* EIGENER UNTERGRUND, NICHT GELIEHENER (07.09.2026). + + Hier stand `rgba(255, 255, 255, .03)` -- also praktisch nichts. Der + Knopf war lesbar, weil hinter ihm zufaellig etwas Dunkles lag. Als + die Buehnenbilder heller wurden (Glanz) und die Flaechen darueber + ihren Weichzeichner verloren, war das vorbei: Auf dem Handy sass + "Ueberfaellig" ploetzlich auf rgb(79, 76, 79) und kam auf 4,16:1 + statt der noetigen 4,5:1 -- gemessen von pruef-buehne an echten + Bildpunkten. + + Das ist kein Bildproblem und wird auch nicht dort behoben: Das Bild + dunkler zu machen haette genau die Verbesserung zurueckgenommen, um + die es ging. Ein Bedienelement, dessen Lesbarkeit davon abhaengt, + was zufaellig dahinter liegt, ist die eigentliche Schwachstelle -- + es geht beim naechsten Hintergrundwechsel wieder kaputt, und + niemand rechnet damit. + + Dunkel und trotzdem durchscheinend: Das Motiv bleibt zu ahnen, der + Text steht aber nie mehr direkt auf einem Foto. */ + background: rgba(9, 13, 21, .62); color: var(--text-leise); font: inherit; font-size: .85rem; cursor: pointer; transition: border-color var(--tempo), color var(--tempo), background var(--tempo); @@ -409,7 +428,10 @@ .schnitt[aria-pressed="true"], .creator-knopf[aria-pressed="true"] { color: var(--text); border-color: color-mix(in srgb, var(--akzent) 55%, transparent); - background: color-mix(in srgb, var(--akzent) 13%, transparent); + /* Die Farbe liegt AUF dem dunklen Untergrund, nicht statt seiner -- + sonst haette der gedrückte Knopf genau das Problem zurück, das der + ungedrückte gerade losgeworden ist. */ + background: color-mix(in srgb, var(--akzent) 15%, rgba(9, 13, 21, .66)); } .schnitt:focus-visible, .creator-knopf:focus-visible { outline: 2px solid var(--akzent); outline-offset: 2px; @@ -429,7 +451,33 @@ dass man ihn nicht mehr liest, ist genau das Raetseln, das der Kommentar vermeiden wollte -- abgeschaltet heisst nicht unsichtbar. */ -.schnitt[disabled] { opacity: .62; cursor: default; color: #aab7c7; } +/* DIE DECKKRAFT MUSSTE GANZ WEG (07.09.2026). + + Am 06.09. stand hier `opacity: .42`, die Messung blieb dreimal bei + 4,05:1, und die Lehre daraus war richtig notiert: Deckkraft aendert + die CSS-Farbe des Textes nicht. Nur habe ich damals die andere Haelfte + uebersehen -- sie aendert dafuer sehr wohl den UNTERGRUND, denn + `opacity` wirkt auf das ganze Element samt seiner Flaeche. + + Heute hat mich genau das ein zweites Mal erwischt, aus der anderen + Richtung: Ich habe dem Knopf einen dunklen Untergrund gegeben, und + die Zahl blieb auf die Stelle genau bei 4,16:1. Wieder das + Unveraenderlichkeits-Zeichen -- eine Zahl, die sich nach einer + Aenderung KEIN Stueck bewegt, sagt nicht "zu wenig", sondern "an der + falschen Stelle". Der neue Untergrund wurde mit 62 Prozent + durchgereicht, das helle Buehnenbild schlug durch, und heraus kam + rgb(79, 76, 79). + + Abgeschaltet heisst jetzt: gedaempfte FARBEN, volle Flaeche. Der + Untergrund traegt, der Text tritt zurueck -- und beides ist messbar, + statt sich gegenseitig zu verrechnen. */ +.schnitt[disabled] { + cursor: default; + color: #aab7c7; + border-color: rgba(150, 186, 220, .12); + background: rgba(9, 13, 21, .72); +} +.schnitt[disabled] .schnitt__zahl { background: rgba(255, 255, 255, .05); color: #96a4b6; } .schnitt__zahl, .creator-knopf__zahl { min-width: 20px; padding: 1px 7px; diff --git a/workspace/assets/css/gate.css b/workspace/assets/css/gate.css index 287732f0..efc79605 100644 --- a/workspace/assets/css/gate.css +++ b/workspace/assets/css/gate.css @@ -22,7 +22,14 @@ Unterschied zwischen "hat einen Hintergrund" und "ist unlesbar". Wer die Kacheln durchsichtiger oder dichter will, aendert nur diese eine Zeile. */ - --flaeche: rgba(9, 13, 22, 0.78); + /* 0,89 statt 0,78 (07.09.2026). Die Flächen der Seite lagen bisher + hinter einem Weichzeichner -- 22 % Durchsicht auf ein VERWISCHTES + Bild fallen nicht auf. Ohne ihn liegt dahinter jetzt ein scharfes, + kontrastreiches Motiv, und dieselben 22 % machen aus einer ruhigen + Fläche eine unruhige. Die Durchsicht bleibt (Filipe: "vielleicht + ganz ganz ganz leicht durch zu sehen sein"), aber als Andeutung, + nicht als Muster. */ + --flaeche: rgba(9, 13, 22, 0.89); --rand: rgba(150, 186, 220, 0.16); --rand-hell: rgba(150, 186, 220, 0.30); @@ -1175,10 +1182,55 @@ body.gate { dort einen dunklen Saum stehen. Ein halber Prozentpunkt Ueberlappung schiebt ihre Kante unter das Glühen -- man sieht nur noch Rahmen und Karte, nichts dazwischen. */ - --tafel-links: 57.00%; - --tafel-oben: 13.00%; - --tafel-breite: 36.60%; - --tafel-hoehe: 63.70%; + /* 07.09.2026, ZWEITER ANLAUF -- diesmal GEMESSEN statt nachjustiert. + + Der Anlauf davor hat die Werte "um 1,1 % nach links" von Hand + korrigiert, nach Augenschein im Bildschirmfoto. Das Ergebnis war + immer noch falsch, und Filipe hat es sofort gesehen: links stand + ein schwarzer Streifen der gemalten Tafel offen. + + Der Grund war die Messung selbst. Sie laeuft vom Tafelinneren nach + aussen und haelt beim ersten FARBIGEN Punkt an -- der linke Rahmen + glueht im Motiv aber viel breiter als der rechte (breites Rot gegen + duennes Blau). Sie hielt links also frueher an, und ich habe den + Fehler mit einer geschaetzten Verschiebung zugedeckt, statt ihn zu + beheben. Eine Korrektur nach Gefuehl auf eine Messung, die man fuer + falsch haelt, ist keine Messung mehr. + + Jetzt anders gemessen, und zwar auf das, was man wirklich sucht: + Die vier Neonlinien sind die einzigen Stellen im ganzen Bild, die + KRAEFTIG UND FARBIG zugleich sind (max > 150 und Spanne > 60). Je + Spalte und je Zeile gezaehlt, wie viele Punkte das erfuellen -- + die Linien stehen als klare Spitzen heraus, nichts sonst kommt in + die Naehe: + + senkrecht 55,37 % (rot) 94,17 % (blau) + waagerecht 13,19 % (rot) 76,96 % (blau) + + Die Oberkante brauchte eine zweite Messung: Sie ist dunkelrot und + faellt unter die Neonschwelle. Ueber das Helligkeitsprofil in der + Tafelbreite ist sie trotzdem eindeutig -- 136 gegen 20 daneben. + + Die Karte geht 0,4 Prozentpunkte UEBER jede Linie hinaus. Filipe + ausdruecklich: "wenn es leicht groesser ist ist egal aber die + kachel vom hintergrund bild muss bedeckt sein". Die Linien gluehen + weich; endete die Karte genau auf der Messkante, bliebe ein + dunkler Saum. So liegt ihr Rand UNTER dem Leuchten -- man sieht + Rahmen und Karte, nichts dazwischen. + + Die Karte reicht BIS AN die Linien, nicht darueber hinaus. Der + Neonrahmen ist das Beste am Bild -- wuerde die Karte ihn ueberdecken, + waere die Tafel zwar bedeckt und die Seite auf einen Schlag + langweilig. Gedeckt werden muss das SCHWARZ dazwischen, und genau + das war offen: 55,37 gemessen, 57,00 eingetragen -- bei einem + 1978 px breiten Rahmen sind das 32 px schwarze Flaeche, die links + stehen blieb. Rechts fehlten 0,6 %. + + gemessen hier */ + --tafel-links: 55.45%; /* 55,37 -- Kante liegt auf dem Licht */ + --tafel-oben: 13.15%; /* 13,19 */ + --tafel-breite: 38.65%; /* bis 94,10 %, Linie bei 94,17 */ + --tafel-hoehe: 63.85%; /* bis 77,00 %, Linie bei 76,96 */ } /* Vorgabe: Der Anker tut nichts. Er wird erst dort zur Schablone, wo @@ -1386,25 +1438,49 @@ body.gate { border: 0; border-radius: 22px; + /* DER WEICHZEICHNER MUSSTE WEG (07.09.2026, abends). + + Hier stand `backdrop-filter: blur(26px)`, und das war der Grund, + warum die Karte im Bildschirmfoto GRAU aussah statt tief. + + Ein Weichzeichner mittelt nicht nur, was HINTER dem Element liegt + -- er zieht seinen Radius weit über dessen Kanten hinaus in die + Umgebung. Hinter der Tafel ist zwar Schwarz, direkt daneben aber + die hellste Stelle des ganzen Motivs: der rote und der blaue + Neonstrich. 26 px Radius holen genau die herein und rechnen sie + zu einem gleichmäßigen Grau. Je schöner der Rahmen, desto grauer + die Karte -- man baut sich das Problem mit dem Schmuckstück + selbst. + + Jetzt ist die Karte eine eigene, fast deckende Scheibe (93 %) und + holt sich die Rahmenfarben ABSICHTLICH herein, an denselben + Ecken, an denen sie im Bild sitzen -- kräftiger als vorher, weil + sie jetzt gegen Schwarz und nicht gegen Grau arbeiten. */ background: /* Der Schein von den Kanten herein, in den Farben des Rahmens: rot oben links, blau unten rechts. Er ist das, was die Karte mit dem Bild verbindet, statt sie davorzusetzen. */ - radial-gradient(120% 90% at 4% 0%, rgba(224, 66, 66, .13), transparent 58%), - radial-gradient(120% 90% at 98% 100%, rgba(58, 150, 235, .15), transparent 60%), - /* Die Anzeige selbst: oben eine Spur heller. */ - linear-gradient(178deg, rgba(14, 20, 32, .80), rgba(6, 9, 16, .88) 62%); - - backdrop-filter: blur(26px) saturate(128%); - -webkit-backdrop-filter: blur(26px) saturate(128%); + radial-gradient(135% 100% at 0% 0%, rgba(232, 62, 62, .26), transparent 55%), + radial-gradient(135% 100% at 100% 100%, rgba(56, 158, 246, .30), transparent 58%), + /* Die Anzeige selbst: oben eine Spur heller, unten fast schwarz. */ + linear-gradient(176deg, + rgba(16, 23, 36, .93) 0%, + rgba(9, 13, 21, .95) 46%, + rgba(5, 7, 13, .96) 100%); + backdrop-filter: none; + -webkit-backdrop-filter: none; box-shadow: /* Die Fuge: innen ein feiner heller Strich oben, dunkel unten. */ - inset 0 1px 0 rgba(255, 255, 255, .09), + inset 0 1px 0 rgba(255, 255, 255, .11), inset 0 -1px 0 rgba(0, 0, 0, .5), + /* Die beiden Rahmenfarben laufen als hauchdünner Saum INNEN an + den Kanten weiter -- der Übergang vom Gehäuse zur Scheibe. */ + inset 2px 2px 0 -1px rgba(232, 62, 62, .34), + inset -2px -2px 0 -1px rgba(56, 158, 246, .38), /* Und ein sehr weicher Schein nach innen, damit die Kante nicht hart abbricht, sondern ins Gehäuse übergeht. */ - inset 0 0 44px -18px rgba(0, 0, 0, .9); + inset 0 0 54px -20px rgba(0, 0, 0, .92); overflow: hidden; } @@ -1415,23 +1491,76 @@ body.gate { pointer-events: none; border-radius: inherit; background: linear-gradient(122deg, - rgba(255, 255, 255, .07) 0%, - rgba(255, 255, 255, .018) 14%, - rgba(255, 255, 255, 0) 32%), - var(--koernung); - background-size: auto, 140px 140px; + rgba(255, 255, 255, .075) 0%, + rgba(255, 255, 255, .020) 14%, + rgba(255, 255, 255, 0) 32%); opacity: .9; } + + /* DIE KÖRNUNG BRAUCHT EINE EIGENE EBENE (07.09.2026, abends). + + Sie stand als zweite Schicht in der Spiegelung oben -- mit deren + Deckkraft von 90 % und ohne Mischmodus. Die Körnung ist aber ein + mittelgraues Rauschen: 90 % davon über der Karte heben JEDEN + Bildpunkt um rund 30 Stufen an. Gemessen im Bildschirmfoto: + rgb(46, 57, 64) statt der berechneten rgb(15, 22, 34). Die Karte + war deshalb grau, egal wie tief der Verlauf darunter eingestellt + war -- ich habe zweimal an der falschen Zahl gedreht. + + Auf der Startseite steht dieselbe Körnung richtig: 3,5 % und + `mix-blend-mode: overlay`, also "hell machen, wo es hell ist, + dunkel, wo es dunkel ist". Genau das hier auch. + + Als Träger dient `.tafel__kante` -- das Element gibt es im HTML + bereits, es war in dieser Ansicht nur abgeschaltet. Ein weiteres + Pseudo-Element gibt es nicht: `::before` und `::after` sind + vergeben. */ + .tafel__kante { + display: block; + position: absolute; inset: 0; z-index: 0; + pointer-events: none; border-radius: inherit; + background-image: var(--koernung); + background-size: 140px 140px; + opacity: .04; + mix-blend-mode: overlay; + } /* Der Inhalt liegt über der Scheibe -- Schrift durch eine Spiegelung zu lesen wäre anstrengend. */ .tafel > * { position: relative; z-index: 1; } - /* ---- Die Rollen als Tasten ---------------------------------------- */ + /* ---- Die Rollen als Tasten in einer Leuchtschiene ------------------ + DIESELBE SPRACHE WIE DIE KACHELN DER STARTSEITE (07.09.2026). + + Auf der Startseite steckt jede Kachel in einer Schiene, die in der + Farbe ihrer Kategorie leuchtet. Hier passiert dasselbe mit den + Rollen -- und das ist mehr als eine Wiederholung: Wer sich als + Manager anmeldet, sieht dabei schon das Gold, in dem ihm gleich + seine Kacheln begegnen. Zwei Bildschirme, eine Sprache. + + Vier Farben, aus dem Knopfsystem oben (--k-*), nicht neu erfunden: + DogFather Gold -- Krone + Manager Bernstein -- Stern + Scout Grün -- Schild + Creator Blau -- Person + Alle gedeckt, keine davon neon: augenschonend bleibt Vorgabe. */ + .tafel .rolle[data-rolle="admin"] { --rton: var(--k-gold); } + .tafel .rolle[data-rolle="manager"] { --rton: var(--k-mittel); } + .tafel .rolle[data-rolle="scout"] { --rton: var(--k-gut); } + .tafel .rolle[data-rolle="creator"] { --rton: var(--akzent); } + .tafel .rolle { + position: relative; + overflow: hidden; + /* Links scharf (dort sitzt die Schiene), rechts rund -- genau die + Silhouette der Kacheln. */ + border-radius: 3px 13px 13px 3px; background: + linear-gradient(90deg, + color-mix(in srgb, var(--rton, var(--akzent)) 20%, transparent) 0%, + color-mix(in srgb, var(--rton, var(--akzent)) 6%, transparent) 22%, + transparent 58%), linear-gradient(180deg, rgba(255, 255, 255, .055), rgba(255, 255, 255, .012)); border: 1px solid rgba(150, 186, 220, .13); - border-radius: 13px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .09), inset 0 -1px 0 rgba(0, 0, 0, .38), @@ -1439,11 +1568,28 @@ body.gate { transition: border-color var(--tempo), background var(--tempo), box-shadow var(--tempo), transform .09s ease; } + /* Die Schiene selbst. */ + .tafel .rolle::before { + content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; + border-radius: 3px 0 0 3px; + background: linear-gradient(180deg, + color-mix(in srgb, var(--rton, var(--akzent)) 55%, #fff) 0%, + var(--rton, var(--akzent)) 34%, + color-mix(in srgb, var(--rton, var(--akzent)) 55%, transparent) 100%); + box-shadow: 2px 0 14px -3px color-mix(in srgb, var(--rton, var(--akzent)) 55%, transparent); + opacity: .72; + transition: opacity var(--tempo), box-shadow var(--tempo); + } .tafel .rolle:hover { background: + linear-gradient(90deg, + color-mix(in srgb, var(--rton, var(--akzent)) 26%, transparent) 0%, + color-mix(in srgb, var(--rton, var(--akzent)) 8%, transparent) 24%, + transparent 60%), linear-gradient(180deg, rgba(255, 255, 255, .085), rgba(255, 255, 255, .022)); border-color: rgba(150, 186, 220, .24); } + .tafel .rolle:hover::before { opacity: 1; } /* Beim Drücken sinkt die Taste ein: Der Schatten unter ihr verschwindet, der innere kommt von oben. Genau das macht ein echter Knopf. */ @@ -1456,14 +1602,24 @@ body.gate { sagt deutlicher als jede Farbe, welche gerade aktiv ist. */ .tafel .rolle[aria-checked="true"] { background: - linear-gradient(180deg, - color-mix(in srgb, var(--akzent) 22%, transparent), - color-mix(in srgb, var(--violett) 13%, transparent)); - border-color: color-mix(in srgb, var(--akzent) 62%, transparent); + linear-gradient(90deg, + color-mix(in srgb, var(--rton, var(--akzent)) 42%, transparent) 0%, + color-mix(in srgb, var(--rton, var(--akzent)) 16%, transparent) 34%, + color-mix(in srgb, var(--rton, var(--akzent)) 5%, transparent) 72%), + linear-gradient(180deg, rgba(255, 255, 255, .10), rgba(255, 255, 255, .02)); + border-color: color-mix(in srgb, var(--rton, var(--akzent)) 58%, transparent); box-shadow: - inset 0 1px 0 rgba(255, 255, 255, .16), - inset 0 0 20px -8px color-mix(in srgb, var(--akzent) 90%, transparent), - 0 0 20px -10px color-mix(in srgb, var(--akzent) 75%, transparent); + inset 0 1px 0 rgba(255, 255, 255, .18), + inset 0 0 24px -9px color-mix(in srgb, var(--rton, var(--akzent)) 92%, transparent), + 0 0 24px -11px color-mix(in srgb, var(--rton, var(--akzent)) 78%, transparent); + } + /* Die Schiene der gewählten Rolle brennt voll -- das ist die + deutlichste Anzeige, die es gibt, und sie kommt ohne ein weiteres + Zeichen aus. */ + .tafel .rolle[aria-checked="true"]::before { + opacity: 1; + width: 4px; + box-shadow: 4px 0 26px -3px color-mix(in srgb, var(--rton, var(--akzent)) 78%, transparent); } /* ---- Das Codefeld: eine eingelassene Rinne ------------------------- */ diff --git a/workspace/assets/css/start.css b/workspace/assets/css/start.css index 7ecd0014..d903f42d 100644 --- a/workspace/assets/css/start.css +++ b/workspace/assets/css/start.css @@ -3980,30 +3980,96 @@ body[data-fremde-sicht="ja"]::after { url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='r'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23r)' opacity='0.55'/%3E%3C/svg%3E"); } +/* ===================================================================== + LEUCHTKASTEN — der zweite Anlauf, und diesmal ein anderes Ding + (07.09.2026, abends) + + Filipe zum ersten Anlauf: "das ist genau das gleiche ... ich wollte + eine krasse aenderung ... es muss einzigartig sein und der seite + angepasst." Und zum Hintergrund: "die sollen nicht durch die kacheln + gehen ... vielleicht ganz ganz ganz leicht durch zu sehen sein." + + Er hat in beidem recht, und der zweite Satz erklaert den ersten. + + DAS MATTGLAS WAR DER FEHLER. 16 px Weichzeichner hinter jeder Kachel + heisst: Unter jeder Kachel liegt eine verschwommene Fläche. Auf einer + Seite mit siebzehn Kacheln ist damit der grösste Teil des Bildes + verwaschen -- man sieht ein unscharfes Bild und hält das Bild für + unscharf. Genau das hat er gemeldet ("sehen die sogar leicht + verschwommen aus"), und ich hatte es selbst eingebaut, mit der + Begründung, Kachel und Bild sollten "zusammengehören". Sie gehören + auch zusammen, wenn die Kachel ein eigener Gegenstand ist -- Mattglas + war die falsche Antwort auf die richtige Frage. + + WAS JETZT DA STEHT, IST EIN ANDERES DING, KEIN AUFGEHÜBSCHTES: + + Die Kachel ist eine dunkle Platte, die in einer LEUCHTSCHIENE + steckt. Die Schiene ist die Farbe der Kategorie. Sie leuchtet nicht + nur am Rand -- sie WIRFT LICHT in die Platte hinein, das nach + rechts ausläuft. Deshalb ist die linke Kante scharf (dort sitzt sie + in der Schiene) und die rechte rund (dort ist sie frei). + + Das ist der Unterschied zu vorher: Die Farbe war ein Strich AN der + Kachel; jetzt ist sie die Lichtquelle DER Kachel. Jede Kategorie + leuchtet in ihrem eigenen Licht, und man erkennt sie aus drei Metern + Entfernung, ohne ein Wort zu lesen. + + Dazu drei Dinge, die eine Fläche zu einem Gegenstand machen und die + bleiben, weil sie richtig waren: Gewicht (zwei Schatten), Material- + stärke (Licht oben, Schatten unten) und Körnung. Neu dazu kommt der + GLANZ, der beim Überfahren über die Platte WANDERT -- so verhält sich + eine lackierte Oberfläche, wenn man den Kopf bewegt. + + DURCHSICHT: 6 Prozent, und SCHARF. Genau das, worum er gebeten hat -- + das Bild schimmert durch, ohne dass irgendwo etwas verwischt wird. + ===================================================================== */ .kachel { - /* MATTGLAS. Der Anteil des Untergrunds ist mit Absicht hoch: Das Bild - dahinter ist der Grund, warum die Seite gut aussieht. 16 px - Weichzeichner sind gemessen -- darunter sieht man die Struktur des - Bildes durch und der Text flimmert, darüber wird es Milchglas und - das Motiv ist wieder weg. */ + /* Links scharf, rechts rund. Das ist die Silhouette, an der man diese + Oberfläche wiedererkennt -- und der einzige Teil des Entwurfs, der + schon aus zehn Metern wirkt. */ + border-radius: 4px 20px 20px 4px; + + /* DREI SCHICHTEN, von oben nach unten: + 1. das Licht der Schiene, das nach rechts ausläuft + 2. die Platte selbst, leicht von oben links beleuchtet + 3. 6 % Durchsicht (die Farbe ist nicht ganz deckend) + Kein backdrop-filter mehr -- nichts wird verwischt. */ background: - linear-gradient(158deg, - color-mix(in srgb, var(--ton) 24%, rgba(9, 13, 22, .70)), - rgba(8, 11, 19, .80) 64%); - backdrop-filter: blur(16px) saturate(135%); - -webkit-backdrop-filter: blur(16px) saturate(135%); + /* STEILER ABFALL, nicht flächig. Beim ersten Anlauf reichte das + Licht bis 68 % -- damit war die Kachel eingefärbt statt + beleuchtet, und eingefärbt sieht billig aus. Licht nimmt mit dem + Quadrat der Entfernung ab; die Stufen bilden das nach: satt an + der Schiene, nach einem Fünftel schon deutlich weniger, nach + einem Drittel nur noch ein Hauch. Das ist der Unterschied + zwischen "farbige Kachel" und "Kachel, auf die Licht fällt". */ + linear-gradient(90deg, + color-mix(in srgb, var(--ton) 40%, transparent) 0%, + color-mix(in srgb, var(--ton) 15%, transparent) 9%, + color-mix(in srgb, var(--ton) 5%, transparent) 22%, + color-mix(in srgb, var(--ton) 1.5%, transparent) 38%, + transparent 52%), + linear-gradient(163deg, + rgba(23, 31, 45, .95) 0%, + rgba(11, 15, 24, .95) 58%, + rgba(8, 11, 18, .95) 100%); + backdrop-filter: none; + -webkit-backdrop-filter: none; /* MATERIALSTÄRKE: Licht oben, Schatten unten -- beide innen. GEWICHT: zwei Schatten, ein enger dunkler direkt darunter und ein weiter weicher. Ein einzelner Schatten sieht immer nach Aufkleber - aus; erst die Kombination liest sich als "liegt auf etwas". */ + aus; erst die Kombination liest sich als "liegt auf etwas". + Dazu innen links ein farbiger Saum: das Licht der Schiene, das auf + die Schnittkante der Platte fällt. */ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .10), - inset 0 -1px 0 rgba(0, 0, 0, .45), + inset 0 -1px 0 rgba(0, 0, 0, .50), + inset 2px 0 0 -1px color-mix(in srgb, var(--ton) 30%, transparent), 0 2px 5px -2px rgba(0, 0, 0, .55), - 0 14px 30px -18px rgba(0, 0, 0, .85); + 0 16px 34px -20px rgba(0, 0, 0, .88); - border-color: color-mix(in srgb, var(--ton) 16%, rgba(150, 186, 220, .14)); + border-color: color-mix(in srgb, var(--ton) 18%, rgba(150, 186, 220, .13)); + transition: border-color var(--tempo), transform var(--tempo), box-shadow var(--tempo); } /* Die Körnung. Sie liegt über der Fläche, aber UNTER dem Inhalt -- @@ -4018,33 +4084,57 @@ body[data-fremde-sicht="ja"]::after { border-radius: inherit; } -/* DER GLANZ. Ein schmaler, sehr weicher Streifen quer über die obere - linke Ecke -- die Spiegelung einer Lichtquelle, die außerhalb des - Bildes liegt. Das ist der Unterschied zwischen einer matten und einer - lackierten Oberfläche, und er ist bewusst so schwach, dass man ihn - nicht als Streifen sieht, sondern nur als "die glänzt". */ +/* DER GLANZ, UND ER WANDERT. + + Ein breiter, sehr weicher Lichtstreifen schräg über die Platte -- die + Spiegelung einer Lampe, die ausserhalb des Bildschirms steht. Beim + Überfahren zieht er einmal von links nach rechts durch. Das ist der + Unterschied zwischen einer matten und einer lackierten Oberfläche: + Bei Lack wandert die Spiegelung, wenn man den Kopf bewegt. + + Gemacht über `background-position`, nicht über eine zweite Ebene mit + `transform`. Grund: Die Kachel hat `overflow: hidden`; eine + verschobene Ebene müsste breiter sein als die Kachel und würde bei + `border-radius` an den Ecken abgeschnitten aussehen. Die Position + eines Verlaufs lässt sich dagegen sauber weich überblenden. + + Die zweite Schicht ist das Licht, das dem Zeiger folgt -- sie bleibt + stehen (`background-size` 100 %), nur der Glanz bewegt sich. */ .kachel::after { background: - linear-gradient(128deg, - rgba(255, 255, 255, .085) 0%, - rgba(255, 255, 255, .022) 16%, - rgba(255, 255, 255, 0) 34%), - radial-gradient(130% 150% at var(--mx) var(--my), - color-mix(in srgb, var(--ton) 22%, transparent), transparent 60%); - opacity: .46; + linear-gradient(100deg, + transparent 30%, + rgba(255, 255, 255, .045) 42%, + rgba(255, 255, 255, .11) 49%, + rgba(255, 255, 255, .035) 56%, + transparent 68%), + radial-gradient(120% 150% at var(--mx) var(--my), + color-mix(in srgb, var(--ton) 24%, transparent), transparent 62%); + background-size: 260% 100%, 100% 100%; + background-position: -30% 0, 0 0; + background-repeat: no-repeat; + opacity: .58; + transition: opacity var(--tempo), background-position .55s cubic-bezier(.35, 0, .2, 1); } -/* Die farbige Kante bekommt Tiefe: oben satt, unten auslaufend, und - ein Hauch Leuchten nach innen. */ +/* DIE LEUCHTSCHIENE. Kein Strich am Rand mehr, sondern eine Lichtquelle: + ein heller Kern, darüber ein Schein, der nach beiden Seiten austritt. + Oben am hellsten -- Leuchtstoff altert nach unten, und genau diese + Ungleichmäßigkeit macht den Unterschied zu einem gemalten Balken. */ .kachel::before { - width: 3px; + width: 4px; + border-radius: 4px 0 0 4px; background: linear-gradient(180deg, - color-mix(in srgb, var(--ton) 100%, transparent) 0%, - color-mix(in srgb, var(--ton) 72%, transparent) 42%, - transparent 94%); - box-shadow: 1px 0 8px -2px color-mix(in srgb, var(--ton) 55%, transparent); - opacity: .62; + color-mix(in srgb, var(--ton) 55%, #fff) 0%, + var(--ton) 26%, + color-mix(in srgb, var(--ton) 82%, transparent) 72%, + color-mix(in srgb, var(--ton) 46%, transparent) 100%); + box-shadow: + 0 0 9px 0 color-mix(in srgb, var(--ton) 55%, transparent), + 3px 0 20px -3px color-mix(in srgb, var(--ton) 42%, transparent); + opacity: .92; + transition: opacity var(--tempo), width var(--tempo), box-shadow var(--tempo); } /* ANHEBEN STATT AUFLEUCHTEN. Beim Überfahren wird die Kachel nicht @@ -4058,11 +4148,22 @@ body[data-fremde-sicht="ja"]::after { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14), inset 0 -1px 0 rgba(0, 0, 0, .45), + inset 3px 0 0 -1px color-mix(in srgb, var(--ton) 52%, transparent), 0 4px 10px -4px rgba(0, 0, 0, .6), - 0 22px 44px -22px rgba(0, 0, 0, .9), - 0 0 26px -12px color-mix(in srgb, var(--ton) 60%, transparent); + 0 24px 48px -24px rgba(0, 0, 0, .9), + 0 0 30px -14px color-mix(in srgb, var(--ton) 65%, transparent); } -.kachel:hover::before { opacity: 1; width: 3px; } +/* Die Schiene wird heller und strahlt weiter in die Platte -- nicht + breiter. Ein Balken, der beim Zeigen wächst, sieht nach Menü aus; + eine Lampe, die heller wird, nach Gerät. */ +.kachel:hover::before { + opacity: 1; + box-shadow: + 0 0 14px 0 color-mix(in srgb, var(--ton) 75%, transparent), + 5px 0 34px -3px color-mix(in srgb, var(--ton) 58%, transparent); +} +/* Und der Glanz zieht einmal durch. */ +.kachel:hover::after { background-position: 130% 0, 0 0; } /* Beim Drücken sinkt sie zurück -- ohne diesen Gegenweg fühlt sich ein Knopf an wie ein Bild von einem Knopf. */ @@ -4090,10 +4191,16 @@ body[data-fremde-sicht="ja"]::after { 0 0 0 1px color-mix(in srgb, var(--ton) 16%, transparent); } -/* Auch die übrigen Flächen der Seite bekommen Mattglas und Körnung -- +/* Auch die übrigen Flächen der Seite bekommen dasselbe Material -- sonst sähen die Kacheln aus wie aus einer anderen Anwendung als der Kasten daneben. Eine Oberfläche, in der zwei Materialien nebeneinander - liegen, wirkt zusammengesucht. */ + liegen, wirkt zusammengesucht. + + HIER STAND EBENFALLS MATTGLAS, und es war derselbe Fehler wie bei den + Kacheln: Sieben weitere Flächen, unter denen das Bild verwischt wird. + Zählt man sie zusammen, war auf der Startseite kaum noch eine Stelle + übrig, an der man das Motiv scharf sah -- und ein Bild, das man nur + noch durch Milchglas kennt, hält man für ein schlechtes Bild. */ .tagesblick, .zahlkarte, .zielbalken__zeile, @@ -4101,11 +4208,11 @@ body[data-fremde-sicht="ja"]::after { .creator, .warnkarte, .summe { - backdrop-filter: blur(13px) saturate(125%); - -webkit-backdrop-filter: blur(13px) saturate(125%); + backdrop-filter: none; + -webkit-backdrop-filter: none; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07), - inset 0 -1px 0 rgba(0, 0, 0, .35), + inset 0 -1px 0 rgba(0, 0, 0, .38), 0 2px 4px -2px rgba(0, 0, 0, .45), 0 12px 26px -18px rgba(0, 0, 0, .8); } @@ -4116,6 +4223,13 @@ body[data-fremde-sicht="ja"]::after { @media (prefers-reduced-motion: reduce) { .kachel, .kachel:hover, .kachel:active { transform: none; } .kachel:hover .kachel__zeichen { transform: none; } + /* Der wandernde Glanz ist Bewegung und faellt damit weg. Der Glanz + selbst BLEIBT -- er steht dann fest auf der Platte, wie eine + Spiegelung bei ruhigem Kopf. Ihn ganz abzuschalten waere die + bequeme Auslegung: Wer Bewegung meidet, will kein schlechteres + Aussehen, sondern nur keine Bewegung. */ + .kachel::after { transition: opacity var(--tempo); background-position: 42% 0, 0 0; } + .kachel:hover::after { background-position: 42% 0, 0 0; } } /* Bei erzwungenen Farben (Windows-Kontrastmodus) sind Mattglas und diff --git a/workspace/assets/css/wissen.css b/workspace/assets/css/wissen.css index 68571e75..a0936d00 100644 --- a/workspace/assets/css/wissen.css +++ b/workspace/assets/css/wissen.css @@ -185,14 +185,25 @@ border-color: rgba(232, 192, 125, .42); background: linear-gradient(rgba(232, 192, 125, .07), rgba(232, 192, 125, .07)), - rgba(9, 13, 22, .88); + /* AN --flaeche GEBUNDEN, nicht danebengeschrieben (07.09.2026). + Hier stand fest '.88'. Als --flaeche von 0,78 auf 0,89 stieg + (die Flaechen der Seite haben ihren Weichzeichner verloren), war + die WICHTIGE Karte ploetzlich duenner als eine gewoehnliche -- + genau der Fehler, gegen den pruef-lesbarkeit gebaut wurde, nur + diesmal von der anderen Seite ausgeloest. Eine feste Zahl neben + einer Groesse, die sich bewegt, geht irgendwann auseinander; + gefunden hat es die Pruefung, nicht das Auge. */ + var(--flaeche); } /* Dasselbe fuer das Neue: Es steht nur 48 Stunden da und soll in dieser Zeit auch wirklich gelesen werden. */ .pdf:has(.marke-neu) { background: linear-gradient(rgba(95, 201, 154, .05), rgba(95, 201, 154, .05)), - rgba(9, 13, 22, .88); + /* Dieselbe Bindung an --flaeche wie oben. Die Pruefung schaut nur + auf die WICHTIGE Karte -- diese hier haette denselben Fehler + still behalten, weil niemand nach ihr sieht. */ + var(--flaeche); } .pdf__kopf { display: flex; gap: 14px; align-items: flex-start; } diff --git a/workspace/assets/img/buehne-arena-breit.webp b/workspace/assets/img/buehne-arena-breit.webp index 41e502ec..fc628905 100644 Binary files a/workspace/assets/img/buehne-arena-breit.webp and b/workspace/assets/img/buehne-arena-breit.webp differ diff --git a/workspace/assets/img/buehne-arena-schmal.webp b/workspace/assets/img/buehne-arena-schmal.webp index 61813a33..e1e73077 100644 Binary files a/workspace/assets/img/buehne-arena-schmal.webp and b/workspace/assets/img/buehne-arena-schmal.webp differ diff --git a/workspace/assets/img/buehne-arena-uhd.webp b/workspace/assets/img/buehne-arena-uhd.webp index 2262ac2e..5e7e4c97 100644 Binary files a/workspace/assets/img/buehne-arena-uhd.webp and b/workspace/assets/img/buehne-arena-uhd.webp differ diff --git a/workspace/assets/img/buehne-garage-breit.webp b/workspace/assets/img/buehne-garage-breit.webp index aef43b6f..89e93607 100644 Binary files a/workspace/assets/img/buehne-garage-breit.webp and b/workspace/assets/img/buehne-garage-breit.webp differ diff --git a/workspace/assets/img/buehne-garage-schmal.webp b/workspace/assets/img/buehne-garage-schmal.webp index cd4a1d54..7b5a3042 100644 Binary files a/workspace/assets/img/buehne-garage-schmal.webp and b/workspace/assets/img/buehne-garage-schmal.webp differ diff --git a/workspace/assets/img/buehne-garage-uhd.webp b/workspace/assets/img/buehne-garage-uhd.webp index a689e8dc..6bf92274 100644 Binary files a/workspace/assets/img/buehne-garage-uhd.webp and b/workspace/assets/img/buehne-garage-uhd.webp differ diff --git a/workspace/assets/img/buehne-halle-breit.webp b/workspace/assets/img/buehne-halle-breit.webp index 41e502ec..fc628905 100644 Binary files a/workspace/assets/img/buehne-halle-breit.webp and b/workspace/assets/img/buehne-halle-breit.webp differ diff --git a/workspace/assets/img/buehne-halle-schmal.webp b/workspace/assets/img/buehne-halle-schmal.webp index 61813a33..e1e73077 100644 Binary files a/workspace/assets/img/buehne-halle-schmal.webp and b/workspace/assets/img/buehne-halle-schmal.webp differ diff --git a/workspace/assets/img/buehne-halle-uhd.webp b/workspace/assets/img/buehne-halle-uhd.webp index 2262ac2e..5e7e4c97 100644 Binary files a/workspace/assets/img/buehne-halle-uhd.webp and b/workspace/assets/img/buehne-halle-uhd.webp differ diff --git a/workspace/assets/img/buehne-lounge-breit.webp b/workspace/assets/img/buehne-lounge-breit.webp index 34063136..820bcb7a 100644 Binary files a/workspace/assets/img/buehne-lounge-breit.webp and b/workspace/assets/img/buehne-lounge-breit.webp differ diff --git a/workspace/assets/img/buehne-lounge-schmal.webp b/workspace/assets/img/buehne-lounge-schmal.webp index 036416a3..6269f207 100644 Binary files a/workspace/assets/img/buehne-lounge-schmal.webp and b/workspace/assets/img/buehne-lounge-schmal.webp differ diff --git a/workspace/assets/img/buehne-lounge-uhd.webp b/workspace/assets/img/buehne-lounge-uhd.webp index 440c9934..0122a0aa 100644 Binary files a/workspace/assets/img/buehne-lounge-uhd.webp and b/workspace/assets/img/buehne-lounge-uhd.webp differ diff --git a/workspace/assets/img/buehne-portal-breit.webp b/workspace/assets/img/buehne-portal-breit.webp index ea2830a5..912fece4 100644 Binary files a/workspace/assets/img/buehne-portal-breit.webp and b/workspace/assets/img/buehne-portal-breit.webp differ diff --git a/workspace/assets/img/buehne-portal-schmal.webp b/workspace/assets/img/buehne-portal-schmal.webp index 0b5ca480..f78e877f 100644 Binary files a/workspace/assets/img/buehne-portal-schmal.webp and b/workspace/assets/img/buehne-portal-schmal.webp differ diff --git a/workspace/assets/img/buehne-portal-uhd.webp b/workspace/assets/img/buehne-portal-uhd.webp index 70e7cb3d..6b25c48e 100644 Binary files a/workspace/assets/img/buehne-portal-uhd.webp and b/workspace/assets/img/buehne-portal-uhd.webp differ diff --git a/workspace/assets/img/buehne-showbuehne-breit.webp b/workspace/assets/img/buehne-showbuehne-breit.webp index 0666ea41..a9b18cbf 100644 Binary files a/workspace/assets/img/buehne-showbuehne-breit.webp and b/workspace/assets/img/buehne-showbuehne-breit.webp differ diff --git a/workspace/assets/img/buehne-showbuehne-schmal.webp b/workspace/assets/img/buehne-showbuehne-schmal.webp index 9a857995..63973a1e 100644 Binary files a/workspace/assets/img/buehne-showbuehne-schmal.webp and b/workspace/assets/img/buehne-showbuehne-schmal.webp differ diff --git a/workspace/assets/img/buehne-showbuehne-uhd.webp b/workspace/assets/img/buehne-showbuehne-uhd.webp index e792024b..e10cfa93 100644 Binary files a/workspace/assets/img/buehne-showbuehne-uhd.webp and b/workspace/assets/img/buehne-showbuehne-uhd.webp differ diff --git a/workspace/assets/img/buehne-skyline-breit.webp b/workspace/assets/img/buehne-skyline-breit.webp index 34063136..820bcb7a 100644 Binary files a/workspace/assets/img/buehne-skyline-breit.webp and b/workspace/assets/img/buehne-skyline-breit.webp differ diff --git a/workspace/assets/img/buehne-skyline-schmal.webp b/workspace/assets/img/buehne-skyline-schmal.webp index 036416a3..6269f207 100644 Binary files a/workspace/assets/img/buehne-skyline-schmal.webp and b/workspace/assets/img/buehne-skyline-schmal.webp differ diff --git a/workspace/assets/img/buehne-skyline-uhd.webp b/workspace/assets/img/buehne-skyline-uhd.webp index 440c9934..0122a0aa 100644 Binary files a/workspace/assets/img/buehne-skyline-uhd.webp and b/workspace/assets/img/buehne-skyline-uhd.webp differ diff --git a/workspace/assets/img/buehne-studio-breit.webp b/workspace/assets/img/buehne-studio-breit.webp index 0b5ee7d6..099c77ef 100644 Binary files a/workspace/assets/img/buehne-studio-breit.webp and b/workspace/assets/img/buehne-studio-breit.webp differ diff --git a/workspace/assets/img/buehne-studio-schmal.webp b/workspace/assets/img/buehne-studio-schmal.webp index 0963b1c9..9b9d3ce8 100644 Binary files a/workspace/assets/img/buehne-studio-schmal.webp and b/workspace/assets/img/buehne-studio-schmal.webp differ diff --git a/workspace/assets/img/buehne-studio-uhd.webp b/workspace/assets/img/buehne-studio-uhd.webp index c313443d..5a51ae4a 100644 Binary files a/workspace/assets/img/buehne-studio-uhd.webp and b/workspace/assets/img/buehne-studio-uhd.webp differ diff --git a/workspace/assets/img/buehne-wald-breit.webp b/workspace/assets/img/buehne-wald-breit.webp index 6dad778f..d74abff5 100644 Binary files a/workspace/assets/img/buehne-wald-breit.webp and b/workspace/assets/img/buehne-wald-breit.webp differ diff --git a/workspace/assets/img/buehne-wald-schmal.webp b/workspace/assets/img/buehne-wald-schmal.webp index b558ffe4..cbb5b866 100644 Binary files a/workspace/assets/img/buehne-wald-schmal.webp and b/workspace/assets/img/buehne-wald-schmal.webp differ diff --git a/workspace/assets/img/buehne-wald-uhd.webp b/workspace/assets/img/buehne-wald-uhd.webp index bd928eba..e224fca4 100644 Binary files a/workspace/assets/img/buehne-wald-uhd.webp and b/workspace/assets/img/buehne-wald-uhd.webp differ diff --git a/workspace/assets/img/buehne/gate-1280.webp b/workspace/assets/img/buehne/gate-1280.webp index edb7dbe0..7469d925 100644 Binary files a/workspace/assets/img/buehne/gate-1280.webp and b/workspace/assets/img/buehne/gate-1280.webp differ diff --git a/workspace/assets/img/buehne/gate-1600.webp b/workspace/assets/img/buehne/gate-1600.webp index 26630338..627ad641 100644 Binary files a/workspace/assets/img/buehne/gate-1600.webp and b/workspace/assets/img/buehne/gate-1600.webp differ diff --git a/workspace/assets/img/buehne/gate-1920.webp b/workspace/assets/img/buehne/gate-1920.webp index b3437e6a..6fef7455 100644 Binary files a/workspace/assets/img/buehne/gate-1920.webp and b/workspace/assets/img/buehne/gate-1920.webp differ diff --git a/workspace/assets/img/buehne/gate-2560.webp b/workspace/assets/img/buehne/gate-2560.webp index 8f976655..842525e0 100644 Binary files a/workspace/assets/img/buehne/gate-2560.webp and b/workspace/assets/img/buehne/gate-2560.webp differ diff --git a/workspace/assets/img/buehne/gate-960.webp b/workspace/assets/img/buehne/gate-960.webp index 6ae15137..69bf28de 100644 Binary files a/workspace/assets/img/buehne/gate-960.webp and b/workspace/assets/img/buehne/gate-960.webp differ diff --git a/workspace/assets/img/buehne/gate-hoch-440.webp b/workspace/assets/img/buehne/gate-hoch-440.webp index 12e61e90..68100842 100644 Binary files a/workspace/assets/img/buehne/gate-hoch-440.webp and b/workspace/assets/img/buehne/gate-hoch-440.webp differ diff --git a/workspace/assets/img/buehne/gate-hoch-580.webp b/workspace/assets/img/buehne/gate-hoch-580.webp index 8716aa68..a93a4528 100644 Binary files a/workspace/assets/img/buehne/gate-hoch-580.webp and b/workspace/assets/img/buehne/gate-hoch-580.webp differ diff --git a/workspace/assets/img/buehne/gate-hoch-900.webp b/workspace/assets/img/buehne/gate-hoch-900.webp index 2a3d8356..7b5c8995 100644 Binary files a/workspace/assets/img/buehne/gate-hoch-900.webp and b/workspace/assets/img/buehne/gate-hoch-900.webp differ diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html index 655e0dd3..2a4fff8b 100644 --- a/workspace/aufgaben.html +++ b/workspace/aufgaben.html @@ -9,9 +9,9 @@ - - - + + + @@ -211,10 +211,10 @@ - - - - - + + + + +