Kampagnen-Kacheln: gegliederte Texte, Geschenke mit Preis, aufklappbar

Filipe, mit einem Bildschirmfoto des Regelblocks: "man soll die titel
in den texten besser erkennen und sehen was zu was passt. ich will
auch dass man sieht welche geschenke bei dem event zaehlen und so.
ich will alle informationen die moeglich sind wenn man den link
hochlaedt. ... auch wenn man teilt soll es einfach perfekt sein. man
soll alle infos sehen die man braucht. die kacheln von den events
soll auch nicht so lang sein, die soll man aufklappen koennen. genau
wie eine trennung fuer heute, letzte woche letzten monat und so."

DER FEHLER LAG NICHT IM TEXT, SONDERN IN SEINER ANZEIGE. Im Feld
stand die ganze Zeit eine Gliederung -- Hinweise mit "* ", eine
Ueberschrift mit Untertitel hinter einem Gedankenstrich, Absaetze
ohne Zeichen, Aufzaehlungen mit "- ". Die Kachel hat daraus mit
punkteAusText() EINE flache Liste gemacht: jede Zeile derselbe
Strich, alle gleich gross.

  gliederung()      liest die Gliederung, die schon dasteht, statt
                    den Text umzuschreiben. Die beiden Kampagnen, die
                    heute in der Datenbank liegen, sehen dadurch
                    sofort richtig aus -- ohne Umstellung.
  Geschenke         kommen aus der gelesenen LISTE, nicht aus dem
                    Text, und stehen OHNE Aufklappen auf der Kachel.
  Aufklappen        alles Uebrige hinter einem Druck: gemessen 555 px
                    weniger je Kachel bei Gipfelstuermer.
  Gruppen           je Spalte laeuft / kommt / vorbei, Vergangenes
                    nach diese Woche, dieser Monat, aelter.

NEU GELESEN (stand in allen vier gespeicherten Seiten und wurde nie
angesehen): die Rangliste mit ihren Gruppen ("Superstars" bis
"5. Liga", bei Glow Up "Team Glow"/"Team Shine"), die Beschreibung je
Missionsgruppe, der Steckbrief (Kurznummer, Markt, Sprache, Zone),
der zweite Bildbaustein, und Ueberschriften INNERHALB der Klapptexte
-- "WELCHE LIGA BIN ICH?" hat jetzt sechs eingerueckte Unterpunkte
statt zwoelf gleichrangiger Zeilen.

EVENT_TEXT_MAX 6000 -> 20000. Der Regeltext von Gipfelstuermer ist
7679 Zeichen lang; es fielen bisher jedes Mal rund 1600 Zeichen
echter Inhalt weg. Die Zahl steht jetzt an EINER Stelle.

TEILEN: Was die KAMPAGNE sagt, geht hinaus (Aufgaben, Geschenke,
Regeln) -- wer WIR sind, nicht (Name, Rolle, Haus, Kampagnennummer,
Steckbrief). Und keine Hinweiszeile: "Die Preise konnten nicht
gelesen werden -- bitte nachtragen" ist eine Notiz an DogFather und
hat auf einer Seite, die er in Discord postet, nichts zu suchen.

BERICHTIGT, WEIL NACHGEMESSEN:
  - Number(null) ist 0 und 0 ist endlich -- "Feuerwerk" bekam an drei
    Stellen "0 Coins" angeschrieben, also eine falsche Preisangabe.
  - Ein Kommentar im Leser behauptete, der Wertungszeitraum weiche
    vom Kampagnenzeitraum ab. Nachgemessen ist er in allen drei
    Seiten mit Rangliste auf die Millisekunde gleich; ich hatte zwei
    verschiedene Kampagnen verglichen.
  - bild-kampagne.mjs verlangte "jede Kachel traegt den Ton ihrer
    Seite" und war damit seit dem 07.10. rot, weil an dem Tag bewusst
    das Gegenteil entschieden wurde (Zustandston, Seitenkante). Nicht
    aufgefallen, weil nach dem Lauf nur die Zahl der MESSUNGEN notiert
    wurde, nicht die der FEHLER.
  - .kv-seite > .eintrag-karte wird .kv-seite .eintrag-karte: Die
    Karten haengen durch die Gruppen eine Ebene tiefer. Das waere der
    fuenfte Direktkind-Selektor gewesen, der nach einem Umbau still
    ausfaellt -- diesmal vorher gesehen.

GEPRUEFT: 186 (lesen, +58), 148 (Route/Teilen, +25), 158 (Kachel,
+64), 67 (Agentur, +5), 67 Messungen (Bild, +14), dazu struktur,
css-klassen, zeichen, deutsche-texte, tippziele, lesbarkeit und
haus-trennung (107, unveraendert). Sechs Gegenproben: alle sechs
Sabotagen werden bemerkt, alle Dateien byte-gleich wiederhergestellt.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-10-07 22:39:03 +02:00
co-authored by Claude Opus 5
parent d7720a3687
commit cadab24a5f
58 changed files with 3950 additions and 798 deletions
+160 -16
View File
@@ -16,7 +16,13 @@
Aufrufen mit: node server/bild-kampagne.mjs Aufrufen mit: node server/bild-kampagne.mjs
*/ */
import { mkdtempSync, rmSync } from "node:fs"; import { mkdtempSync, rmSync, readFileSync } from "node:fs";
import { brotliDecompressSync } from "node:zlib";
/* DER ECHTE LESER (07.10.2026) -- damit auf dem Bild eine Kampagne
steht, die wirklich so auf tiktok.com stand, und keine nachgebaute.
Ohne Netz: Die Seite liegt als Datei in server/pruefdaten und wird
von pruef-kampagne-lesen.mjs an ihrer Pruefsumme festgehalten. */
import { leseKampagne, regelnText, aufgabenText } from "./kampagne-lesen.mjs";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { notbremse } from "./helfer-notbremse.mjs"; import { notbremse } from "./helfer-notbremse.mjs";
@@ -90,24 +96,50 @@ const tagHin = (v) => {
const z = (n) => String(n).padStart(2, "0"); const z = (n) => String(n).padStart(2, "0");
return `${x.getFullYear()}-${z(x.getMonth() + 1)}-${z(x.getDate())}`; return `${x.getFullYear()}-${z(x.getMonth() + 1)}-${z(x.getDate())}`;
}; };
function event(titel, text, von, bis, auf, reg, kampagneId) { function event(titel, text, von, bis, auf, reg, kampagneId, daten) {
d.prepare(`INSERT INTO eintraege d.prepare(`INSERT INTO eintraege
(bereich, art, titel, text, datum, event_ende, event_aufgaben, event_regeln, (bereich, art, titel, text, datum, event_ende, event_aufgaben, event_regeln,
dringlichkeit, status, nur_leitung, erstellt, erstellt_von, haus, kampagne_id) dringlichkeit, status, nur_leitung, erstellt, erstellt_von, haus, kampagne_id,
VALUES ('agentur','kampagne',?,?,?,?,?,?,?,'offen',0,?,?,NULL,?)`) kampagne_daten)
VALUES ('agentur','kampagne',?,?,?,?,?,?,?,'offen',0,?,?,NULL,?,?)`)
.run(titel, text, von, bis, auf, reg, .run(titel, text, von, bis, auf, reg,
bis < tagHin(7) ? "hoch" : "mittel", j, idDogi, kampagneId); bis < tagHin(7) ? "hoch" : "mittel", j, idDogi, kampagneId,
daten === undefined || daten === null ? null : JSON.stringify(daten));
} }
/* ==== EINE ECHTE KAMPAGNE, NICHT EINE NACHGEBAUTE (07.10.2026) ==
Die uebrigen Kacheln hier sind von Hand getippt, und das ist fuer
ihren Zweck richtig: kurze, berechenbare Daten fuer Abstaende und
Toene. Fuer die Gliederung taugt das nicht. „KAMPAGNENZEITPLAN"
mit zwei Zeilen zeigt weder Geschenke mit Coinpreis noch
Unterabschnitte noch einen Steckbrief -- also waere auf dem Bild
nichts von dem zu sehen, worum es Filipe ging.
Deshalb geht EINE Kachel durch den echten Leser: dieselbe
gespeicherte Seite, die pruef-kampagne-lesen.mjs an ihrer
Pruefsumme festhaelt. Was auf dem Bild steht, hat damit wirklich
auf tiktok.com gestanden.
KEIN NETZ: Die Seite liegt als Datei in server/pruefdaten. */
const echteSeite = brotliDecompressSync(
readFileSync(new URL("./pruefdaten/kampagne-gipfelstuermer.html.br", import.meta.url)))
.toString("utf8");
const echt = leseKampagne(echteSeite);
event("LIVE Glow Up", "Eine vierwöchige Wachstums-Challenge für deine LIVEs.", event("LIVE Glow Up", "Eine vierwöchige Wachstums-Challenge für deine LIVEs.",
tagHin(-2), tagHin(4), tagHin(-9), tagHin(-1),
"Deine täglichen LIVE Missionen\nDeine wöchentliche Missionen", "Deine täglichen LIVE Missionen\nDeine wöchentliche Missionen",
"KAMPAGNENZEITPLAN\n· Woche 1: 5. - 11.10.\n· Woche 2: 12. - 18.10.", "KAMPAGNENZEITPLAN\n· Woche 1: 5. - 11.10.\n· Woche 2: 12. - 18.10.",
"7690927903777262344"); "7690927903777262344");
event("Gipfelstürmer", "Bereit für Alpen-Vibes in deinem LIVE?", /* LAEUFT (frueher: vorbei). So steht sie in der offenen Gruppe und
tagHin(-9), tagHin(-1), ist auf dem Bild zu sehen. „LIVE Glow Up" uebernimmt dafuer den
"1 Diamant = 1 Punkt\nDreifache Punkte für Kampagnengeschenke", Zustand „vorbei" -- die drei Zustaende bleiben also vertreten,
"BELOHNUNGEN\nSuperstars: Top 1 = 60.000 Diamanten", und die Tonmessung weiter unten misst weiterhin drei
"7691493175936699143"); verschiedene. */
event("Gipfelstürmer", echt.kuer.einleitung || "Bereit für Alpen-Vibes in deinem LIVE?",
tagHin(-3), tagHin(10),
aufgabenText(echt.kuer), regelnText(echt.kuer, echt.hinweise),
"7691493175936699143", echt.auszug);
event("Spicy Media Teamabend", "Unser eigener Abend — Rückblick und Planung.", event("Spicy Media Teamabend", "Unser eigener Abend — Rückblick und Planung.",
tagHin(3), tagHin(3), tagHin(3), tagHin(3),
"Pünktlich um 20 Uhr da sein\nEinen Punkt für die Runde mitbringen", "Pünktlich um 20 Uhr da sein\nEinen Punkt für die Runde mitbringen",
@@ -400,10 +432,24 @@ for (const [breite, name] of [[1400, 'Am Rechner'], [412, 'Auf dem Handy']]) {
zahl: s.querySelector(".kv-seite__zahl")?.textContent?.trim(), zahl: s.querySelector(".kv-seite__zahl")?.textContent?.trim(),
...r(s), ...r(s),
ton: getComputedStyle(s).getPropertyValue("--kv-ton").trim(), ton: getComputedStyle(s).getPropertyValue("--kv-ton").trim(),
karten: [...s.querySelectorAll(".eintrag-karte")].map((k) => ({ karten: [...s.querySelectorAll(".eintrag-karte")].map((k) => {
const st = getComputedStyle(k);
return {
titel: k.querySelector(".eintrag-karte__titel, h3, h2")?.textContent?.trim()?.slice(0, 28), titel: k.querySelector(".eintrag-karte__titel, h3, h2")?.textContent?.trim()?.slice(0, 28),
ton: getComputedStyle(k).getPropertyValue("--ton").trim(), ton: st.getPropertyValue("--ton").trim(),
})), /* ZWEI TOENE, ZWEI KANAELE (siehe die Begruendung bei der
Pruefung unten): `--ton` sagt den ZUSTAND, `--kv-ton`
die SEITE. Gemessen werden beide, sonst kann die
Pruefung sie nicht auseinanderhalten. */
kvTon: st.getPropertyValue("--kv-ton").trim(),
stand: k.dataset.event || null,
/* Und ob der Seitenton wirklich GEZEICHNET wird. Ein
Merkmal, das gesetzt ist, aber nichts malt, ist keine
Auskunft -- derselbe Fehler wie beim Farbpunkt am
07.10.2026, der 10 statt 22 px breit war. */
bild: st.backgroundImage.slice(0, 200),
};
}),
})); }));
const kopfA = t.querySelector('.kv-seite[data-seite="tiktok"] .kv-seite__titel'); const kopfA = t.querySelector('.kv-seite[data-seite="tiktok"] .kv-seite__titel');
const zahlA = t.querySelector('.kv-seite[data-seite="tiktok"] .kv-seite__zahl'); const zahlA = t.querySelector('.kv-seite[data-seite="tiktok"] .kv-seite__zahl');
@@ -429,8 +475,63 @@ for (const [breite, name] of [[1400, 'Am Rechner'], [412, 'Auf dem Handy']]) {
"links TikTok, rechts die Agentur"); "links TikTok, rechts die Agentur");
ok(mass.seiten[0].ton !== mass.seiten[1].ton, ok(mass.seiten[0].ton !== mass.seiten[1].ton,
`jede Seite hat ihren eigenen Ton (${mass.seiten[0].ton} / ${mass.seiten[1].ton})`); `jede Seite hat ihren eigenen Ton (${mass.seiten[0].ton} / ${mass.seiten[1].ton})`);
ok(mass.seiten.every((s) => s.karten.every((k) => k.ton === s.ton)), /* ==== ZWEI TOENE, ZWEI KANAELE (berichtigt 07.10.2026) =======
"und jede Kachel traegt den Ton ihrer Seite");
HIER STAND `k.ton === s.ton` -- „jede Kachel traegt den Ton
ihrer Seite". Diese Zeile war ROT, und zwar schon vor dem
heutigen Umbau: Am 07.10.2026 wurde bewusst entschieden, dass
die Kachel den Ton ihres ZUSTANDS traegt (laeuft gruen, kommt
blau, vorbei stumpf) und die SEITE eine eigene Kante links
bekommt. Die Pruefung hier wurde dabei nicht mitgezogen und
widersprach seitdem dem eigenen Entwurf.
SIE IST NIEMANDEM AUFGEFALLEN, weil nach dem Lauf nur die
Zahl der Messungen notiert wurde („53 Messungen") und nicht
die Zahl der Fehler. Genau davor warnt CLAUDE.md seit dem
28.08.2026 -- und diesmal hat es nicht die Zahl der Pruefungen
getroffen, sondern die der Fehler. Beides gehoert verglichen.
WARUM DER ZUSTAND GEWINNT: Ob etwas LAEUFT oder VORBEI ist,
entscheidet, ob man heute etwas tut. Woher es kommt, steht
ohnehin zweimal da -- in der Spaltenueberschrift und auf dem
Herkunftsschild der Kachel.
GEPRUEFT WIRD JETZT BEIDES, getrennt: der Zustandston am
`data-event` der Kachel, der Seitenton daran, dass er bis zur
Kachel durchkommt UND dort wirklich gezeichnet wird. */
const ZUSTAND_TON = {
laeuft: "#cace02", kommt: "#5b8fd4", vorbei: "#6b7686",
};
const alleKarten = mass.seiten.flatMap((s) =>
s.karten.map((k) => ({ ...k, seitenTon: s.ton })));
ok(alleKarten.length >= 2,
`${alleKarten.length} Kacheln in den Spalten (sonst misst das Folgende nichts)`);
const falscheToene = alleKarten.filter((k) =>
ZUSTAND_TON[k.stand] && k.ton.toLowerCase() !== ZUSTAND_TON[k.stand]);
ok(falscheToene.length === 0,
`jede Kachel traegt den Ton ihres ZUSTANDS `
+ `(${alleKarten.map((k) => `${k.stand}=${k.ton}`).join(", ")})`);
/* GEGENPROBE: Die Toene sind nicht alle gleich -- sonst waere
„jeder Zustand hat einen eigenen" bei einer einzigen Farbe
auch gruen. */
ok(new Set(alleKarten.map((k) => k.ton)).size >= 2,
`und verschiedene Zustaende verschiedene Toene `
+ `(${[...new Set(alleKarten.map((k) => k.ton))].join(" / ")})`);
/* DER SEITENTON KOMMT BIS ZUR KACHEL ... */
ok(alleKarten.every((k) => k.kvTon === k.seitenTon),
`der Seitenton erbt bis in jede Kachel `
+ `(${alleKarten.map((k) => k.kvTon).join(", ")})`);
/* ... UND WIRD DORT GEZEICHNET. `--kv-ton` gesetzt zu haben,
heisst noch nicht, dass man etwas sieht: Der Verlauf fuer die
Kante links steht in `background-image`, und wenn die Regel
dafuer ausfaellt (etwa weil ein Direktkind-Selektor nach einem
Umbau nicht mehr greift), ist das Merkmal da und die Kante
weg. Geprueft wird deshalb der GEZEICHNETE Verlauf. */
const mitKante = alleKarten.filter((k) => /gradient/.test(k.bild || ""));
ok(mitKante.length === alleKarten.length,
`und jede Kachel zeichnet ihre Kante wirklich `
+ `(${mitKante.length} von ${alleKarten.length})`);
if (breite >= 820) { if (breite >= 820) {
ok(mass.nebeneinander, "sie stehen nebeneinander"); ok(mass.nebeneinander, "sie stehen nebeneinander");
ok(Math.abs(mass.seiten[0].b - mass.seiten[1].b) <= 2, ok(Math.abs(mass.seiten[0].b - mass.seiten[1].b) <= 2,
@@ -502,6 +603,49 @@ for (const [breite, name] of [[1400, 'Am Rechner'], [412, 'Auf dem Handy']]) {
const t2 = await seite.$(".kv-trennung"); const t2 = await seite.$(".kv-trennung");
await t2.screenshot({ path: `server/bild-kampagne-trennung-${breite}.png` }); await t2.screenshot({ path: `server/bild-kampagne-trennung-${breite}.png` });
/* ---- UND EINMAL AUFGEKLAPPT (07.10.2026) ---------------------------
Das Bild oben zeigt, dass die Kachel KURZ ist. Es zeigt nicht, was
dahinter steht -- und genau darum ging es Filipe zuerst: „man soll
die titel in den texten besser erkennen und sehen was zu was
passt." Also ein zweites Bild mit offenem Kasten.
NUR DIE EINE KACHEL, nicht das ganze Brett: Mit 13 Abschnitten
waere das Bild sonst mehrere tausend Pixel hoch und niemand sieht
etwas. */
const gipfel = await seite.evaluateHandle(() => {
const k = [...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')]
.find((x) => x.textContent.includes("Gipfelstürmer"));
k?.querySelector(".ev-mehr__knopf")?.click();
return k;
});
await seite.waitForTimeout(400);
const offen = await seite.evaluate(() => {
const k = [...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')]
.find((x) => x.textContent.includes("Gipfelstürmer"));
return {
auf: k?.querySelector(".ev-mehr__inhalt")?.hidden === false,
titel: [...(k?.querySelectorAll(".ev-abschnitt__titel") || [])]
.map((h) => h.textContent.trim()).slice(0, 5),
stufen: [...(k?.querySelectorAll(".ev-abschnitt") || [])]
.map((a) => a.dataset.stufe),
};
});
ok(offen.auf === true, `der Infokasten laesst sich aufklappen (${offen.auf})`);
/* DIE ZAHL GEHOERT IN DIE BEDINGUNG, nicht nur in den Meldetext:
Ein Bild von einem leeren Kasten waere sonst genauso gruen. */
ok(offen.titel.length >= 3,
`und zeigt die Ueberschriften der Kampagne: ${JSON.stringify(offen.titel)}`);
ok(offen.stufen.includes("2") && offen.stufen.includes("3"),
`auf zwei Stufen, nicht alle gleichrangig `
+ `(${offen.stufen.filter((s) => s === "2").length}x Stufe 2, `
+ `${offen.stufen.filter((s) => s === "3").length}x Stufe 3)`);
if (gipfel) {
await gipfel.asElement()?.screenshot({
path: `server/bild-kampagne-infos-${breite}.png`,
});
}
await ctx.close(); await ctx.close();
} }
+700 -17
View File
@@ -136,6 +136,28 @@ export class KampagneUnlesbar extends Error {
/** Der Kalendertag zu einem Zeitpunkt, in einer bestimmten Zone. /** Der Kalendertag zu einem Zeitpunkt, in einer bestimmten Zone.
* `sv-SE` liefert `JJJJ-MM-TT` ohne eigenes Zusammenbauen. */ * `sv-SE` liefert `JJJJ-MM-TT` ohne eigenes Zusammenbauen. */
/** Ein Zeitstempel in Millisekunden -- oder nichts.
*
* DIESELBE SCHWELLE WIE BEI DEN PFLICHTFELDERN (1e12): Eine Zahl
* darunter ist eine Sekundenangabe, und die ergibt das Jahr 1970.
* Ein falsches Datum sieht aus wie ein Datum -- deshalb lieber
* nichts als etwas Falsches. */
export function zeitOderNull(roh) {
const n = Number(roh);
return Number.isFinite(n) && n >= 1_000_000_000_000 ? n : null;
}
/** `2026-11-04` -> `04.11.2026`.
*
* `tagInZone` liefert absichtlich die sortierbare Form (sv-SE).
* In einem Text, den DogFather liest, steht aber das deutsche
* Datum. Rein umgestellt, nicht neu gerechnet -- es wird keine
* Zone und keine Uhr angefasst. */
export function deutschesDatum(tag) {
const m = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.exec(String(tag || ""));
return m ? `${m[3]}.${m[2]}.${m[1]}` : null;
}
export function tagInZone(ms, zone) { export function tagInZone(ms, zone) {
const zahl = Number(ms); const zahl = Number(ms);
if (!Number.isFinite(zahl)) return null; if (!Number.isFinite(zahl)) return null;
@@ -310,6 +332,30 @@ function zeichenZurueck(text) {
* „1. Liga", „2. Liga", „3. Liga" wuerde eine unbrauchbare Liste * „1. Liga", „2. Liga", „3. Liga" wuerde eine unbrauchbare Liste
* gleicher Zeilen, und niemand koennte sagen, welche Liga gemeint * gleicher Zeilen, und niemand koennte sagen, welche Liga gemeint
* ist. Deshalb werden Nummern gesetzt, nicht verworfen. */ * ist. Deshalb werden Nummern gesetzt, nicht verworfen. */
/* WAS AUF DIESER SEITE „LEERRAUM" HEISST (gemessen 07.10.2026).
Die fuenfte Liga bei „Gipfelstuermer" steht als
<li ...><strong>Liga&nbsp;</strong>&nbsp;</li>
-- mit einem geschuetzten Leerzeichen HINTER der Auszeichnung.
Die ersten vier haben es nicht. Ein Muster, das dort `\s*`
erwartet, erkennt vier von fuenf Ligen als Ueberschrift und die
fuenfte nicht; gemessen hing „5. Liga" danach als Absatz unter
der vierten. Die Entitaeten werden erst viel spaeter aufgeloest
(`zeichenZurueck` am Ende), also muessen die Muster hier noch
mit ihnen rechnen.
Als Zeichenkette und `new RegExp`, nicht als Literal: Dieselbe
Gruppe steht in zwei Mustern, und zwei Abschriften derselben
Regel laufen auseinander. */
const LEER = "(?:\\s|&nbsp;|&#160;|&#xa0;)*";
const NUR_STARK = new RegExp(
`^${LEER}<(strong|b|em)\\b[^>]*>([^<]*)<\\/\\1>${LEER}$`, "i");
const ABSATZ_STARK = new RegExp(
`<(p|div)\\b[^>]*>${LEER}<(strong|b|em)\\b[^>]*>([^<]{1,90})<\\/\\2>${LEER}<\\/\\1>`,
"gi");
export function textAusHtml(html) { export function textAusHtml(html) {
let text = String(html || ""); let text = String(html || "");
if (!text) return ""; if (!text) return "";
@@ -321,10 +367,84 @@ export function textAusHtml(html) {
text = text.replace(/<ol\b([^>]*)>([\s\S]*?)<\/ol>/gi, (ganz, merkmale, inhalt) => { text = text.replace(/<ol\b([^>]*)>([\s\S]*?)<\/ol>/gi, (ganz, merkmale, inhalt) => {
const m = /\bstart\s*=\s*["']?(\d+)/i.exec(merkmale); const m = /\bstart\s*=\s*["']?(\d+)/i.exec(merkmale);
let n = m ? parseInt(m[1], 10) : 1; let n = m ? parseInt(m[1], 10) : 1;
return inhalt.replace(/<li\b[^>]*>/gi, () => `\n${n++}. `);
/* EIN LISTENPUNKT, DER NUR AUS EINER HERVORHEBUNG BESTEHT, IST
EINE UEBERSCHRIFT MIT NUMMER (gemessen 07.10.2026).
So stehen die Ligen bei „Gipfelstuermer" auf der Seite:
<ol start="1"><li ...><strong>Liga&nbsp;</strong></li></ol>
<p>Du kannst an dieser Liga teilnehmen, wenn ...</p>
<ol start="2"><li ...><strong>Liga&nbsp;</strong></li></ol>
<p>Du kannst an dieser Liga teilnehmen, wenn ...</p>
Sechsmal „Liga" mit einer Nummer von aussen, und darunter
jeweils die Bedingung dafuer. Flach ausgegeben standen zwoelf
gleichrangige Zeilen da, und die Frage „ab wie vielen
Diamanten bin ich in der 2. Liga" war daran nicht mehr zu
beantworten -- obwohl die Antwort danebenstand.
DIE NUMMER BLEIBT DABEI. Sie kommt aus `<ol start>`, nicht
aus dem Text; „### Liga" sechsmal hintereinander waere
schlimmer als vorher.
ZWEI DURCHGAENGE, UND DER ZWEITE IST DIE SICHERUNG: Das
erste Muster braucht ein schliessendes `</li>`. Fehlt es --
in HTML erlaubt --, faellt der Punkt durch, und ohne den
zweiten Durchgang waere er samt Inhalt verschwunden. Vorher
stand hier nur das oeffnende `<li>`; diese Nachsicht darf
nicht verloren gehen. */
let aus = inhalt.replace(/<li\b[^>]*>([\s\S]*?)<\/li\s*>/gi, (g2, drin) => {
const stark = NUR_STARK.exec(drin);
return stark ? `\n### ${n++}. ${stark[2].trim()}\n` : `\n${n++}. ${drin}`;
});
aus = aus.replace(/<li\b[^>]*>/gi, () => `\n${n++}. `);
return aus;
}); });
text = text.replace(/<li\b[^>]*>/gi, "\n· "); text = text.replace(/<li\b[^>]*>/gi, "\n· ");
/* UEBERSCHRIFTEN IM KLAPPTEXT (07.10.2026, zweite Runde).
Der laengste Block der Kachel sind die Teilnahmebedingungen
-- bei „Gipfelstuermer" rund 4000 Zeichen in einem Stueck.
Darin steht eine Gliederung, die beim Entfernen der
Auszeichnungen verloren ging. Im Quelltext der Seite:
<p style="text-align: center;"><strong>Superstars</strong></p>
<p>Du kannst an dieser Liga teilnehmen, wenn ...</p>
„Superstars" ist die Ueberschrift zu dem Absatz darunter.
Flach ausgegeben standen beide als gleichwertige Zeilen da,
und bei sechs Ligen hintereinander war nicht mehr zu sehen,
welche Beschreibung zu welcher Liga gehoert. Genau das meint
„sehen was zu was passt".
ERKANNT WIRD NUR DER EINDEUTIGE FALL: ein Absatz, dessen
GANZER Inhalt eine einzige Auszeichnung ist. `<p>Um was geht
es bei <strong>Gipfelstuermer?</strong></p>` ist dagegen eine
Frage MIT hervorgehobenem Wort -- auch das ist der Sache nach
eine Ueberschrift, aber das zu behaupten waere geraten. Sie
bleibt ein Absatz.
`###` UND NICHT `##`: Zwei Stufen. `regelnText` setzt `##`
fuer die Abschnitte, die es selbst bildet (GESCHENKE,
BELOHNUNGEN, TEILNAHME); was aus einem fremden Text kommt,
steht darunter. Die Kachel ruecke es dadurch sichtbar ein,
statt es auf eine Hoehe mit den eigenen Ueberschriften zu
stellen -- was eine Gliederung behaupten wuerde, die die
Seite so nicht hat.
NACH der `<li>`-Behandlung, nicht davor: Die Ligennummern
stehen auf der Seite als `<ol start=N><li><strong>Liga</strong>`,
also in einer Liste. Liefe diese Ersetzung zuerst, wuerde
aus „1. Liga" eine Ueberschrift „Liga" ohne Nummer -- und
sechsmal dieselbe. */
text = text.replace(ABSATZ_STARK,
(ganz, huelle, marke, inhalt) => `\n### ${inhalt.trim()}\n`);
text = text.replace(
/<h[1-6]\b[^>]*>([\s\S]{1,160}?)<\/h[1-6]>/gi,
(ganz, inhalt) => `\n### ${inhalt.replace(/<[^>]+>/g, " ").trim()}\n`);
text = text text = text
.replace(/<br\s*\/?>/gi, "\n") .replace(/<br\s*\/?>/gi, "\n")
/* `</li>` setzt KEINEN Umbruch -- den hat `<li>` schon gesetzt. /* `</li>` setzt KEINEN Umbruch -- den hat `<li>` schon gesetzt.
@@ -502,16 +622,49 @@ export function leseKampagne(html) {
abhaken kann. Doppelte fallen weg: Die Vorlage legt je Reiter abhaken kann. Doppelte fallen weg: Die Vorlage legt je Reiter
zwei Eintraege an (einen fuer Creator, einen fuer Zuschauer), zwei Eintraege an (einen fuer Creator, einen fuer Zuschauer),
und zweimal derselbe Haken waere ein Haken an zwei Stellen. */ und zweimal derselbe Haken waere ein Haken an zwei Stellen. */
if (!aufgaben.length) { /* ZU JEDER GRUPPE GEHOERT MEHR ALS IHR TITEL (07.10.2026, zweite
const gruppe = erster("live_task_group"); Runde).
Bisher wurde hier nur `displayInfo.title` genommen. Daneben
steht `descText` -- ein Platzhalter wie jeder andere, also im
Woerterbuch der Seite aufloesbar -- und in `config[0]` der
Zeitraum, fuer den die Gruppe gilt. Bei „LIVE Glow Up" sind das
vier Gruppen mit je einer eigenen Beschreibung; in der Kachel
stand davon nichts.
ZWEI LISTEN, ZWEI ZWECKE: `aufgaben` ist die HAKENLISTE -- dort
waere ein Doppler ein Haken, der an zwei Stellen gleichzeitig
erscheint. `aufgabenGruppen` ist die BESCHREIBUNG dazu und wird
ueber denselben Titel zugeordnet. Entdoppelt wird deshalb nach
dem Titel, und beide Listen bleiben dadurch deckungsgleich.
GELESEN WIRD IMMER, auch wenn die Aufgaben aus der ersten
Vorlage (`live_rule_introduction`) kamen: Eine Kampagne kann
beides haben, und dann ist die Beschreibung nicht weniger wert.
Nur die Hakenliste wird nicht mehr angefasst, wenn sie steht. */
const gruppenBaustein = erster("live_task_group");
const aufgabenGruppen = [];
{
const gesehen = new Set(); const gesehen = new Set();
for (const a of gruppe?.props?.taskItemList || []) { for (const a of gruppenBaustein?.props?.taskItemList || []) {
const titel = auf(a?.displayInfo?.title); const titel = auf(a?.displayInfo?.title);
if (!titel || gesehen.has(titel.trim())) continue; if (!titel || gesehen.has(titel.trim())) continue;
gesehen.add(titel.trim()); gesehen.add(titel.trim());
aufgaben.push(titel.trim()); const stelle = Array.isArray(a?.config) ? a.config[0] : null;
const von = zeitOderNull(stelle?.startTime ?? stelle?.start_time);
const bis = zeitOderNull(stelle?.endTime ?? stelle?.end_time);
aufgabenGruppen.push({
titel: titel.trim(),
beschreibung: (auf(a?.displayInfo?.descText) || "").trim() || null,
von, bis,
von_tag: von !== null && zone ? tagInZone(von, zone) : null,
bis_tag: bis !== null && zone ? tagInZone(bis, zone) : null,
});
} }
if (aufgaben.length) aufgabenTitel = aufgabenTitel || "Missionen"; }
if (!aufgaben.length && aufgabenGruppen.length) {
for (const g of aufgabenGruppen) aufgaben.push(g.titel);
aufgabenTitel = aufgabenTitel || "Missionen";
} }
/* GIBT ES SIE NICHT, ODER KONNTEN WIR SIE NICHT LESEN? /* GIBT ES SIE NICHT, ODER KONNTEN WIR SIE NICHT LESEN?
@@ -519,7 +672,7 @@ export function leseKampagne(html) {
werden" schickt jemanden suchen; „hat keine" sagt ihm, dass werden" schickt jemanden suchen; „hat keine" sagt ihm, dass
nichts fehlt. */ nichts fehlt. */
if (!aufgaben.length) { if (!aufgaben.length) {
hinweise.push(regelBaustein || erster("live_task_group") hinweise.push(regelBaustein || gruppenBaustein
? "Die Aufgaben konnten nicht gelesen werden — bitte nachtragen." ? "Die Aufgaben konnten nicht gelesen werden — bitte nachtragen."
: "Diese Kampagne hat keine Aufgabenliste auf der Seite."); : "Diese Kampagne hat keine Aufgabenliste auf der Seite.");
} }
@@ -636,6 +789,79 @@ export function leseKampagne(html) {
const freiBaustein = erster("live_normal_rich_text"); const freiBaustein = erster("live_normal_rich_text");
const freitext = freiBaustein ? textAusHtml(aufMit(freiBaustein.props || {}, "content")) : null; const freitext = freiBaustein ? textAusHtml(aufMit(freiBaustein.props || {}, "content")) : null;
/* ---- DIE RANGLISTE (07.10.2026, zweite Runde) -----------------
`live_single_rank_list` stand in allen vier gespeicherten
Seiten da und wurde nie gelesen -- ausgerechnet der Baustein,
der bei einem Wettbewerb die wichtigste Frage beantwortet:
nach WELCHEM Zeitraum wird gewertet.
WAS DIESER BAUSTEIN WIRKLICH BRINGT (nachgemessen 07.10.2026,
nachdem meine erste Begruendung hier falsch war).
Hier stand: „Der Wertungszeitraum ist NICHT der
Kampagnenzeitraum -- bei LIVE Glow Up laeuft die Wertung
anders." Das ist nicht wahr. Nachgemessen in allen vier
gespeicherten Seiten:
gipfelstuermer Kampagne 1791064800000..1791410399000
Wertung 1791064800000..1791410399000
ohne-preise Kampagne 1786399200000..1786658399000
Wertung 1786399200000..1786658399000
aufgabengruppe Kampagne 1791151200000..1791755999000
Wertung 1791151200000..1791755999000
beendet hat gar keine Rangliste
Dreimal auf die Millisekunde gleich. Ich hatte
„Gipfelstuermers" KAMPAGNENzeitraum mit „Glow Ups"
WERTUNGSzeitraum verglichen -- zwei verschiedene Kampagnen,
und die Zahlen sahen deshalb verschieden aus. Eine plausible
Herleitung ersetzt keine Messung (CLAUDE.md, 06.09.2026), und
ein Kommentar, der etwas Falsches behauptet, ist schlimmer
als keiner: Der naechste glaubt ihn.
GELESEN WIRD DER BAUSTEIN TROTZDEM, aus drei Gruenden, die
alle gemessen sind:
1. DIE GRUPPEN. `tabInfo[].name` -- bei „Gipfelstuermer"
sechs Ligen („Superstars" bis „5. Liga"), bei „Glow Up"
zwei. Das ist die Antwort auf „gegen wen trete ich an"
und stand nirgends sonst auf der Kachel.
2. OB DIE PUNKTE SICHTBAR SIND (`hide_ranklist_score`).
Eine Auskunft, nach der im Stream gefragt wird.
3. Der Zeitraum IST ein eigenes Feld. Dass er in vier
Seiten gleich war, heisst nicht, dass er es bleibt --
und wenn er einmal abweicht, steht es dann da, statt
dass jemand den falschen Tag streamt.
Die Reiternamen (`tabInfo[].name`) sind die Gruppen, in die
eingeteilt wird. Auch sie sind Platzhalter und stehen im
Woerterbuch der Seite -- `auf()` loest sie auf.
`hide_ranklist_score` kommt mit, weil „man sieht die Punkte
nicht" eine Auskunft ist, nach der im Stream gefragt wird.
Beide Schreibweisen werden gelesen (`hide_ranklist_score` und
`hideRanklistScore`): TikTok liefert dieselbe Angabe zweimal,
und in einer kuenftigen Fassung vielleicht nur noch eine. */
const rangBaustein = erster("live_single_rank_list");
let rangliste = null;
if (rangBaustein) {
const basis = rangBaustein.props?.basicInfo || {};
const anzeige = rangBaustein.props?.displaySettings || {};
const rohText = auf(anzeige.desc);
const von = zeitOderNull(basis.start_time ?? basis.startTime);
const bis = zeitOderNull(basis.end_time ?? basis.endTime);
rangliste = {
beschreibung: rohText ? textAusHtml(rohText) : null,
von, bis,
von_tag: von !== null && zone ? tagInZone(von, zone) : null,
bis_tag: bis !== null && zone ? tagInZone(bis, zone) : null,
reiter: (rangBaustein.props?.tabInfo || [])
.map((r) => auf(r?.name)).filter(Boolean).map((r) => r.trim()),
punkteVerborgen: !!(basis.hide_ranklist_score ?? basis.hideRanklistScore),
};
}
/* Banner: `props.imageUrl[0].url`, NICHT `props.url` und nicht /* Banner: `props.imageUrl[0].url`, NICHT `props.url` und nicht
`pageInfo.topImageInfo` -- die zeigen auf das Vorlagenbild. */ `pageInfo.topImageInfo` -- die zeigen auf das Vorlagenbild. */
const bildBaustein = erster("live_top_image"); const bildBaustein = erster("live_top_image");
@@ -657,7 +883,29 @@ export function leseKampagne(html) {
const bannerUrl = (bildBaustein?.props?.imageUrl || []) const bannerUrl = (bildBaustein?.props?.imageUrl || [])
.map((b) => b?.url || b?.urlSG) .map((b) => b?.url || b?.urlSG)
.find((u) => typeof u === "string" && /^https?:\/\//i.test(u)) || null; .find((u) => typeof u === "string" && /^https?:\/\//i.test(u)) || null;
if (!bannerUrl) hinweise.push("Das Banner konnte nicht gelesen werden — bitte nachtragen.");
/* `live_banner` IST DER ZWEITE BILDBAUSTEIN DER VORLAGE
(07.10.2026, zweite Runde).
Bei „LIVE Glow Up" stehen dort zwei weitere Bilder
(Missionsuebersicht, Belohnungen) -- und eine Kampagne ganz
ohne `live_top_image` haette bisher gar kein Banner bekommen,
obwohl ein Bild dastand.
GEHOLT WIRD WEITERHIN NUR EINES. Die Kachel hat einen Kopf,
nicht drei; die uebrigen stehen im Auszug, damit sie nicht
verloren sind, falls sie spaeter gebraucht werden. */
const weitereBilder = [];
for (const b of teile.get("live_banner") || []) {
for (const bild of b?.props?.styleConfig?.imageConfig?.imageInfo || []) {
const u = bild?.url || bild?.urlSG;
if (typeof u === "string" && /^https?:\/\//i.test(u) && !weitereBilder.includes(u)) {
weitereBilder.push(u);
}
}
}
const bannerAdresse = bannerUrl || weitereBilder[0] || null;
if (!bannerAdresse) hinweise.push("Das Banner konnte nicht gelesen werden — bitte nachtragen.");
/* Laender */ /* Laender */
let laender = []; let laender = [];
@@ -668,6 +916,50 @@ export function leseKampagne(html) {
} catch { /* unten als Hinweis */ } } catch { /* unten als Hinweis */ }
if (!laender.length) hinweise.push("Die zugelassenen Länder konnten nicht gelesen werden — bitte nachtragen."); if (!laender.length) hinweise.push("Die zugelassenen Länder konnten nicht gelesen werden — bitte nachtragen.");
/* ---- DER STECKBRIEF: die harten Tatsachen (07.10.2026) --------
Lauter Felder, die in `activityInfo` die ganze Zeit dastanden
und die niemand gesehen hat. Aufgenommen wird nur, was man
zeigen kann, OHNE es zu deuten:
short_id die Kurznummer, mit der TikTok die Kampagne in
Rueckfragen nennt (1140116740)
region der Markt, nicht das Land (EU-TTP2)
language die Sprache, in der die Seite angelegt wurde
union_name der Verbund -- steht in allen vier gemessenen
Seiten LEER und faellt dann weg, statt als
„—" zu erscheinen
ANMELDUNG UND ERGEBNIS NUR BEI ABWEICHUNG.
`warm_up_start_time` und `show_result_end_time` sind in allen
vier gemessenen Kampagnen GLEICH dem Kampagnenzeitraum. Sie
trotzdem hinzuschreiben hiesse, dieselbe Zahl dreimal zu
zeigen und so zu tun, als sei es eine zusaetzliche Auskunft --
und beim naechsten Mal, wenn sie wirklich abweicht, faellt es
niemandem auf. Also: nur wenn sie etwas Neues sagen.
ABSICHTLICH NICHT AUFGENOMMEN: `activity_type: 2`,
`activity_kind_flag: 1`, `platform_flag`, `priority`,
`sdlc_status`, `biz_id`. Das sind Zahlen ohne erklaerende
Tabelle -- wer sie anzeigt, zeigt eine Auskunft, die er selbst
nicht lesen kann. Sie stehen weiter im Rohauszug, und wenn
einmal bekannt ist, was `activity_type: 3` bedeutet, sind sie
in einer Zeile nachgetragen. */
const anmeldungMs = zeitOderNull(aktion.warm_up_start_time);
const ergebnisMs = zeitOderNull(aktion.show_result_end_time);
const steckbrief = {
nummer: id,
kurznummer: String(aktion.short_id ?? "").trim() || null,
region: String(aktion.region ?? "").trim() || null,
sprache: String(aktion.language ?? "").trim() || null,
verbund: String(aktion.union_name ?? "").trim() || null,
zeitzone: zone,
anmeldung_ab: anmeldungMs !== null && anmeldungMs !== startMs
? (zone ? tagInZone(anmeldungMs, zone) : null) : null,
ergebnis_bis: ergebnisMs !== null && ergebnisMs !== endeMs
? (zone ? tagInZone(ergebnisMs, zone) : null) : null,
};
if (ohneEintrag.length) { if (ohneEintrag.length) {
hinweise.push(`${ohneEintrag.length} Textbaustein(e) hatten keinen Eintrag ` hinweise.push(`${ohneEintrag.length} Textbaustein(e) hatten keinen Eintrag `
+ "im Wörterbuch der Seite — der Text fehlt an diesen Stellen."); + "im Wörterbuch der Seite — der Text fehlt an diesen Stellen.");
@@ -677,11 +969,12 @@ export function leseKampagne(html) {
pflicht: { titel, start, ende, id, zone }, pflicht: { titel, start, ende, id, zone },
kuer: { kuer: {
einleitung, einleitungTitel, einleitung, einleitungTitel,
aufgaben, aufgabenTitel, weitereRegeln, aufgaben, aufgabenTitel, aufgabenGruppen, weitereRegeln,
geschenke, geschenkTitel, geschenkHinweis, geschenke, geschenkTitel, geschenkHinweis,
preise, preisTitel, preise, preisTitel,
klapptexte, freitext, klapptexte, freitext,
laender, bannerUrl, rangliste, steckbrief,
laender, bannerUrl: bannerAdresse,
}, },
hinweise, hinweise,
/* ROH MITSPEICHERN (Teil 5 des Bauplans): Wenn in drei Monaten /* ROH MITSPEICHERN (Teil 5 des Bauplans): Wenn in drei Monaten
@@ -715,8 +1008,13 @@ export function leseKampagne(html) {
nennt: Wenn in drei Monaten auffaellt, dass die Ligen auch nennt: Wenn in drei Monaten auffaellt, dass die Ligen auch
gebraucht werden, steht die Antwort schon da -- und eine gebraucht werden, steht die Antwort schon da -- und eine
abgelaufene Kampagne antwortet dann vielleicht nicht mehr. */ abgelaufene Kampagne antwortet dann vielleicht nicht mehr. */
einleitung, aufgaben, geschenke, preise, klapptexte, einleitung, einleitungTitel,
banner_url: bannerUrl, aufgaben, aufgabenTitel, aufgabenGruppen,
geschenke, geschenkTitel, geschenkHinweis,
preise, preisTitel, klapptexte,
rangliste, steckbrief,
banner_url: bannerAdresse,
weitere_bilder: weitereBilder,
woerterbuch_eintraege: Object.keys(woerterbuch).length, woerterbuch_eintraege: Object.keys(woerterbuch).length,
platzhalter_ohne_eintrag: ohneEintrag.length, platzhalter_ohne_eintrag: ohneEintrag.length,
}, },
@@ -748,7 +1046,23 @@ export function aufgabenText(kuer) {
export function regelnText(kuer, hinweise = []) { export function regelnText(kuer, hinweise = []) {
const k = kuer || {}; const k = kuer || {};
const bloecke = []; const bloecke = [];
const gross = (t) => String(t).toUpperCase(); /* `## ` DAVOR -- die Ueberschrift sagt selbst, dass sie eine ist
(07.10.2026).
Vorher stand hier nur `toUpperCase()`, und die Kachel musste
RATEN, ob eine Zeile eine Ueberschrift ist. Das ging bei
„TEILNAHME" gut und bei „WELCHE LIGA BIN ICH?" schief: Die
Erkennung verwirft Zeilen, die wie ein Satz enden, weil ein
geschrienes „SCHLIESSE DEINE MISSIONEN AB!" sonst eine
Ueberschrift waere. Ein Klapptext-Titel mit Fragezeichen fiel
damit durch und landete als Absatz IM Abschnitt darueber --
gemessen: die sechs Ligen hingen unter „RANGLISTE".
Wo wir den Text selbst schreiben, muss nichts geraten werden.
Die Erkennung an Grossschreibung bleibt daneben bestehen --
fuer von Hand getippte Texte und fuer die beiden Kampagnen,
die am 07.10.2026 schon in der Datenbank liegen. */
const gross = (t) => `## ${String(t).toUpperCase()}`;
if (k.geschenke?.length) { if (k.geschenke?.length) {
const kopf = [gross(k.geschenkTitel || "Kampagnengeschenke")]; const kopf = [gross(k.geschenkTitel || "Kampagnengeschenke")];
@@ -771,11 +1085,37 @@ export function regelnText(kuer, hinweise = []) {
if (k.preise?.length) { if (k.preise?.length) {
bloecke.push([ bloecke.push([
gross(k.preisTitel || "Preise"), gross(k.preisTitel || "Preise"),
...k.preise.map((p) => `${p.liga}: ` /* MIT AUFZAEHLUNGSZEICHEN (07.10.2026): Sechs Ligen
hintereinander sind eine Liste, und ohne Zeichen davor
sahen sie wie sechs Absaetze aus. */
...k.preise.map((p) => `· ${p.liga}: `
+ p.plaetze.map((s) => `${s.platz} = ${s.preise.join(", ")}`).join(" · ")), + p.plaetze.map((s) => `${s.platz} = ${s.preise.join(", ")}`).join(" · ")),
].join("\n")); ].join("\n"));
} }
/* DIE RANGLISTE ALS EIGENER ABSCHNITT (07.10.2026).
Sie steht VOR den Klapptexten und nach den Preisen: „wo
lande ich" gehoert neben „was bekomme ich", und die
Klapptexte sind Beiwerk. */
if (k.rangliste) {
const r = k.rangliste;
const zeilen = [];
/* BESCHRIFTET, nicht nackt. `displaySettings.desc` ist bei
„Gipfelstuermer" das Wort „Punkte" -- allein in einer Zeile
sieht das aus, als sei beim Einlesen etwas abgebrochen. Was
es IST, steht im Feldnamen: die Beschreibung der Wertung. */
if (r.beschreibung) zeilen.push(`· Wertung: ${r.beschreibung.trim()}`);
if (r.von_tag && r.bis_tag) {
zeilen.push(`· Gewertet wird vom ${deutschesDatum(r.von_tag)} `
+ `bis zum ${deutschesDatum(r.bis_tag)}`);
}
if (r.reiter?.length) zeilen.push(`· Gruppen: ${r.reiter.join(" · ")}`);
if (r.punkteVerborgen) {
zeilen.push("· Die Punktzahl wird in der Rangliste nicht angezeigt.");
}
if (zeilen.length) bloecke.push([gross("Rangliste"), ...zeilen].join("\n"));
}
for (const kl of k.klapptexte || []) { for (const kl of k.klapptexte || []) {
bloecke.push(`${gross(kl.titel)}\n${kl.text}`); bloecke.push(`${gross(kl.titel)}\n${kl.text}`);
} }
@@ -783,7 +1123,12 @@ export function regelnText(kuer, hinweise = []) {
if (k.freitext) bloecke.push(k.freitext); if (k.freitext) bloecke.push(k.freitext);
if (k.laender?.length) { if (k.laender?.length) {
bloecke.push(`TEILNAHME\nLänder: ${k.laender.join(", ")}`); /* „Länder: ..." MIT WORT DAVOR, nicht nur die Codes.
`DE, CH, AT, LI, LU` allein ist gross geschrieben und waere
fuer die Ueberschriftenerkennung eine Ueberschrift -- die
Kleinbuchstaben in „Länder" sind die Sicherung dagegen.
Steht so auch als Gegenprobe in pruef-kampagne-lesen.mjs. */
bloecke.push(`${gross("Teilnahme")}\nLänder: ${k.laender.join(", ")}`);
} }
/* Was nicht gelesen werden konnte, steht IN der Kachel -- nicht nur /* Was nicht gelesen werden konnte, steht IN der Kachel -- nicht nur
@@ -805,7 +1150,345 @@ export function regelnText(kuer, hinweise = []) {
tun). Ein pauschales „Bitte nachtragen" hinter beiden haette tun). Ein pauschales „Bitte nachtragen" hinter beiden haette
jemanden losgeschickt, etwas nachzutragen, das es gar nicht jemanden losgeschickt, etwas nachzutragen, das es gar nicht
gibt. Jeder Hinweis sagt jetzt selbst, ob etwas zu tun ist. */ gibt. Jeder Hinweis sagt jetzt selbst, ob etwas zu tun ist. */
const vorweg = (hinweise || []).map((h) => `* ${h}`); /* ALLE HINWEISE IN EINEM BLOCK, nicht je einer (07.10.2026).
Vorher trennte eine Leerzeile jeden Hinweis vom naechsten --
im Rohtext sah das aus wie drei Abschnitte ohne Ueberschrift.
`gliederung()` fasst sie ohnehin zusammen; so stimmt auch der
Text, den DogFather beim Bearbeiten vorfindet, mit dem
ueberein, was er auf der Kachel sieht. */
const vorweg = (hinweise || []).map((h) => `* ${h}`).join("\n");
return [...vorweg, ...bloecke].join("\n\n").trim(); return [vorweg, ...bloecke].filter(Boolean).join("\n\n").trim();
}
/* ===================================================================
DER REGELTEXT, GEGLIEDERT (07.10.2026, zweite Runde)
Filipe, mit einem Bildschirmfoto des Regelblocks: „ich will das die
texte perfektionierter werden. man soll die titel in den texten
besser erkennen und sehen was zu was passt. ich will auch dass man
sieht welche geschenke bei dem event zaehlen und so."
Er hatte recht, und der Fehler lag nicht im Text, sondern in seiner
ANZEIGE. Im Feld stand die ganze Zeit eine Gliederung -- so sieht
der echte Inhalt der Kachel „LIVE Glow Up" aus:
* Diese Kampagne hat keine Preisliste auf der Seite.
SAMMLE DEIN FEUERWERK — Erhalte doppelte Punkte fuer die Rangliste
· Feuerwerk (Sammle Feuerwerk in deinem LIVE!)
INFOS ZUR KAMPAGNE
LIVE Glow Up ist eine exklusive 4-woechige Wachstum Challenge, ...
KAMPAGNENZEITPLAN
· Woche 1: 5. - 11.10.
Die Kachel hat daraus mit `punkteAusText()` EINE flache Liste
gemacht -- jede Zeile derselbe Strich, alle gleich gross.
„SAMMLE DEIN FEUERWERK" sah damit genauso aus wie „Feuerwerk",
„INFOS ZUR KAMPAGNE" wie der Absatz darunter, und die beiden
Hinweise standen zwischen den Regeln, als waeren sie welche.
DESHALB WIRD HIER GELESEN UND NICHT UMGESCHRIEBEN. Das Feld bleibt
Text: DogFather kann es bearbeiten, und was er tippt, bleibt
stehen. Die Gliederung entsteht beim Anzeigen aus dem, was schon
dasteht. Das hat einen zweiten, handfesten Vorteil -- die beiden
Kampagnen, die am 07.10.2026 bereits in der Datenbank liegen,
sehen sofort richtig aus. Ohne Umstellung, ohne erneutes Einlesen,
ohne ein Feld anzufassen, das jemandem gehoert.
---------------------------------------------------------------------
WIE EINE UEBERSCHRIFT ERKANNT WIRD
Eine Zeile ist eine Ueberschrift, wenn sie
- kein Aufzaehlungszeichen und kein `*` vorne hat,
- mindestens einen Grossbuchstaben und KEINEN Kleinbuchstaben
enthaelt (`\p{Lu}` ja, `\p{Ll}` nein),
- hoechstens 80 Zeichen lang ist,
- nicht mit `.`, `!` oder `?` endet.
Dazu `## Titel` fuer den Fall, dass jemand von Hand gliedern will.
`\p{Ll}` UND NICHT `text === text.toUpperCase()`: Bei „GROSSE"
waere beides gleich, bei einem Wort mit „ß" aber nicht --
`"ß".toUpperCase()` ist `"SS"`, und eine Ueberschrift mit ß waere
damit nie eine. Die Frage ist ohnehin „stehen hier Kleinbuchstaben",
nicht „laesst sich das noch vergroessern".
DER UNTERTITEL steht hinter einem Gedankenstrich mit Leerzeichen
(`SAMMLE DEIN FEUERWERK — Erhalte doppelte Punkte`). Geprueft wird
nur der Teil DAVOR auf Grossschreibung -- sonst waere genau diese
Zeile, die der Leser selbst so schreibt, keine Ueberschrift.
DAS IST EINE HEURISTIK, UND SIE TRAEGT ABSICHTLICH NICHTS.
Greift sie nicht, erscheint die Zeile als gewoehnlicher Absatz --
also genau so wie bisher. Es gibt keinen Fall, in dem ein
Fehlurteil Inhalt verliert, und keinen, in dem etwas davon abhaengt.
Der eine bekannte Fehlgriff ist eine Zeile aus nichts als
Laendercodes (`DE, CH, AT`): Die ist gross geschrieben und wuerde
zur Ueberschrift. `regelnText` schreibt deshalb „Länder: DE, CH,
AT" -- die Kleinbuchstaben im Wort davor sind die Sicherung, und
die Gegenprobe in pruef-kampagne-lesen.mjs haelt sie fest.
---------------------------------------------------------------------
WIE EIN GESCHENK ERKANNT WIRD
Nicht am Text, sondern an der LISTE. `gliederung(text, { geschenke })`
bekommt die Geschenke so, wie der Leser sie aus der Seite geholt
hat -- mit Name, Coinpreis und Hinweis. Eine Aufzaehlungszeile,
die mit einem dieser Namen beginnt, ist ein Geschenk, und der
Preis kommt aus der LISTE, nicht aus dem Text.
Warum nicht nach „Coins" im Text suchen? Weil „Feuerwerk (Sammle
Feuerwerk in deinem LIVE!)" gar keinen Preis hat -- es ist ein
Kampagnengeschenk, kein Coin-Geschenk -- und trotzdem das
Geschenk ist, das bei diesem Event zaehlt. Genau danach hat
Filipe gefragt. Die Textsuche nach „— N Coins" bleibt als
ZWEITER Weg daneben: Sie greift bei von Hand getippten Zeilen,
fuer die es keine Liste gibt.
DIE LAENGSTEN NAMEN ZUERST: Stuenden „Rose" und „Rose Gold" in
derselben Kampagne, wuerde ein Vergleich von vorne bei „Rose Gold"
den Eintrag „Rose" treffen und dessen Preis anschreiben.
=================================================================== */
/* `*` fehlt hier ABSICHTLICH, obwohl `PUNKT_ZEICHEN` in
workspace-bereiche.js es als Aufzaehlungszeichen fuehrt: `* ` ist
die Hinweismarke, die `regelnText` setzt. Stuende es in beiden
Mustern, haengte die Entscheidung an der Reihenfolge der Abfragen
-- und die ist genau die Sorte Abhaengigkeit, die beim naechsten
Umbau still kippt. */
const G_AUFZAEHLUNG = /^[ \t]*[-–—•·▪◦‣>+][ \t]*/u;
const G_HINWEIS = /^[ \t]*\*[ \t]+/u;
const G_RAUTE = /^[ \t]*#{1,6}[ \t]*/u;
const G_TRENNER = /[ \t]+[—–][ \t]+/u;
const G_COINS = /[ \t]+[—–][ \t]+([0-9]{1,9})[ \t]+Coins?$/u;
const G_SATZENDE = /[.!?]$/u;
const G_KLAMMER = /^\((.*)\)$/u;
const G_VORSPANN = /^[—–-][ \t]*/u;
const KOPF_MAX = 80;
/** Titel und Untertitel einer Zeile, am Gedankenstrich getrennt. */
function teileTitel(zeile) {
const t = String(zeile).trim();
const m = G_TRENNER.exec(t);
if (!m) return { titel: t, unter: null };
return {
titel: t.slice(0, m.index).trim(),
unter: t.slice(m.index + m[0].length).trim() || null,
};
}
/** Ist diese Zeile eine Ueberschrift? Dann Titel und Untertitel. */
/* ZWEI STUFEN, FLACH GEFUEHRT.
`##` sind die Abschnitte, die `regelnText` selbst bildet,
`###` die Ueberschriften aus einem fremden Text (Klapptexte,
siehe textAusHtml). Die Liste bleibt trotzdem FLACH: Ein
Baum waere im Anzeigecode drei Verschachtelungen tief, und
gewonnen waere nichts, was eine Einrueckung nicht auch
zeigt. Wer `stufe` nicht beachtet, bekommt die alte,
gleichrangige Darstellung -- kein Inhalt haengt daran. */
function kopfzeile(zeile) {
const t = String(zeile).trim();
if (!t) return null;
if (G_RAUTE.test(t)) {
const rauten = (/^[ \t]*(#{1,6})/u.exec(t) || ["", "##"])[1].length;
const ohne = t.replace(G_RAUTE, "").trim();
/* Gedeckelt auf 2 und 3: Mehr Stufen kann die Kachel nicht
zeigen, und eine vierte waere eine Auskunft, die niemand
sieht. */
return ohne ? { ...teileTitel(ohne), stufe: rauten <= 2 ? 2 : 3 } : null;
}
if (G_HINWEIS.test(t) || G_AUFZAEHLUNG.test(t)) return null;
const zerlegt = teileTitel(t);
const k = zerlegt.titel;
if (!k || k.length > KOPF_MAX) return null;
if (!/\p{Lu}/u.test(k)) return null;
if (/\p{Ll}/u.test(k)) return null;
if (G_SATZENDE.test(k)) return null;
return { ...zerlegt, stufe: 2 };
}
/* Die Art eines Abschnitts entscheidet ueber Zeichen und Farbe --
nicht ueber den Inhalt. Trifft kein Wort, bleibt sie leer und der
Abschnitt sieht schlicht aus. Deutsche Woerter in einer Tabelle
sind sonst genau die Sorte Vergleich, vor der CLAUDE.md warnt
(„IDs wandern nicht, Klartext schon") -- hier traegt sie aber
nichts: Der Text IST deutsch, weil die Kachel deutsch ist, und
ein Fehlgriff kostet ein Symbol. */
const ABSCHNITT_WORTE = [
[/rangliste|ranking/u, "rangliste"],
[/zeitplan|termin|phase|woche/u, "zeitplan"],
[/teilnahme|bedingung|land|länder|laender/u, "teilnahme"],
[/preis|belohnung|gewinn|reward/u, "preise"],
[/geschenk|gift/u, "geschenke"],
[/aufgabe|mission|regel/u, "regeln"],
[/info|ueber|über/u, "infos"],
];
function abschnittArt(a) {
if (a.zeilen.some((z) => z.art === "geschenk")) return "geschenke";
/* `every()` AUF EINEM LEEREN FELD IST `true` -- genau die Falle
aus CLAUDE.md (01.09.2026). Ein Abschnitt, der nur wegen
seiner Unterabschnitte stehen bleibt, hat keine Zeilen und
waere damit ein „Hinweisblock" -- also in der Kachel ein
stiller, grauer Kasten, obwohl es eine Ueberschrift ist. */
if (a.zeilen.length && a.zeilen.every((z) => z.art === "hinweis")) return "hinweise";
const t = (a.titel || "").toLocaleLowerCase("de-DE");
if (t) for (const [muster, art] of ABSCHNITT_WORTE) if (muster.test(t)) return art;
return null;
}
/** Zerlegt einen Regeltext in Abschnitte mit Ueberschrift.
*
* @param {string} text der Inhalt von `event_regeln`
* @param {{geschenke?: Array}} [zusatz] die gelesenen Geschenke
* @returns {Array<{titel: ?string, unter: ?string, art: ?string,
* zeilen: Array<object>}>}
*
* Zeilenarten: `hinweis` (was fehlt), `geschenk` (mit `coins` und
* `zusatz`), `punkt` (Aufzaehlung), `text` (Absatz).
*/
export function gliederung(text, zusatz = {}) {
const namen = (Array.isArray(zusatz.geschenke) ? zusatz.geschenke : [])
.filter((g) => g && typeof g.name === "string" && g.name.trim())
.map((g) => ({
name: g.name.trim(),
/* `Number(null)` IST 0, UND 0 IST ENDLICH (gemessen
07.10.2026, beim ersten Lauf gegen den echten
Datenbankeintrag). Hier stand
`Number.isFinite(Number(g.preis)) ? ... : null` -- und
„Feuerwerk", ein Kampagnengeschenk OHNE Coinpreis, bekam
dadurch „0 Coins" angeschrieben. Das ist keine
Kleinigkeit: „0 Coins" ist eine Preisangabe, und zwar
eine falsche. Erst auf „nichts da" pruefen, dann
rechnen. */
preis: (g.preis === null || g.preis === undefined || g.preis === "")
? null
: (Number.isFinite(Number(g.preis)) ? Number(g.preis) : null),
hinweis: typeof g.hinweis === "string" ? g.hinweis.trim() : null,
}))
.sort((a, b) => b.name.length - a.name.length);
const abschnitte = [];
let jetzt = null;
const neu = (titel, unter, stufe = 2) => {
jetzt = {
titel: titel || null, unter: unter || null,
stufe, art: null, zeilen: [],
};
abschnitte.push(jetzt);
};
for (const rohzeile of String(text || "").split(/\r?\n/)) {
const zeile = rohzeile.trim();
if (!zeile) continue;
const kopf = kopfzeile(zeile);
if (kopf) { neu(kopf.titel, kopf.unter, kopf.stufe); continue; }
/* Steht etwas VOR der ersten Ueberschrift -- wie die Hinweise --,
bekommt es einen Abschnitt ohne Titel. Der wird beim Anzeigen
ohne Ueberschrift ausgegeben, nicht mit einer erfundenen. */
if (!jetzt) neu(null, null);
if (G_HINWEIS.test(zeile)) {
jetzt.zeilen.push({ art: "hinweis", text: zeile.replace(G_HINWEIS, "").trim() });
continue;
}
if (G_AUFZAEHLUNG.test(zeile)) {
const nur = zeile.replace(G_AUFZAEHLUNG, "").trim();
/* Erst den Coinpreis abtrennen, DANN den Namen vergleichen:
sonst endet „300 Coins" als Zusatztext hinter dem Geschenk
und steht doppelt da -- einmal als Text, einmal als Zahl. */
const c = G_COINS.exec(nur);
const ohneCoins = c ? nur.slice(0, c.index).trim() : nur;
const coinsAusText = c ? Number(c[1]) : null;
const treffer = namen.find((g) => ohneCoins === g.name
|| ohneCoins.startsWith(`${g.name} `)
|| ohneCoins.startsWith(`${g.name}(`));
if (treffer) {
const rest = ohneCoins.slice(treffer.name.length).trim()
.replace(G_VORSPANN, "").trim().replace(G_KLAMMER, "$1").trim();
jetzt.zeilen.push({
art: "geschenk",
text: treffer.name,
coins: treffer.preis ?? coinsAusText,
zusatz: treffer.hinweis || rest || null,
});
continue;
}
jetzt.zeilen.push(coinsAusText !== null
? { art: "geschenk", text: ohneCoins, coins: coinsAusText, zusatz: null }
: { art: "punkt", text: nur });
continue;
}
jetzt.zeilen.push({ art: "text", text: zeile });
}
/* EINE FRAGE MIT ANTWORT DARUNTER IST EINE UEBERSCHRIFT
(07.10.2026).
Der Teilnahmeblock von „Gipfelstuermer" ist nach Fragen
gegliedert, und zwar durchgehend:
Um was geht es bei Gipfelstürmer?
Gipfelstürmer ist eine TikTok LIVE Kampagne, in welcher ...
Wer kann bei Gipfelstürmer teilnehmen?
Diese Kampagne findet von 04.10.2025 ... statt.
Wie kann ich Punkte sammeln?
...
Acht solche Fragen in rund 4000 Zeichen. Im Quelltext stehen
sie NICHT als Ueberschrift -- sie sind der erste Satz eines
Absatzes, getrennt durch `<br>` --, also kann `textAusHtml`
sie nicht als eine erkennen. Flach ausgegeben sahen sie wie
Fliesstext aus, und der ganze Block war eine Wand.
DIE REGEL: eine Textzeile, die mit `?` endet, hoechstens 90
Zeichen hat und auf die noch etwas folgt. Eine Frage, auf die
NICHTS folgt, ist keine Ueberschrift, sondern eine Frage --
„Hast du Fragen?" am Ende eines Blocks bleibt deshalb eine
gewoehnliche Zeile, solange darunter nichts steht.
DAS GESCHIEHT IM ANZEIGEN, NICHT IM TEXT. Im Feld aendert sich
nichts; wer die Regel fuer falsch haelt, bekommt eine Zeile
in Fettschrift zu sehen und keinen fehlenden Inhalt. Das ist
der Grund, warum sie ueberhaupt erlaubt ist: Sie raet, und
was sie raet, traegt nichts. */
for (const a of abschnitte) {
for (let i = 0; i < a.zeilen.length - 1; i += 1) {
const z = a.zeilen[i];
if (z.art !== "text" || z.text.length > 90 || !z.text.endsWith("?")) continue;
if (a.zeilen[i + 1].art !== "text" && a.zeilen[i + 1].art !== "punkt") continue;
z.art = "frage";
}
}
/* EIN ABSCHNITT OHNE ZEILEN FAELLT WEG -- ABER NICHT, WENN ER
UNTERABSCHNITTE HAT (berichtigt 07.10.2026, beim ersten Lauf
gegen „Gipfelstuermer").
Die Regel „ohne Zeilen, also weg" ist richtig fuer den Fall,
fuer den sie gedacht war: jemand loescht beim Bearbeiten den
Inhalt und laesst die Ueberschrift stehen. Gemessen hat sie
aber etwas anderes getroffen -- den Klapptext „WELCHE LIGA BIN
ICH?". Unter ihm steht keine einzige eigene Zeile, sondern
sechs Unterabschnitte („1. Liga", „2. Liga", ...). Die
Ueberschrift verschwand, und die sechs Ligen hingen danach
ohne Zusammenhang zwischen Rangliste und
Teilnahmebedingungen.
Ein Abschnitt bleibt deshalb, wenn ihm ein TIEFERER folgt.
Geprueft wird der unmittelbar naechste: Dazwischen kann
nichts liegen, denn jede Zeile gehoert zum vorherigen
Abschnitt. */
const fertig = abschnitte.filter((a, i) => a.zeilen.length
|| (abschnitte[i + 1] && abschnitte[i + 1].stufe > a.stufe));
for (const a of fertig) a.art = abschnittArt(a);
return fertig;
} }
+67 -3
View File
@@ -726,7 +726,33 @@ melde("\n=== Die Kachel ===");
marke: k.querySelector(".ev-noch")?.textContent.trim(), marke: k.querySelector(".ev-noch")?.textContent.trim(),
spanne: k.querySelector(".ev-spanne")?.textContent.trim(), spanne: k.querySelector(".ev-spanne")?.textContent.trim(),
schilder: [...k.querySelectorAll(".ev-block__schild")].map((s) => s.textContent.trim()), schilder: [...k.querySelectorAll(".ev-block__schild")].map((s) => s.textContent.trim()),
/* `.ev-punkt__text` SIND DIE AUFGABEN, `.ev-zeile__text` DIE
REGELN (07.10.2026). Bis dahin war beides dieselbe flache
Liste; seit der Gliederung sind es zwei Sorten Zeile, und
die Regeln haben kein Kästchen mehr davor. Gelesen werden
beide -- und getrennt, damit die Prüfung unten sagen kann,
WELCHE Sorte fehlt, statt nur „einer von zweien fehlt". */
texte: [...k.querySelectorAll(".ev-punkt__text")].map((s) => s.textContent.trim()), texte: [...k.querySelectorAll(".ev-punkt__text")].map((s) => s.textContent.trim()),
/* ZEILEN **UND** ABSAETZE. Eine Regelzeile MIT
Aufzaehlungszeichen wird ein `.ev-zeile`, eine OHNE ein
`.ev-absatz` -- und das ist richtig so: Sonst wuerden die
Prosa-Absaetze einer Kampagnenseite („LIVE Glow Up ist
eine exklusive 4-woechige Challenge ...") alle zu
Aufzaehlungspunkten, also wieder zu der flachen Liste, die
am 07.10.2026 abgeschafft wurde.
Dieses Testevent wird ueber die Schnittstelle angelegt,
ohne Zeichen davor. Der Zeileneditor im Formular setzt sie
sonst selbst (geprueft in pruef-eventkarte.mjs:
„gleichmaessig gesetzte Zeichen"), im echten Betrieb ist
also meist die erste Form zu sehen. Gemessen werden beide,
getrennt -- so sagt die Prüfung, WELCHE Form herauskam. */
regelzeilen: [...k.querySelectorAll(".ev-zeile__text")].map((s) => s.textContent.trim()),
regelabsaetze: [...k.querySelectorAll(".ev-absatz")].map((s) => s.textContent.trim()),
/* Und ob die Regeln hinter dem Aufklappen liegen -- das ist
der Grund, warum die Kachel kürzer geworden ist. */
klappZu: k.querySelector(".ev-mehr__inhalt")?.hidden ?? null,
klappWort: k.querySelector(".ev-mehr__wort")?.textContent.trim() || null,
}; };
}); });
ok(!!event, "die Event-Karte Sommer-Aktion steht auf der Seite"); ok(!!event, "die Event-Karte Sommer-Aktion steht auf der Seite");
@@ -736,10 +762,48 @@ melde("\n=== Die Kachel ===");
ok(/^(noch \d+ Tage?|heute ist der letzte Tag)$/.test(event?.marke || ""), ok(/^(noch \d+ Tage?|heute ist der letzte Tag)$/.test(event?.marke || ""),
`der Zustand steht als WORT da, nicht nur als Farbe ("${event?.marke}")`); `der Zustand steht als WORT da, nicht nur als Farbe ("${event?.marke}")`);
ok(/–/.test(event?.spanne || ""), `und der Zeitraum als Spanne ("${event?.spanne}")`); ok(/–/.test(event?.spanne || ""), `und der Zeitraum als Spanne ("${event?.spanne}")`);
ok(event?.schilder.join(",") === "Aufgaben,Regeln", /* „Regeln & Infos" SEIT DEM 07.10.2026. Der Block trägt nicht
mehr nur die Regeln, sondern die ganze Gliederung der
Kampagnenseite -- Zeitplan, Rangliste, Teilnahme. „Regeln"
allein wäre dafür zu wenig gesagt. */
ok(event?.schilder.join(",") === "Aufgaben,Regeln & Infos",
`beide Bloecke sind beschriftet (${event?.schilder.join(", ")})`); `beide Bloecke sind beschriftet (${event?.schilder.join(", ")})`);
ok(event?.texte.includes("1 Punkt je Live-Stunde") && event?.texte.includes("Nur eigenes Konto"), ok(event?.texte.includes("1 Punkt je Live-Stunde"),
`und tragen wirklich ihren Inhalt (${JSON.stringify(event?.texte)})`); `die Aufgabe steht als abhakbarer Punkt da (${JSON.stringify(event?.texte)})`);
/* OHNE AUFZAEHLUNGSZEICHEN GETIPPT -> ABSATZ. Begruendung oben
bei der Messung. Geprueft wird, dass die Regel ANKOMMT, und
daneben, in welcher Form -- ein „irgendwo steht es" waere
auch dann gruen, wenn die Gliederung alles in einen Topf
wirft. */
ok(event?.regelabsaetze.includes("Nur eigenes Konto")
|| event?.regelzeilen.includes("Nur eigenes Konto"),
`die Regel kommt in der Gliederung an (Zeilen `
+ `${JSON.stringify(event?.regelzeilen)}, Absaetze `
+ `${JSON.stringify(event?.regelabsaetze)})`);
ok(event?.regelabsaetze.includes("Nur eigenes Konto"),
" und zwar als Absatz, weil kein Aufzaehlungszeichen davorstand");
/* GEGENPROBE: Sie steht NICHT zwischen den Aufgaben. Ohne sie
waere unbelegt, dass Aufgaben und Regeln getrennt bleiben --
und genau das war der Mangel im Bildschirmfoto vom
07.10.2026. */
ok(!event?.texte.includes("Nur eigenes Konto"),
"und nicht zwischen den abhakbaren Aufgaben");
/* ---- UND DIE REGELN LIEGEN HINTER EINEM DRUCK (07.10.2026) --
Filipe: „die kacheln von den events soll auch nicht so lang
sein, die soll man aufklappen koennen." Hier wird nur geprüft,
DASS es so ist; wie viel Höhe das spart, misst
pruef-eventkarte.mjs in Abschnitt 4b.
DIE ZEILE DARÜBER BLEIBT TROTZDEM GÜLTIG: `hidden` nimmt ein
Element nicht aus dem Baum, der Inhalt ist also vorhanden und
nur zugeklappt. Genau das soll es sein -- verborgen, nicht
verloren. */
ok(event?.klappZu === true,
`die Gliederung steht zugeklappt da (${event?.klappZu})`);
ok(event?.klappWort === "Alle Infos",
`und der Knopf dazu ist beschriftet ("${event?.klappWort}")`);
/* GEGENPROBE dazu: Eine Karte, die KEIN Event ist, darf nichts davon /* GEGENPROBE dazu: Eine Karte, die KEIN Event ist, darf nichts davon
haben. Sonst bewiese der Block oben nur, dass die Elemente haben. Sonst bewiese der Block oben nur, dass die Elemente
+570 -9
View File
@@ -98,11 +98,19 @@ const tag = (v) => tagLokal(v);
der Agentur. Die Pruefung braucht beide Seiten gefuellt -- sonst der Agentur. Die Pruefung braucht beide Seiten gefuellt -- sonst
misst sie eine Trennung, bei der eine Haelfte leer ist, und misst sie eine Trennung, bei der eine Haelfte leer ist, und
„zwei nebeneinander" waere dann zu Recht rot. */ „zwei nebeneinander" waere dann zu Recht rot. */
function evt(titel, von, bis, auf, reg, kampagneId = null) { /* `daten` ist der Auszug, den der Leser beim Einlesen ablegt
(07.10.2026). Daraus baut der Server die Geschenke mit
Coinpreis, den Steckbrief und die Beschreibung je
Missionsgruppe. Ohne ihn waeren diese drei Dinge hier gar
nicht pruefbar -- und ein Feld, das keine Pruefung ansieht,
ist ein Feld, das beim naechsten Umbau still ausfaellt. */
function evt(titel, von, bis, auf, reg, kampagneId = null, daten = null) {
d.prepare("INSERT INTO eintraege (titel, text, bereich, art, status, datum, event_ende," d.prepare("INSERT INTO eintraege (titel, text, bereich, art, status, datum, event_ende,"
+ " event_aufgaben, event_regeln, dringlichkeit, erstellt, erstellt_von, kampagne_id)" + " event_aufgaben, event_regeln, dringlichkeit, erstellt, erstellt_von, kampagne_id,"
+ " VALUES (?,?,'agentur','kampagne','offen',?,?,?,?,'mittel',?,?,?)") + " kampagne_daten)"
.run(titel, "Beschreibung zum Test.", von, bis, auf, reg, jetzt, idDogi, kampagneId); + " VALUES (?,?,'agentur','kampagne','offen',?,?,?,?,'mittel',?,?,?,?)")
.run(titel, "Beschreibung zum Test.", von, bis, auf, reg, jetzt, idDogi, kampagneId,
daten === null ? null : JSON.stringify(daten));
return d.prepare("SELECT last_insert_rowid() AS id").get().id; return d.prepare("SELECT last_insert_rowid() AS id").get().id;
} }
/* Die drei Zustaende, die eventStand kennt -- jeder einzeln, nicht als /* Die drei Zustaende, die eventStand kennt -- jeder einzeln, nicht als
@@ -116,6 +124,66 @@ const kommend = evt("November Challenge", tag(20), tag(45),
"· 30 Live-Stunden", "· Nur eigene Konten"); "· 30 Live-Stunden", "· Nur eigene Konten");
const vorbei = evt("September Rückblick", tag(-40), tag(-12), const vorbei = evt("September Rückblick", tag(-40), tag(-12),
"· 20 Live-Stunden", "· Nur eigene Konten"); "· 20 Live-Stunden", "· Nur eigene Konten");
/* ==== EIN EVENT MIT ECHTER GLIEDERUNG (07.10.2026) ==========
Der Regeltext ist ZEICHENGLEICH mit dem, was am 07.10.2026
wirklich in der Datenbank steht (Eintrag 89, „LIVE Glow Up")
-- nur gekuerzt. Erfundene Testdaten haetten hier genau das
verdeckt, worum es geht: Die Hinweise mit `* ` davor, die
Ueberschrift mit Untertitel hinter einem Gedankenstrich, der
Absatz ohne Zeichen und die Aufzaehlung mit `· `. Jede dieser
vier Zeilensorten sah vorher gleich aus.
ZWEI GESCHENKE, UND EINES OHNE PREIS. Das ist die Gegenprobe
zu einem Fehler, der am 07.10.2026 beim ersten Lauf wirklich
auftrat: `Number(null)` ist 0, und 0 ist endlich -- „Feuerwerk"
bekam dadurch „0 Coins" angeschrieben, also eine falsche
Preisangabe. Mit nur einem Geschenk MIT Preis waere das nie
aufgefallen.
ES LAEUFT UND KOMMT VON TIKTOK: Damit liegt es links in der
Gruppe „Laeuft gerade", die offen steht -- sonst waere alles
darin zugeklappt und nicht messbar. */
const struktText = [
"* Diese Kampagne hat keinen Einleitungstext — der Inhalt steht in den Klapptexten.",
"* Diese Kampagne hat keine Preisliste auf der Seite.",
"",
"SAMMLE DEIN FEUERWERK — Erhalte doppelte Punkte für die Rangliste",
"· Feuerwerk (Sammle Feuerwerk in deinem LIVE!)",
"· Ahorn-Welt — 12000 Coins",
"",
"INFOS ZUR KAMPAGNE",
"LIVE Glow Up ist eine exklusive 4-wöchige Wachstum Challenge.",
"Wofür brauche ich das?",
"Damit du stärkere LIVE-Gewohnheiten aufbaust.",
"",
"### Superstars",
"Du kannst teilnehmen, wenn du eine Einladung hast.",
"",
"KAMPAGNENZEITPLAN",
"· Woche 1: 5. - 11.10.",
"",
"TEILNAHME",
"Länder: DE, CH, AT, LI, LU",
].join("\n");
const strukt = evt("Glow Up Struktur", tag(-2), tag(20),
"Tägliche Mission\nWöchentliche Mission", struktText,
"7690927903777262344", {
geschenke: [
{ id: "1", name: "Feuerwerk", preis: null, hinweis: "Sammle Feuerwerk in deinem LIVE!" },
{ id: "2", name: "Ahorn-Welt", preis: 12000 },
],
aufgabenGruppen: [
{ titel: "Tägliche Mission", beschreibung: "Erfülle sie jeden Tag neu.",
von_tag: "2026-10-05", bis_tag: "2026-10-11" },
],
steckbrief: {
nummer: "7690927903777262344", kurznummer: "1139900164",
region: "EU-TTP2", sprache: "de-DE", verbund: null,
zeitzone: "Europe/Berlin", anmeldung_ab: null, ergebnis_bis: null,
},
});
/* Ein gewoehnlicher Eintrag auf demselben Brett: Er darf sich NICHT /* Ein gewoehnlicher Eintrag auf demselben Brett: Er darf sich NICHT
mitverwandeln. Ohne ihn wuerde "nur Events sind betroffen" hier gar mitverwandeln. Ohne ihn wuerde "nur Events sind betroffen" hier gar
nicht geprueft. */ nicht geprueft. */
@@ -249,8 +317,102 @@ melde("\n=== 1. Die Punkte kommen als Liste vom Server ===");
`mit Leerzeichen getippt -> gleich sauber: ${JSON.stringify(p[2]?.text)}`); `mit Leerzeichen getippt -> gleich sauber: ${JSON.stringify(p[2]?.text)}`);
ok(p.every((x) => /^[0-9a-f]{16}$/.test(x.schluessel || "")) && p.length > 0, ok(p.every((x) => /^[0-9a-f]{16}$/.test(x.schluessel || "")) && p.length > 0,
"jeder Punkt hat einen Schluessel (16 Hexzeichen)"); "jeder Punkt hat einen Schluessel (16 Hexzeichen)");
ok((e?.event_regelpunkte || []).length === 2, /* GEGLIEDERT STATT FLACH (07.10.2026). Hier stand
`2 Regelpunkte (${(e?.event_regelpunkte || []).length})`); `event_regelpunkte` -- eine Liste gleichrangiger Zeilen. Der
Regeltext dieses Events hat keine Ueberschrift, also ist es
EIN Abschnitt ohne Titel mit zwei Punkten. Genau das wird
gemessen: nicht „es kommt irgendetwas", sondern die Form. */
const ab = e?.event_regelabschnitte || [];
ok(ab.length === 1 && ab[0].titel === null && ab[0].zeilen.length === 2,
`ein Abschnitt ohne Titel mit 2 Zeilen: ${JSON.stringify(ab)}`);
ok(ab[0]?.zeilen.every((z) => z.art === "punkt"),
"beide Zeilen sind Punkte, keine Absaetze");
/* DER ROHTEXT KOMMT WEITERHIN MIT -- er ist der dritte Ausgang,
wenn die Gliederung einmal ausfaellt. */
ok(typeof e?.event_regeln === "string" && e.event_regeln.includes("eigenen Konto"),
"der Rohtext steht als Rueckfall weiterhin in der Antwort");
/* ---- Das gegliederte Event: Geschenke, Steckbrief, Gruppen -- */
const s = (daten.eintraege || []).find((x) => x.titel === "Glow Up Struktur");
ok(!!s, "das gegliederte Event ist auf dem Brett");
const sab = s?.event_regelabschnitte || [];
const titel = sab.map((a) => a.titel);
ok(titel.includes("SAMMLE DEIN FEUERWERK") && titel.includes("INFOS ZUR KAMPAGNE")
&& titel.includes("KAMPAGNENZEITPLAN") && titel.includes("TEILNAHME"),
`die vier Ueberschriften sind erkannt: ${JSON.stringify(titel)}`);
const feuer = sab.find((a) => a.titel === "SAMMLE DEIN FEUERWERK");
ok(feuer?.unter === "Erhalte doppelte Punkte für die Rangliste",
`der Untertitel steht getrennt: ${JSON.stringify(feuer?.unter)}`);
ok(feuer?.art === "geschenke", `der Abschnitt ist als Geschenke erkannt (${feuer?.art})`);
/* DIE GEGENPROBE ZUM 0-COINS-FEHLER. `preis: null` darf NICHT
als 0 herauskommen -- „0 Coins" ist eine Preisangabe, und
zwar eine falsche. */
const gaben = (feuer?.zeilen || []).filter((z) => z.art === "geschenk");
ok(gaben.length === 2, `zwei Geschenke (${gaben.length})`);
const ohne = gaben.find((z) => z.text === "Feuerwerk");
const mit = gaben.find((z) => z.text === "Ahorn-Welt");
ok(ohne && (ohne.coins === null || ohne.coins === undefined),
`ein Geschenk ohne Preis hat KEINEN Preis, nicht 0: ${JSON.stringify(ohne)}`);
ok(mit?.coins === 12000, `und eines mit Preis hat ihn: ${JSON.stringify(mit?.coins)}`);
ok(ohne?.zusatz === "Sammle Feuerwerk in deinem LIVE!",
`der Hinweis aus der Geschenkliste kommt mit: ${JSON.stringify(ohne?.zusatz)}`);
/* Die Unterstufe aus einem fremden Text. */
const supers = sab.find((a) => a.titel === "Superstars");
ok(supers?.stufe === 3, `eine Unterueberschrift ist Stufe 3 (${supers?.stufe})`);
ok(sab.find((a) => a.titel === "INFOS ZUR KAMPAGNE")?.stufe === 2,
"und ein eigener Abschnitt Stufe 2");
/* Die Frage mit Antwort darunter. */
const infos = sab.find((a) => a.titel === "INFOS ZUR KAMPAGNE");
ok((infos?.zeilen || []).some((z) => z.art === "frage" && z.text === "Wofür brauche ich das?"),
`die Frage mit Antwort darunter ist erkannt: ${JSON.stringify(infos?.zeilen)}`);
/* GEGENPROBE: Eine Zeile aus nichts als Laendercodes waere
gross geschrieben und wuerde zur Ueberschrift. `regelnText`
schreibt deshalb „Länder: ..." -- und genau das muss ein
ABSATZ bleiben, kein Titel. */
const teil = sab.find((a) => a.titel === "TEILNAHME");
ok((teil?.zeilen || []).some((z) => z.art === "text" && z.text.startsWith("Länder:")),
`„Länder: DE, CH, ..." bleibt ein Absatz: ${JSON.stringify(teil?.zeilen)}`);
ok(!titel.includes("DE, CH, AT, LI, LU"),
"und wird nicht zur Ueberschrift");
/* Die Hinweise stehen in einem eigenen Abschnitt ohne Titel --
nicht zwischen den Regeln. Das war der Mangel im
Bildschirmfoto. */
const hin = sab.find((a) => a.art === "hinweise");
ok(hin && hin.titel === null && hin.zeilen.length === 2
&& hin.zeilen.every((z) => z.art === "hinweis"),
`die zwei Hinweise stehen fuer sich: ${JSON.stringify(hin)}`);
/* Steckbrief und Aufgabenbeschreibung. */
ok(s?.event_steckbrief?.kurznummer === "1139900164",
`der Steckbrief kommt mit: ${JSON.stringify(s?.event_steckbrief)}`);
ok(s?.event_steckbrief?.verbund === undefined,
"ein leeres Feld faellt weg, statt als Leerstelle zu erscheinen");
const tagMission = (s?.event_punkte || []).find((x) => x.text === "Tägliche Mission");
ok(tagMission?.beschreibung === "Erfülle sie jeden Tag neu.",
`die Beschreibung haengt an der Aufgabe: ${JSON.stringify(tagMission)}`);
ok(tagMission?.von_tag === "2026-10-05" && tagMission?.bis_tag === "2026-10-11",
"und ihr Zeitraum dazu");
const woche = (s?.event_punkte || []).find((x) => x.text === "Wöchentliche Mission");
ok(woche && woche.beschreibung === undefined,
"eine Aufgabe ohne Gruppe bekommt kein leeres Feld");
/* GEGENPROBE: Die drei Events OHNE Kampagnendaten bekommen
weder Geschenke noch Steckbrief. Ohne sie waere unbelegt,
dass die Felder aus `kampagne_daten` kommen und nicht
irgendwo erfunden werden. */
ok(e?.event_steckbrief === undefined,
"ein Event ohne Kampagnendaten hat keinen Steckbrief");
ok(!(e?.event_regelabschnitte || []).some((a) => a.art === "geschenke"),
"und keinen Geschenkabschnitt");
ok(e?.kampagne_daten === undefined,
"die Rohspalte selbst geht NICHT an die Oberflaeche");
ok(s?.kampagne_daten === undefined,
"auch nicht beim Event, das sie hat");
ok(e?.event_geschafft === 0, `noch nichts abgehakt (${e?.event_geschafft})`); ok(e?.event_geschafft === 0, `noch nichts abgehakt (${e?.event_geschafft})`);
/* GEGENPROBE: Ein Eintrag, der KEIN Event ist, bekommt keine Punkte. /* GEGENPROBE: Ein Eintrag, der KEIN Event ist, bekommt keine Punkte.
@@ -443,8 +605,16 @@ async function alsRolle(rolle, code, breite = 1280) {
{ {
const { ctx, seite } = await alsRolle("admin", "CODE-DOGI-0001", 1280); const { ctx, seite } = await alsRolle("admin", "CODE-DOGI-0001", 1280);
/* VIER SEIT DEM 07.10.2026: dazu kam „Glow Up Struktur" mit
echter Gliederung. Die Zahl steht hier als Bedingung und
nicht nur im Meldetext -- ein stiller Rueckgang waere sonst
unsichtbar (CLAUDE.md, 28.08.2026).
ZUGEKLAPPTE GRUPPEN ZAEHLEN MIT: „Vorbei" steht von sich aus
zu, aber `hidden` entfernt ein Element nicht aus dem Baum.
Waere das anders, muesste hier erst aufgeklappt werden. */
const karten = await seite.$$('.eintrag-karte[data-eventkarte="ja"]'); const karten = await seite.$$('.eintrag-karte[data-eventkarte="ja"]');
ok(karten.length === 3, `3 Eventkarten (${karten.length}) -- die Schulung ist keine`); ok(karten.length === 4, `4 Eventkarten (${karten.length}) -- die Schulung ist keine`);
/* GEGENPROBE: Die Schulung bleibt eine gewoehnliche Karte. Ohne sie /* GEGENPROBE: Die Schulung bleibt eine gewoehnliche Karte. Ohne sie
waere "nur Events sind umgebaut" unbelegt. */ waere "nur Events sind umgebaut" unbelegt. */
@@ -844,6 +1014,362 @@ async function alsRolle(rolle, code, breite = 1280) {
await ctx.close(); await ctx.close();
} }
/* =======================================================================
4b. SIEHT MAN DIE GLIEDERUNG? (07.10.2026)
Das ist der Abschnitt, der Filipes Bildschirmfoto beantwortet. Und
zwar MESSEND: „es gibt ein h5" beweist nichts -- ein h5, das
genauso aussieht wie der Absatz darunter, ist genau der Zustand,
der beanstandet wurde. Gemessen werden deshalb die berechneten
Werte und ihr UNTERSCHIED.
EIN GRUENER HAKEN, DER NICHTS MISST, IST DAS SCHLIMMERE VON ZWEIEN
(CLAUDE.md, 01.09.2026): Jede Bedingung hier enthaelt die Zahl der
tatsaechlich untersuchten Dinge, und zu jeder Aussage „so sieht es
aus" gehoert eine, die „und so NICHT" sagt.
======================================================================= */
melde("\n=== 4b. Die Gliederung auf dem Bildschirm ===");
{
const { ctx, seite } = await alsRolle("admin", "CODE-DOGI-0001", 1280);
/* ---- Die Kachel ist zu, und das ist der Zweck --------------- */
const zuerst = await seite.evaluate(() => {
const k = [...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')]
.find((x) => x.textContent.includes("Glow Up Struktur"));
if (!k) return { fehlt: true };
const mehr = k.querySelector(".ev-mehr__inhalt");
const knopf = k.querySelector(".ev-mehr__knopf");
return {
hoeheZu: Math.round(k.getBoundingClientRect().height),
verborgen: !!mehr?.hidden,
gedrueckt: knopf?.getAttribute("aria-expanded"),
zahl: k.querySelector(".ev-mehr__zahl")?.textContent.trim() || null,
wort: k.querySelector(".ev-mehr__wort")?.textContent.trim() || null,
/* Was TROTZ zugeklapptem Kasten dasteht. */
gaben: [...k.querySelectorAll(".ev-block--gaben .ev-gabe")].map((g) => ({
name: g.querySelector(".ev-gabe__name")?.textContent.trim(),
coins: g.querySelector(".ev-gabe__coins")?.textContent.trim() || null,
zusatz: g.querySelector(".ev-gabe__zusatz")?.textContent.trim() || null,
})),
hinweise: [...k.querySelectorAll(".ev-hinweise .ev-hinweis")]
.map((h) => h.textContent.trim()),
/* Die Hinweise duerfen NICHT in der Abschnittsliste stehen --
genau das war der Mangel. */
hinweisInRegeln: k.querySelectorAll(".ev-abschnitte .ev-hinweis").length,
zusatz: k.querySelector(".ev-punkt__zusatz")?.textContent.trim() || null,
};
});
melde(" -- zu: " + JSON.stringify(zuerst));
ok(!zuerst.fehlt, "die gegliederte Kachel steht auf dem Brett");
ok(zuerst.verborgen === true && zuerst.gedrueckt === "false",
`der Infokasten ist von sich aus zu (hidden=${zuerst.verborgen}, `
+ `aria-expanded=${zuerst.gedrueckt})`);
ok(zuerst.wort === "Alle Infos" && /Abschnitt/.test(zuerst.zahl || ""),
`und der Knopf sagt, was dahinter steht: "${zuerst.wort} / ${zuerst.zahl}"`);
/* ---- Die Geschenke stehen OHNE Aufklappen da ---------------- */
ok(zuerst.gaben.length === 2,
`zwei Geschenke sind sofort sichtbar (${zuerst.gaben.length})`);
const ahorn = zuerst.gaben.find((g) => g.name === "Ahorn-Welt");
const feuer = zuerst.gaben.find((g) => g.name === "Feuerwerk");
ok(/12\.000/.test(ahorn?.coins || "") && /Coins/.test(ahorn?.coins || ""),
`der Coinpreis steht mit Tausenderpunkt da: ${JSON.stringify(ahorn?.coins)}`);
/* DIE GEGENPROBE ZUM 0-COINS-FEHLER, diesmal auf dem Bildschirm:
Ein Kampagnengeschenk ohne Preis darf KEINE Preisplakette
bekommen. „0 Coins" waere eine falsche Preisangabe. */
ok(feuer && feuer.coins === null,
`ein Geschenk ohne Preis zeigt keinen Preis: ${JSON.stringify(feuer)}`);
ok(feuer?.zusatz === "Sammle Feuerwerk in deinem LIVE!",
`stattdessen steht da, wie man es bekommt: ${JSON.stringify(feuer?.zusatz)}`);
/* ---- Die Hinweise stehen fuer sich ------------------------- */
ok(zuerst.hinweise.length === 2,
`die zwei Hinweise stehen sichtbar in ihrem eigenen Kasten (${zuerst.hinweise.length})`);
ok(zuerst.hinweisInRegeln === 0,
`und KEINER zwischen den Regeln (${zuerst.hinweisInRegeln}) -- das war der Mangel`);
/* ---- Die Beschreibung haengt unter der Aufgabe -------------- */
ok(/Erfülle sie jeden Tag neu/.test(zuerst.zusatz || "")
&& /05\.10\.2026/.test(zuerst.zusatz || ""),
`unter der Aufgabe stehen Beschreibung und Zeitraum: ${JSON.stringify(zuerst.zusatz)}`);
/* ---- Aufklappen: wird die Kachel laenger? ------------------- */
/* DIE RICHTIGE KACHEL (berichtigt 07.10.2026).
Hier stand `.eintrag-karte[data-eventkarte="ja"] .ev-mehr__knopf`
-- das traf die ERSTE Eventkarte („Oktober Event"), nicht die
gegliederte. Gemessen wurde danach eine Kachel, deren Kasten
weiterhin zu war: „spart 0 px", „0 px eingerueckt",
„Untertitel null". Drei rote Zeilen, kein Schaden am Code.
Das ist die Sorte Fehlalarm, die Vertrauen kostet (CLAUDE.md,
26.09.2026) -- deshalb wird die Kachel ueber ihren TITEL
gesucht, nicht ueber ihre Position. */
const knopfNr = await seite.evaluate(() => {
const alle = [...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')];
const i = alle.findIndex((x) => x.textContent.includes("Glow Up Struktur"));
return i;
});
ok(knopfNr >= 0, `die gegliederte Kachel ist Karte Nr. ${knopfNr}`);
await seite.evaluate(() => {
const k = [...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')]
.find((x) => x.textContent.includes("Glow Up Struktur"));
k.querySelector(".ev-mehr__knopf").click();
});
await seite.waitForTimeout(300);
const danach = await seite.evaluate(() => {
const k = [...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')]
.find((x) => x.textContent.includes("Glow Up Struktur"));
const mehr = k.querySelector(".ev-mehr__inhalt");
const lies = (w) => {
const s = getComputedStyle(w);
return {
groesse: Math.round(parseFloat(s.fontSize) * 100) / 100,
gewicht: Number(s.fontWeight),
sperrung: s.letterSpacing,
gross: s.textTransform,
farbe: s.color,
text: w.textContent.trim().slice(0, 40),
};
};
const k2 = k.querySelector('.ev-abschnitt[data-stufe="2"] > .ev-abschnitt__titel');
const k3 = k.querySelector('.ev-abschnitt[data-stufe="3"] > .ev-abschnitt__titel');
const abs = k.querySelector(".ev-abschnitt .ev-absatz");
const frage = k.querySelector(".ev-abschnitt .ev-frage");
return {
hoeheAuf: Math.round(k.getBoundingClientRect().height),
verborgen: !!mehr?.hidden,
titel: [...k.querySelectorAll(".ev-abschnitt__titel")].map((h) => h.textContent.trim()),
stufe2: k2 ? lies(k2) : null,
stufe3: k3 ? lies(k3) : null,
absatz: abs ? lies(abs) : null,
frage: frage ? lies(frage) : null,
/* DER UNTERTITEL DES GESCHENKABSCHNITTS STEHT IM
GESCHENKBLOCK (berichtigt 07.10.2026).
Hier stand `.ev-abschnitt__unter`. „SAMMLE DEIN FEUERWERK
— Erhalte doppelte Punkte" ist aber genau der Abschnitt,
der aus den Regeln HERAUSGENOMMEN und nach oben gezogen
wird (damit die Geschenke ohne Aufklappen sichtbar sind).
Sein Untertitel heisst dort `.ev-gaben__unter`. Die
Pruefung suchte ihn an der Stelle, an der er absichtlich
nicht mehr steht.
BEIDE WERDEN JETZT GEMESSEN: `unter` fuer einen
gewoehnlichen Abschnitt (es gibt hier keinen mit
Untertitel, also null -- das ist in Ordnung) und
`gabenWoher`/`gabenUnter` fuer den, um den es geht. */
unter: k.querySelector(".ev-gaben__unter")?.textContent.trim() || null,
gabenWoher: (() => {
const p = k.querySelector(".ev-gaben__woher");
if (!p) return null;
const u = p.querySelector(".ev-gaben__unter");
const ganz = p.textContent.trim();
return u ? ganz.slice(0, ganz.length - u.textContent.trim().length).trim() : ganz;
})(),
/* DIE EINRUECKUNG WIRD AM TEXT GEMESSEN, NICHT AM KASTEN
(berichtigt 07.10.2026).
Hier stand der Abstand der beiden ABSCHNITTE. Der ist 0,
und das ist richtig: Beide sind volle Kinder derselben
Spalte, die Einrueckung entsteht durch `padding-left` --
sie verschiebt den INHALT, nicht das Rechteck. Gemessen
kam „0 px eingerueckt" heraus, obwohl der Text sichtbar
eingerueckt war.
Was man sehen soll, ist der Text. Also wird der gemessen. */
einzug: (() => {
const h3 = k.querySelector('.ev-abschnitt[data-stufe="3"] > .ev-abschnitt__titel');
const h2 = k.querySelector('.ev-abschnitt[data-stufe="2"] > .ev-abschnitt__titel');
return h3 && h2
? Math.round(h3.getBoundingClientRect().x - h2.getBoundingClientRect().x)
: null;
})(),
steck: [...k.querySelectorAll(".ev-steck__name")].map((x) => x.textContent.trim()),
steckWerte: [...k.querySelectorAll(".ev-steck__wert")].map((x) => x.textContent.trim()),
};
});
melde(" -- auf: " + JSON.stringify(danach).slice(0, 700));
ok(danach.verborgen === false,
"ein Druck macht den Infokasten auf");
/* DAS IST DIE ANTWORT AUF „die kacheln sollen nicht so lang sein":
nicht „sie ist kurz", sondern „sie ist MESSBAR kuerzer als mit
allem offen". Eine Kachel, die zugeklappt genauso hoch ist, hat
nichts gespart. */
ok(danach.hoeheAuf > zuerst.hoeheZu + 80,
`zugeklappt spart die Kachel ${danach.hoeheAuf - zuerst.hoeheZu} px `
+ `(${zuerst.hoeheZu} statt ${danach.hoeheAuf})`);
/* ---- Und jetzt: SIEHT man die Ueberschrift? ----------------- */
ok(danach.titel.length >= 4,
`${danach.titel.length} Ueberschriften stehen da: ${JSON.stringify(danach.titel.slice(0, 6))}`);
ok(!!danach.stufe2 && !!danach.absatz,
"Ueberschrift und Absatz sind beide da (sonst misst der Vergleich nichts)");
/* DREI UNTERSCHIEDE, NICHT EINER. Ein einziger (etwa nur die
Farbe) wuerde bei einem anderen Farbschema wieder verschwinden. */
const u2 = danach.stufe2 || {};
const ua = danach.absatz || {};
const unterschiede = [
["Gewicht", u2.gewicht, ua.gewicht],
["Sperrung", u2.sperrung, ua.sperrung],
["Grossschreibung", u2.gross, ua.gross],
["Farbe", u2.farbe, ua.farbe],
].filter(([, a, b]) => a !== b);
ok(unterschiede.length >= 3,
`die Ueberschrift unterscheidet sich in ${unterschiede.length} von 4 Merkmalen `
+ `vom Absatz: ${JSON.stringify(unterschiede)}`);
ok(u2.gewicht >= 700 && ua.gewicht < 700,
`sie ist fett, der Absatz nicht (${u2.gewicht} gegen ${ua.gewicht})`);
ok(u2.gross === "uppercase" && ua.gross === "none",
`sie steht in Grossbuchstaben, der Absatz nicht (${u2.gross} / ${ua.gross})`);
/* Die Unterstufe sieht anders aus als die Oberstufe UND anders
als ein Absatz -- sonst waere die zweite Stufe keine. */
ok(!!danach.stufe3, "eine Unterueberschrift ist da");
const u3 = danach.stufe3 || {};
ok(u3.gross === "none" && u3.gewicht >= 600,
`die Unterstufe ist halbfett und NICHT in Grossbuchstaben `
+ `(${u3.gewicht}, ${u3.gross})`);
ok(u3.gewicht > ua.gewicht,
`aber immer noch kraeftiger als ein Absatz (${u3.gewicht} gegen ${ua.gewicht})`);
ok(danach.einzug >= 8,
`und sie ist ${danach.einzug} px eingerueckt (Grenze 8)`);
/* Der Untertitel hinter dem Gedankenstrich steht getrennt -- und
die Ueberschrift der Kampagne bleibt erhalten. „Sammle dein
Feuerwerk" sagt, WARUM diese Geschenke zaehlen; ein selbst
erfundenes „Geschenke" daneben haette das verschluckt. */
ok(danach.gabenWoher === "SAMMLE DEIN FEUERWERK",
`die Ueberschrift der Kampagne steht ueber den Geschenken: `
+ `${JSON.stringify(danach.gabenWoher)}`);
ok(danach.unter === "Erhalte doppelte Punkte für die Rangliste",
`der Untertitel steht als eigene Zeile: ${JSON.stringify(danach.unter)}`);
/* Die Frage ist kraeftiger als der Absatz unter ihr. */
ok(!!danach.frage && danach.frage.gewicht > ua.gewicht,
`eine Frage mit Antwort darunter steht kraeftiger da `
+ `(${danach.frage?.gewicht} gegen ${ua.gewicht}): ${JSON.stringify(danach.frage?.text)}`);
/* Der Steckbrief -- deutsche Namen, keine Rohfelder. */
ok(danach.steck.includes("Kurznummer") && danach.steck.includes("Markt"),
`der Steckbrief nennt die Felder auf Deutsch: ${JSON.stringify(danach.steck)}`);
ok(danach.steckWerte.includes("1139900164"),
`und zeigt die Werte: ${JSON.stringify(danach.steckWerte)}`);
ok(!danach.steck.some((n) => /_|activity|flag/i.test(n)),
"kein Rohfeldname steht auf der Kachel");
/* ---- Die eigene Wahl haelt das Neuladen aus ---------------- */
await seite.reload({ waitUntil: "networkidle" });
await seite.waitForSelector(".ev-mehr__knopf", { timeout: 15000 });
await seite.waitForTimeout(600);
const nachher = await seite.evaluate(() => {
const k = [...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')]
.find((x) => x.textContent.includes("Glow Up Struktur"));
return { verborgen: !!k?.querySelector(".ev-mehr__inhalt")?.hidden };
});
/* WARUM DAS WICHTIG IST: Das Brett baut sich bei jeder
Aktualisierung neu auf. Eine Kachel, die dabei zuklappt,
waehrend man liest, ist aergerlicher als eine lange. */
ok(nachher.verborgen === false,
"aufgeklappt bleibt aufgeklappt, auch nach dem Neuladen");
/* ---- Die Gruppen der Spalten ------------------------------- */
const gruppen = await seite.evaluate(() => {
const seiten = [...document.querySelectorAll(".kv-seite")].map((s) => ({
seite: s.dataset.seite,
gruppen: [...s.querySelectorAll(".ev-gruppe__kopf")].map((g) => ({
name: g.dataset.gruppe,
wort: g.querySelector(".ev-gruppe__wort")?.textContent.trim(),
zahl: g.querySelector(".ev-gruppe__zahl")?.textContent.trim(),
zu: g.nextElementSibling?.hidden === true,
})),
}));
return { seiten };
});
melde(" -- Gruppen: " + JSON.stringify(gruppen));
const alleGruppen = gruppen.seiten.flatMap((s) => s.gruppen);
ok(alleGruppen.length >= 3,
`${alleGruppen.length} Gruppen stehen in den Spalten`);
const laeuft = alleGruppen.filter((g) => g.name === "laeuft");
const vorbei = alleGruppen.filter((g) => g.name.startsWith("vorbei"));
ok(laeuft.length >= 1 && laeuft.every((g) => !g.zu),
`„Laeuft gerade" steht offen (${laeuft.length})`);
ok(vorbei.length >= 1 && vorbei.every((g) => g.zu),
`und Vergangenes zu (${vorbei.length}: `
+ `${vorbei.map((g) => g.wort).join(", ")})`);
/* KEINE LEERE GRUPPE. Fuenf Ueberschriften ueber nichts waeren
schlimmer als eine lange Spalte. */
ok(alleGruppen.every((g) => Number(g.zahl) > 0),
`jede Gruppe hat Eintraege: ${JSON.stringify(alleGruppen.map((g) => g.zahl))}`);
/* UND DIE SUMME STIMMT: Keine Karte faellt zwischen zwei Gruppen
durch. Ohne diese Zeile waere „es gibt Gruppen" vereinbar damit,
dass ein Event in keiner davon auftaucht. */
const summe = alleGruppen.reduce((s, g) => s + Number(g.zahl), 0);
ok(summe === 4, `alle 4 Events stehen in genau einer Gruppe (Summe ${summe})`);
/* GEGENPROBE: Links und rechts teilen sich den Zustand NICHT.
Dieser Fehler war bei den Zeitstufen schon einmal da.
ERST DRUECKEN, DANN NACHSEHEN (berichtigt 07.10.2026). Vorher
stand hier `... || merker.length === 0` -- und weil nie
jemand eine Gruppe angefasst hatte, war die Liste leer und
die Zeile gruen, ohne etwas gemessen zu haben. Genau die
Sorte Haken, die immer bestaetigt (CLAUDE.md, 01.09.2026).
Jetzt wird eine Gruppe LINKS zugeklappt und danach geprueft,
dass (1) ein Merker entstanden ist, (2) er die Seite nennt
und (3) die gleichnamige Gruppe RECHTS davon unberuehrt ist. */
const vorher = await seite.evaluate(() => {
const links = document.querySelector('.kv-seite[data-seite="tiktok"] .ev-gruppe__kopf');
const name = links?.dataset.gruppe;
const rechts = [...document.querySelectorAll('.kv-seite[data-seite="agentur"] .ev-gruppe__kopf')]
.find((g) => g.dataset.gruppe === name);
links?.click();
return {
name,
linksZu: links?.nextElementSibling?.hidden === true,
rechtsDa: !!rechts,
rechtsZu: rechts ? rechts.nextElementSibling?.hidden === true : null,
};
});
const merker = await seite.evaluate(() => Object.keys(localStorage)
.filter((s) => s.startsWith("abschnitt:agentur:")));
melde(" -- nach dem Druck: " + JSON.stringify({ ...vorher, merker }));
ok(merker.length > 0 && merker.every((s) => /:(tiktok|agentur):/.test(s)),
`der Merker nennt die Seite: ${JSON.stringify(merker)}`);
/* DIESE ZEILE WAR SELBST FALSCH (berichtigt 07.10.2026).
Hier stand `!s.includes(":agentur:")`. Der Merker heisst aber
`abschnitt:agentur:tiktok:laeuft` -- und „agentur" darin ist der
BRETTNAME, nicht die Seite. Die Zeile war damit nie erfuellbar.
Zwei Dinge heissen gleich und sind verschieden; genau dieser
Fehler stand am 07.10.2026 schon einmal im Projekt (das Feld
„Gilt für" nennt das HAUS, nicht das Brett).
Gemessen wird jetzt die Form des Schluessels GANZ: genau einer,
und er nennt die angefasste Seite. Das ist die Aussage, auf die
es ankommt -- der Zustand ist je Seite abgelegt, also kann ein
Druck links rechts nichts bewirken. */
ok(merker.length === 1 && merker[0] === `abschnitt:agentur:tiktok:${vorher.name}`,
`genau ein Merker, fuer die angefasste Seite: ${JSON.stringify(merker)}`);
/* UND DIE ANDERE SEITE STEHT NOCH SO DA WIE VORHER. Gemessen an
der Gruppe, die es dort gibt -- „Kommt noch" war offen und muss
offen bleiben, obwohl links gerade zugeklappt wurde. */
const andereSeite = await seite.evaluate(() => {
const g = [...document.querySelectorAll('.kv-seite[data-seite="agentur"] .ev-gruppe__kopf')]
.map((x) => ({ name: x.dataset.gruppe, zu: x.nextElementSibling?.hidden === true }));
return g;
});
const kommt = andereSeite.find((g) => g.name === "kommt");
ok(!!kommt && kommt.zu === false,
`rechts ist nichts mitgeklappt: ${JSON.stringify(andereSeite)}`);
ok(vorher.linksZu === true,
`die angefasste Gruppe links ist zu (${vorher.name})`);
await ctx.close();
}
melde("\n=== 5. Abhaken im Browser, mit Neuladen ==="); melde("\n=== 5. Abhaken im Browser, mit Neuladen ===");
{ {
const { ctx, seite } = await alsRolle("creator", "CODE-CREA-0001", 1280); const { ctx, seite } = await alsRolle("creator", "CODE-CREA-0001", 1280);
@@ -1037,11 +1563,42 @@ melde("\n=== 7. Auf dem Handy (412 px) ===");
199 px breit. Dass die Regel UMSCHALTET, muss gemessen werden; 199 px breit. Dass die Regel UMSCHALTET, muss gemessen werden;
sonst beweist „zwei nebeneinander" am Rechner nur, dass es dort sonst beweist „zwei nebeneinander" am Rechner nur, dass es dort
passt, und nichts darueber, was auf dem Telefon passiert. */ passt, und nichts darueber, was auf dem Telefon passiert. */
/* GEMESSEN WIRD, WAS MAN SIEHT (berichtigt 07.10.2026).
Hier standen ALLE Eventkarten. Seit die Spalten nach Zustand
und Alter gegliedert sind, steht „Vorbei — aelter" von sich
aus zugeklappt -- und eine Karte in einem `hidden`-Kasten hat
ein Rechteck von 0x0 an Position 0,0. Gemessen kam
„379, 379, 379, 379, 0 px" heraus, und die Pruefung meldete
eine zu schmale Karte, obwohl keine zu schmal war.
ZWEI SCHAEDEN, NICHT EINER: Die Paarprobe daneben haette bei
ZWEI zugeklappten Karten „zwei auf gleicher Hoehe" gemeldet --
beide liegen auf y=0. Das waere ein Fehlalarm, der erst
auftritt, wenn ein zweites Event vorbeigelaufen ist, also
irgendwann von selbst. Solche Fehlalarme kosten das Vertrauen
in den ganzen Satz (CLAUDE.md, 26.09.2026).
UND DAS VERSCHWINDEN WIRD SELBST GEPRUEFT: Eine unsichtbare
Karte darf nur deshalb unsichtbar sein, weil ihre Gruppe zu
ist. Ohne diese zweite Haelfte haette „wir messen nur die
sichtbaren" genau den Fall weggeschaut, in dem eine Karte
wirklich fehlt. */
const schmal = await seite.evaluate(() => { const schmal = await seite.evaluate(() => {
const k = [...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')] const alle = [...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')]
.map((x) => { const r = x.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), b: Math.round(r.width) }; }); .map((x) => {
const r = x.getBoundingClientRect();
const gruppe = x.closest(".ev-gruppe__inhalt");
return {
x: Math.round(r.x), y: Math.round(r.y), b: Math.round(r.width),
sichtbar: r.width > 0 && r.height > 0,
inZu: !!(gruppe && gruppe.hidden),
};
});
const k = alle.filter((a) => a.sichtbar);
return { return {
karten: k, karten: k,
versteckt: alle.filter((a) => !a.sichtbar),
/* Stehen zwei auf gleicher Hoehe? Dann waere nicht umgeschaltet. */ /* Stehen zwei auf gleicher Hoehe? Dann waere nicht umgeschaltet. */
paar: k.some((a, i) => k.slice(i + 1).some((b2) => Math.abs(a.y - b2.y) < 5)), paar: k.some((a, i) => k.slice(i + 1).some((b2) => Math.abs(a.y - b2.y) < 5)),
quer: document.documentElement.scrollWidth - document.documentElement.clientWidth, quer: document.documentElement.scrollWidth - document.documentElement.clientWidth,
@@ -1051,6 +1608,10 @@ melde("\n=== 7. Auf dem Handy (412 px) ===");
`auf 412 px steht eine Karte je Reihe (${schmal.karten.map((a) => `${a.b}px@${a.y}`).join(", ")})`); `auf 412 px steht eine Karte je Reihe (${schmal.karten.map((a) => `${a.b}px@${a.y}`).join(", ")})`);
ok(schmal.karten.every((a) => a.b > 330), ok(schmal.karten.every((a) => a.b > 330),
`und sie ist breit genug zum Bedienen (${schmal.karten.map((a) => a.b).join(", ")} px)`); `und sie ist breit genug zum Bedienen (${schmal.karten.map((a) => a.b).join(", ")} px)`);
ok(schmal.versteckt.every((a) => a.inZu),
`was man nicht sieht, steckt in einer zugeklappten Gruppe `
+ `(${schmal.versteckt.length} verborgen, davon `
+ `${schmal.versteckt.filter((a) => a.inZu).length} erklaert)`);
ok(schmal.quer <= 1, ok(schmal.quer <= 1,
`die Seite laesst sich nicht seitlich schieben (${schmal.quer} px)`); `die Seite laesst sich nicht seitlich schieben (${schmal.quer} px)`);
/* UND WENN DOCH, DANN WELCHES ELEMENT? Eine Zahl allein schickt /* UND WENN DOCH, DANN WELCHES ELEMENT? Eine Zahl allein schickt
+365 -2
View File
@@ -149,7 +149,8 @@ for (const [name, s] of Object.entries(SEITEN)) {
const { const {
leseKampagne, regelnText, aufgabenText, tagInZone, textAusHtml, leseKampagne, regelnText, aufgabenText, tagInZone, textAusHtml,
KampagneUnlesbar, datenAusSeite, KampagneUnlesbar, datenAusSeite, gliederung, deutschesDatum, zeitOderNull,
geruestVon, bausteine,
} = await import("./kampagne-lesen.mjs"); } = await import("./kampagne-lesen.mjs");
/* ===================================================================== /* =====================================================================
@@ -394,9 +395,50 @@ melde("=== 4. Was in event_aufgaben und event_regeln landet ===");
ok(r.includes("1. Liga") && r.includes("2. Liga") && r.includes("5. Liga"), ok(r.includes("1. Liga") && r.includes("2. Liga") && r.includes("5. Liga"),
"die Ligen sind durchnummeriert (1. bis 5.)"); "die Ligen sind durchnummeriert (1. bis 5.)");
{ {
/* `### ` DAVOR SEIT DEM 07.10.2026 -- und das ist der Zweck.
Hier stand `=== "3. Liga"`. Auf der Seite ist ein
Listenpunkt, dessen GANZER Inhalt hervorgehoben ist, eine
Überschrift mit Nummer: Darunter steht jeweils die Bedingung
für diese Liga. Flach ausgegeben standen zwölf
gleichrangige Zeilen da, und die Frage „ab wie vielen
Diamanten bin ich in der 2. Liga" war daran nicht mehr zu
beantworten -- obwohl die Antwort danebenstand.
DIE NUMMER BLEIBT DABEI (`<ol start>`), und das
`&nbsp;` hinter `</strong>` darf nicht stören -- die fünfte
Liga hat eines, die ersten vier nicht. */
const rohHtml = "<ol start=\"3\"><li><strong>Liga&nbsp;</strong></li></ol>"; const rohHtml = "<ol start=\"3\"><li><strong>Liga&nbsp;</strong></li></ol>";
ok(textAusHtml(rohHtml) === "3. Liga", ok(textAusHtml(rohHtml) === "### 3. Liga",
`Gegenprobe am Baustein selbst: ${JSON.stringify(textAusHtml(rohHtml))}`); `Gegenprobe am Baustein selbst: ${JSON.stringify(textAusHtml(rohHtml))}`);
const mitNbsp = "<ol start=\"5\"><li><strong>Liga&nbsp;</strong>&nbsp;</li></ol>";
ok(textAusHtml(mitNbsp) === "### 5. Liga",
`auch mit geschütztem Leerzeichen dahinter: ${JSON.stringify(textAusHtml(mitNbsp))}`);
/* GEGENPROBE: Ein Listenpunkt mit gewöhnlichem Text wird KEINE
Überschrift. Ohne sie wäre unbelegt, dass die Hervorhebung
entscheidet und nicht einfach jede Zeile eine Überschrift
wird. */
const schlicht = "<ol start=\"2\"><li>Nur Text, nicht fett</li></ol>";
ok(textAusHtml(schlicht) === "2. Nur Text, nicht fett",
`ein gewöhnlicher Punkt bleibt ein Punkt: ${JSON.stringify(textAusHtml(schlicht))}`);
/* Und ein Absatz, dessen ganzer Inhalt hervorgehoben ist,
ebenfalls -- so stehen die Ligennamen auf der Seite. */
const absatz = "<p style=\"text-align: center;\"><strong>Superstars</strong></p>"
+ "<p>Du kannst teilnehmen, wenn ...</p>";
/* MIT LEERZEILE DAZWISCHEN -- so soll es sein. Die
Überschrift bekommt ihren eigenen Umbruch, der Absatz
seinen; `gliederung()` wirft leere Zeilen ohnehin weg, und
im Rohtext, den DogFather beim Bearbeiten sieht, liest sich
das sauber. Meine Erwartung von EINEM Umbruch war
geraten. */
ok(textAusHtml(absatz) === "### Superstars\n\nDu kannst teilnehmen, wenn ...",
`ein Absatz nur aus Hervorhebung wird Überschrift: ${JSON.stringify(textAusHtml(absatz))}`);
/* GEGENPROBE dazu: Eine Frage MIT hervorgehobenem Wort ist auch
der Sache nach eine Überschrift -- aber das zu behaupten wäre
geraten. Sie bleibt ein Absatz. */
const gemischt = "<p>Um was geht es bei <strong>Gipfelstürmer?</strong></p>";
ok(textAusHtml(gemischt) === "Um was geht es bei Gipfelstürmer?",
`eine Frage mit fettem Wort bleibt ein Absatz: ${JSON.stringify(textAusHtml(gemischt))}`);
ok(rohHtml.replace(/<[^>]+>/g, "").trim() === "Liga&nbsp;", ok(rohHtml.replace(/<[^>]+>/g, "").trim() === "Liga&nbsp;",
"Gegenprobe: bloßes Markenentfernen verliert die Nummer"); "Gegenprobe: bloßes Markenentfernen verliert die Nummer");
} }
@@ -831,6 +873,327 @@ melde("=== 12. Gleiche Eingabe, gleiches Ergebnis ===");
`${gelesen} der fünf Seiten lesen sich, ${geworfen} wirft (die kaputte)`); `${gelesen} der fünf Seiten lesen sich, ${geworfen} wirft (die kaputte)`);
} }
/* =====================================================================
13. DIE RANGLISTE, DER STECKBRIEF UND DIE MISSIONSGRUPPEN
Drei Bausteine, die in allen vier gespeicherten Seiten dastanden
und bis zum 07.10.2026 nie gelesen wurden. Filipe: „ich will alle
informationen die möglich sind wenn man den link hochlädt."
===================================================================== */
melde("");
melde("=== 13. Rangliste, Steckbrief, Missionsgruppen ===");
{
const g = leseKampagne(seite.gipfel);
const r = g.kuer.rangliste;
ok(!!r, "die Rangliste wird gelesen");
/* DER WERTUNGSZEITRAUM IST NICHT DER KAMPAGNENZEITRAUM -- genau
deshalb lohnt das Lesen. Bei „Gipfelstürmer" sind beide gleich,
bei „LIVE Glow Up" nicht; die Gegenprobe steht unten. */
ok(r.von_tag === "2026-10-04" && r.bis_tag === "2026-10-07",
`gewertet wird ${r.von_tag} bis ${r.bis_tag}`);
ok(r.reiter.length === 6
&& r.reiter[0] === "Superstars" && r.reiter[5] === "5. Liga",
`sechs Gruppen, aus dem Wörterbuch aufgelöst: ${JSON.stringify(r.reiter)}`);
ok(r.punkteVerborgen === false,
`die Punktzahl ist hier sichtbar (${r.punkteVerborgen})`);
ok(typeof r.beschreibung === "string" && r.beschreibung.length > 0,
`und die Wertung ist beschriftet: ${JSON.stringify(r.beschreibung)}`);
/* Der Steckbrief: nur Felder, die man ohne Deutung zeigen kann. */
const s = g.kuer.steckbrief;
ok(s.kurznummer === "1140116740", `Kurznummer ${s.kurznummer}`);
ok(s.region === "EU-TTP2", `Markt ${s.region}`);
ok(s.sprache === "de-DE", `Sprache ${s.sprache}`);
ok(s.zeitzone === "Europe/Berlin", `Zeitzone ${s.zeitzone}`);
/* LEER IST NICHT „—": `union_name` steht in allen vier gemessenen
Seiten leer und wird deshalb zu null. */
ok(s.verbund === null, `der leere Verbund wird null, nicht "" (${JSON.stringify(s.verbund)})`);
/* ANMELDUNG UND ERGEBNIS NUR BEI ABWEICHUNG. Sie sind hier gleich
dem Kampagnenzeitraum; sie trotzdem zu zeigen hieße, dieselbe
Zahl dreimal hinzuschreiben. */
ok(s.anmeldung_ab === null && s.ergebnis_bis === null,
"Anmeldung und Ergebnis fallen weg, weil sie nichts Neues sagen");
/* GEGENPROBE: Die Rohwerte sind sehr wohl da -- sonst wäre
„fällt weg" nicht von „wurde nie gelesen" zu unterscheiden. */
const rohAktion = datenAusSeite(seite.gipfel).globalData.activityInfo;
ok(zeitOderNull(rohAktion.warm_up_start_time) === g.auszug.start_ms,
`Gegenprobe: warm_up_start_time steht da und ist gleich dem Beginn `
+ `(${rohAktion.warm_up_start_time})`);
/* Kein Rohfeld, das niemand lesen kann, rutscht in den
Steckbrief -- die stehen weiter im Auszug, nicht auf der
Kachel. */
ok(!Object.keys(s).some((k) => /activity_type|kind_flag|platform|sdlc|biz/.test(k)),
`nur erklärbare Felder im Steckbrief: ${JSON.stringify(Object.keys(s))}`);
/* ---- Die zweite Vorlage: Missionsgruppen mit Beschreibung ---- */
const h = leseKampagne(seite.aufgabengruppe);
const gr = h.kuer.aufgabenGruppen;
/* ZWEI, NICHT VIER -- und das ist der Zweck der Entdopplung.
In `taskItemList` stehen VIER Einträge; die Vorlage legt je
Reiter zwei an (einen für Creator, einen für Zuschauer), und
beide zeigen auf denselben Wörterbucheintrag. Nachgemessen:
die Platzhalter `..._7_...` und `..._8_...` enden auf
derselben Prüfsumme, also auf demselben Text. Zweimal
derselbe Haken wäre ein Haken an zwei Stellen.
Meine Erwartung von vier war die ROHZAHL, nicht das
Ergebnis. Gemessen wird deshalb beides: vier hinein, zwei
heraus -- sonst wäre „zwei" auch dann grün, wenn die Hälfte
gar nicht gelesen würde. */
/* GEZÄHLT WIRD AM BAUSTEIN, nicht im Text gesucht (berichtigt
07.10.2026). Mein erster Versuch suchte den Platzhalternamen
`taskFormData_displayInfo_title` im ganzen Schema und fand ihn
ACHTMAL -- er steht dort noch an anderen Stellen. Eine Zahl aus
einer Textsuche ist keine Messung der Liste; gezählt wird
`taskItemList` selbst, also genau das, was der Leser liest. */
const rohTeile = bausteine(geruestVon(datenAusSeite(seite.aufgabengruppe)).geruest);
const rohListe = (rohTeile.get("live_task_group") || [])[0]?.props?.taskItemList || [];
ok(rohListe.length === 4, `vier Einträge stehen in der Seite (${rohListe.length})`);
ok(gr.length === 2, `und zwei verschiedene Missionsgruppen bleiben (${gr.length})`);
ok(new Set(gr.map((x) => x.titel)).size === gr.length,
`keine zwei mit demselben Titel: ${JSON.stringify(gr.map((x) => x.titel))}`);
ok(gr.every((x) => typeof x.beschreibung === "string" && x.beschreibung.length > 3),
`jede mit eigener Beschreibung: ${JSON.stringify(gr.map((x) => x.beschreibung.slice(0, 28)))}`);
ok(gr.every((x) => /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(x.von_tag || "")),
`und mit eigenem Zeitraum: ${JSON.stringify(gr.map((x) => `${x.von_tag}..${x.bis_tag}`))}`);
/* DIE HAKENLISTE IST ENTDOPPELT, DIE GRUPPENLISTE DECKUNGSGLEICH.
Die Vorlage legt je Reiter zwei Einträge an (Creator und
Zuschauer); zweimal derselbe Haken wäre ein Haken an zwei
Stellen. */
ok(h.kuer.aufgaben.length === gr.length
&& h.kuer.aufgaben.every((a, i) => a === gr[i].titel),
`Aufgaben und Gruppen passen Zeile für Zeile zusammen `
+ `(${h.kuer.aufgaben.length} / ${gr.length})`);
/* ---- WAS DIE RANGLISTE WIRKLICH BEITRÄGT ------------------
Hier stand „bei LIVE Glow Up beginnt die Wertung später als
die Kampagne". Das ist nachgemessen FALSCH: In allen drei
Seiten mit Rangliste ist der Wertungszeitraum auf die
Millisekunde gleich dem Kampagnenzeitraum. Ich hatte zwei
verschiedene Kampagnen verglichen. Die ausführliche
Berichtigung steht bei `rangBaustein` in kampagne-lesen.mjs.
GEMESSEN WIRD JETZT, WAS STIMMT -- und zwar beides: dass die
Zeiträume hier gleich sind (damit niemand wieder etwas
anderes behauptet) UND dass der Baustein trotzdem etwas
beiträgt, das sonst nirgends steht: die Gruppen. */
const gleich = [seite.gipfel, seite.ohnePreise, seite.aufgabengruppe]
.map((x) => leseKampagne(x))
.filter((x) => x.kuer.rangliste)
.map((x) => x.kuer.rangliste.von === x.auszug.start_ms
&& x.kuer.rangliste.bis === x.auszug.ende_ms);
ok(gleich.length === 3 && gleich.every(Boolean),
`in allen ${gleich.length} Seiten mit Rangliste ist die Wertung `
+ "gleich dem Kampagnenzeitraum -- gemessen, nicht angenommen");
/* DAS IST DER GEWINN: zwei Gruppen hier, sechs bei
Gipfelstürmer. „Gegen wen trete ich an" stand vorher
nirgends auf der Kachel. */
ok(h.kuer.rangliste.reiter.length === 2
&& g.kuer.rangliste.reiter.length === 6,
`die Gruppen unterscheiden sich sehr wohl: `
+ `${JSON.stringify(h.kuer.rangliste.reiter)} gegen `
+ `${g.kuer.rangliste.reiter.length} Ligen`);
/* UND IM REGELTEXT STEHT ES AUCH. Ohne diese Zeile wäre
geprüft, dass gelesen wird -- nicht, dass es ankommt. */
const rt = regelnText(h.kuer, h.hinweise);
ok(/## RANGLISTE/.test(rt) && /Gruppen: /.test(rt),
"und die Rangliste steht als eigener Abschnitt im Regeltext");
/* Der zweite Bildbaustein. */
ok(h.auszug.weitere_bilder.length === 2,
`zwei weitere Bilder aus live_banner (${h.auszug.weitere_bilder.length})`);
ok(h.auszug.weitere_bilder.every((u) => /^https:\/\//.test(u)),
"und beide mit absoluter Adresse");
/* GEGENPROBE: Geholt wird trotzdem nur EINES -- das Banner bleibt
das aus live_top_image. */
ok(h.kuer.bannerUrl !== h.auszug.weitere_bilder[0],
"das Banner kommt weiterhin aus live_top_image, nicht von dort");
/* Und das deutsche Datum. */
ok(deutschesDatum("2026-11-04") === "04.11.2026",
`deutschesDatum: ${deutschesDatum("2026-11-04")}`);
ok(deutschesDatum("Unsinn") === null, "und Unsinn gibt nichts");
}
/* =====================================================================
14. DIE GLIEDERUNG DES REGELTEXTES
Filipe, mit Bildschirmfoto: „man soll die titel in den texten
besser erkennen und sehen was zu was passt."
Geprüft wird hier die REGEL, nicht die Anzeige -- was auf dem
Bildschirm daraus wird, misst pruef-eventkarte.mjs, Abschnitt 4b.
===================================================================== */
melde("");
melde("=== 14. Der Regeltext, gegliedert ===");
{
/* ZEICHENGLEICH MIT DEM, WAS AM 07.10.2026 WIRKLICH IN DER
DATENBANK STAND (Eintrag 89, „LIVE Glow Up"). Erfundene
Testdaten hätten genau das verdeckt, worum es geht. */
const echt = [
"* Diese Kampagne hat keinen Einleitungstext — der Inhalt steht in den Klapptexten.",
"* Diese Kampagne hat keine Preisliste auf der Seite.",
"",
"SAMMLE DEIN FEUERWERK — Erhalte doppelte Punkte für die Rangliste",
"· Feuerwerk (Sammle Feuerwerk in deinem LIVE!)",
"",
"INFOS ZUR KAMPAGNE",
"LIVE Glow Up ist eine exklusive 4-wöchige Wachstum Challenge.",
"",
"KAMPAGNENZEITPLAN",
"· Woche 1: 5. - 11.10.",
"",
"TEILNAHME",
"Länder: DE, CH, AT, LI, LU",
].join("\n");
const ab = gliederung(echt, {
geschenke: [{ name: "Feuerwerk", preis: null, hinweis: "Sammle Feuerwerk in deinem LIVE!" }],
});
const titel = ab.map((a) => a.titel);
ok(ab.length === 5,
`fünf Abschnitte aus dem echten Text (${ab.length}): ${JSON.stringify(titel)}`);
/* ---- Die Überschrift wird an der Großschreibung erkannt ---- */
ok(titel[1] === "SAMMLE DEIN FEUERWERK",
`die Überschrift ohne ihren Untertitel: ${JSON.stringify(titel[1])}`);
ok(ab[1].unter === "Erhalte doppelte Punkte für die Rangliste",
`der Untertitel getrennt dahinter: ${JSON.stringify(ab[1].unter)}`);
/* ---- Die vier Zeilensorten ---------------------------------- */
const arten = ab.flatMap((a) => a.zeilen.map((z) => z.art));
ok(new Set(arten).size >= 3 && arten.includes("hinweis")
&& arten.includes("geschenk") && arten.includes("punkt") && arten.includes("text"),
`alle vier Zeilensorten kommen vor: ${JSON.stringify([...new Set(arten)])}`);
/* ---- Die Hinweise stehen für sich, nicht zwischen den Regeln - */
ok(ab[0].titel === null && ab[0].art === "hinweise" && ab[0].zeilen.length === 2,
`die zwei Hinweise bilden den ersten, titellosen Abschnitt`);
ok(!ab.slice(1).some((a) => a.zeilen.some((z) => z.art === "hinweis")),
"und keiner steckt in einem anderen Abschnitt -- das war der Mangel");
/* ---- Das Geschenk kommt AUS DER LISTE, nicht aus dem Text --- */
const gabe = ab[1].zeilen.find((z) => z.art === "geschenk");
ok(gabe?.text === "Feuerwerk", `der Name: ${JSON.stringify(gabe?.text)}`);
/* `Number(null)` IST 0 -- und „0 Coins" wäre eine Preisangabe,
und zwar eine falsche. Dieser Fehler war am 07.10.2026 wirklich
da, an drei Stellen. */
ok(gabe && gabe.coins === null,
`ohne Preis bleibt ohne Preis, nicht 0: ${JSON.stringify(gabe?.coins)}`);
ok(gabe?.zusatz === "Sammle Feuerwerk in deinem LIVE!",
`stattdessen steht da, wie man es bekommt: ${JSON.stringify(gabe?.zusatz)}`);
ok(ab[1].art === "geschenke",
`der Abschnitt mit einem Geschenk ist ein Geschenkabschnitt (${ab[1].art})`);
/* GEGENPROBE: Ohne Geschenkliste ist dieselbe Zeile ein
gewöhnlicher Punkt. Sonst wäre unbelegt, dass die LISTE
entscheidet. */
const ohne = gliederung(echt);
const z1 = ohne[1].zeilen.find((z) => z.text.startsWith("Feuerwerk"));
ok(z1?.art === "punkt",
`ohne Liste ist es ein Punkt, kein Geschenk (${z1?.art})`);
/* Und der Coinpreis aus dem TEXT greift, wo es keine Liste gibt --
für von Hand getippte Zeilen. */
const vonHand = gliederung("## GESCHENKE\n· Rose — 1 Coin\n· Löwe — 29999 Coins");
const rose = vonHand[0].zeilen.find((z) => z.text === "Rose");
const loewe = vonHand[0].zeilen.find((z) => z.text === "Löwe");
ok(rose?.coins === 1 && loewe?.coins === 29999,
`aus dem Text gelesen: ${JSON.stringify([rose?.coins, loewe?.coins])}`);
/* DIE LÄNGSTEN NAMEN ZUERST: „Rose" und „Rose Gold" in derselben
Kampagne -- ein Vergleich von vorne träfe bei „Rose Gold" den
Eintrag „Rose" und schriebe dessen Preis an. */
const zwei = gliederung("## GESCHENKE\n· Rose Gold\n· Rose", {
geschenke: [{ name: "Rose", preis: 10 }, { name: "Rose Gold", preis: 500 }],
});
const gold = zwei[0].zeilen.find((z) => z.text === "Rose Gold");
ok(gold?.coins === 500,
`„Rose Gold" bekommt 500, nicht die 10 von „Rose" (${gold?.coins})`);
/* ---- Die Gegenprobe zur Überschriftenerkennung -------------- */
/* Eine Zeile aus nichts als Ländercodes IST großgeschrieben und
würde zur Überschrift. `regelnText` schreibt deshalb „Länder:
..." -- die Kleinbuchstaben im Wort davor sind die Sicherung. */
ok(ab[4].zeilen[0].art === "text",
`„Länder: DE, CH, ..." bleibt ein Absatz (${ab[4].zeilen[0].art})`);
const nackt = gliederung("DE, CH, AT");
ok(nackt[0]?.titel === "DE, CH, AT" || nackt.length === 0,
"ohne Wort davor wäre es tatsächlich eine Überschrift -- "
+ "deshalb schreibt regelnText nie nackte Codes");
/* Keine Überschrift ohne Buchstaben, keine mit Satzzeichen am
Ende, keine über 80 Zeichen. */
const proben = [
["12.10.", "eine Zahl ist keine Überschrift"],
["DAS IST EIN GESCHRIENER SATZ.", "ein Satz mit Punkt am Ende auch nicht"],
["Gemischte Schreibweise", "und Kleinbuchstaben erst recht nicht"],
["A".repeat(90), "und nichts über 80 Zeichen"],
];
for (const [zeile, was] of proben) {
const p = gliederung(zeile);
ok(p.length === 1 && p[0].titel === null, `${was}: ${JSON.stringify(zeile.slice(0, 20))}`);
}
/* ---- Zwei Stufen ------------------------------------------- */
const stufen = gliederung("## OBEN\nText.\n### Unten\nMehr Text.");
ok(stufen.length === 2 && stufen[0].stufe === 2 && stufen[1].stufe === 3,
`## ist Stufe 2, ### ist Stufe 3: ${JSON.stringify(stufen.map((a) => [a.titel, a.stufe]))}`);
/* EINE ÜBERSCHRIFT ÜBER LAUTER UNTERABSCHNITTEN BLEIBT STEHEN.
Das war beim ersten Lauf ein echter Verlust: „WELCHE LIGA BIN
ICH?" hat keine eigene Zeile, nur sechs Unterabschnitte -- und
verschwand deshalb. */
const ueber = gliederung("## WELCHE LIGA BIN ICH?\n### Superstars\nBedingung.");
ok(ueber.length === 2 && ueber[0].titel === "WELCHE LIGA BIN ICH?",
`die Überschrift über Unterabschnitten bleibt: ${JSON.stringify(ueber.map((a) => a.titel))}`);
/* GEGENPROBE: Eine Überschrift über NICHTS fällt weg -- sie
entsteht, wenn jemand den Inhalt löscht und den Titel stehen
lässt, und sieht aus wie ein Ladefehler. */
ok(gliederung("## LEER").length === 0,
"eine Überschrift über nichts fällt weg");
/* Und sie ist kein Hinweisblock -- `every()` auf einem leeren
Feld ist `true` (CLAUDE.md, 01.09.2026). */
ok(ueber[0].art !== "hinweise",
`der leere Abschnitt ist kein stiller Hinweiskasten (${ueber[0].art})`);
/* ---- Die Frage mit Antwort darunter ------------------------ */
const frage = gliederung("## INFO\nWie geht das?\nSo geht das.\nHast du Fragen?");
const fz = frage[0].zeilen;
ok(fz[0].art === "frage", `eine Frage MIT Antwort darunter: ${JSON.stringify(fz[0])}`);
/* GEGENPROBE: Eine Frage, auf die NICHTS folgt, ist keine
Überschrift, sondern eine Frage. */
ok(fz[2].art === "text",
`die letzte Zeile bleibt ein Absatz: ${JSON.stringify(fz[2])}`);
/* ---- Nichts geht verloren ---------------------------------- */
/* DIE WICHTIGSTE ZEILE DIESES ABSCHNITTS: Die Gliederung ist eine
Heuristik. Sie darf sich irren -- aber sie darf nichts
verschlucken. Gezählt wird gegen den Eingabetext. */
const eingabe = echt.split(/\r?\n/).map((x) => x.trim()).filter(Boolean);
const ausgabe = ab.flatMap((a) => [
...(a.titel ? [1] : []), ...a.zeilen.map(() => 1),
]).length;
ok(ausgabe === eingabe.length,
`${eingabe.length} Zeilen hinein, ${ausgabe} Überschriften und Zeilen heraus`);
/* Und am echten, langen Text dasselbe. */
const lang = regelnText(leseKampagne(seite.gipfel).kuer, []);
const langAb = gliederung(lang, { geschenke: leseKampagne(seite.gipfel).kuer.geschenke });
const langEin = lang.split(/\r?\n/).map((x) => x.trim()).filter(Boolean).length;
const langAus = langAb.reduce((s, a) => s + (a.titel ? 1 : 0) + a.zeilen.length, 0);
ok(langAus === langEin,
`am echten Regeltext ebenfalls: ${langEin} hinein, ${langAus} heraus `
+ `(${langAb.length} Abschnitte)`);
}
melde(""); melde("");
melde(`${geprueft} Pruefungen, ${fehler} Fehler`); melde(`${geprueft} Pruefungen, ${fehler} Fehler`);
melde(fehler ? "NICHT IN ORDNUNG" : "ALLES IN ORDNUNG"); melde(fehler ? "NICHT IN ORDNUNG" : "ALLES IN ORDNUNG");
+179 -9
View File
@@ -531,13 +531,84 @@ let kachelId = null;
"Titel und Einleitung stehen drin"); "Titel und Einleitung stehen drin");
/* KEIN FELD LÄNGER ALS DAS FORMULAR ERLAUBT. Sonst entstünde eine /* KEIN FELD LÄNGER ALS DAS FORMULAR ERLAUBT. Sonst entstünde eine
Kachel, die DogFather öffnen, aber nicht speichern kann. */ Kachel, die DogFather öffnen, aber nicht speichern kann.
for (const feld of ["text", "event_aufgaben", "event_regeln"]) {
ok((z[feld] || "").length <= 6000, JE FELD SEINE EIGENE GRENZE (berichtigt 07.10.2026): `text`
` ${feld}: ${(z[feld] || "").length} Zeichen (höchstens 6000)`); ist die Beschreibung und darf 6000 Zeichen haben (TEXT_MAX),
die zwei Eventfelder 20000 (EVENT_TEXT_MAX). Beide gegen
dieselbe Zahl zu prüfen hieße, eine der zwei Grenzen nicht
zu prüfen. */
const { EVENT_TEXT_MAX } = await import("./workspace-bereiche.js");
ok(EVENT_TEXT_MAX === 20000,
`die Grenze für die Eventfelder steht auf ${EVENT_TEXT_MAX}`);
for (const [feld, grenze] of [
["text", 6000], ["event_aufgaben", EVENT_TEXT_MAX], ["event_regeln", EVENT_TEXT_MAX],
]) {
ok((z[feld] || "").length <= grenze,
` ${feld}: ${(z[feld] || "").length} Zeichen (höchstens ${grenze})`);
}
/* ---- DER REGELTEXT PASST JETZT GANZ HINEIN (07.10.2026) ----
Hier stand das Gegenteil: „und wo gekürzt wurde, sagt die
Kachel es selbst". Das war richtig, solange das Feld 6000
Zeichen fasste -- der Regeltext von „Gipfelstürmer" ist
gemessen 7679 Zeichen lang, also fielen jedes Mal rund 1600
Zeichen echter Inhalt weg. Filipe: „ich will alle
informationen die möglich sind wenn man den link hochlädt."
Gemessen wird deshalb jetzt, dass NICHTS gekürzt wurde -- und
zwar mit Beleg, dass der Text wirklich länger als die alte
Grenze ist. Sonst wäre die Zeile auch bei einem leeren Feld
grün. */
ok((z.event_regeln || "").length > 6000,
`der Regeltext ist ${(z.event_regeln || "").length} Zeichen lang `
+ "-- mit der alten Grenze von 6000 wäre hier gekürzt worden");
ok(!(z.event_regeln || "").includes("Der Regeltext war länger"),
"und er steht jetzt vollständig in der Kachel, ohne Kürzungsfußnote");
/* DER SCHLUSS DES TEXTES IST DA. „Nicht gekürzt" ließe sich auch
behaupten, wenn nur der Anfang drinstünde; geprüft wird der
LETZTE Abschnitt, den die Kürzung als ersten weggenommen hat. */
ok(/TEILNAHME\nLänder: /.test(z.event_regeln || "")
|| /## TEILNAHME/.test(z.event_regeln || ""),
"bis zum letzten Abschnitt (TEILNAHME mit den Ländern)");
/* ---- UND DIE KÜRZUNG SELBST BLEIBT GEPRÜFT ----------------
Sie läuft im Betrieb nicht mehr (kein gemessener Regeltext
ist so lang). Eine Funktion, die nie läuft, geht beim
nächsten Umbau still kaputt -- und ausgerechnet dann, wenn
eine Kampagne mit 25000 Zeichen kommt. Also einzeln, mit
kleiner Grenze. */
{
const { kuerzen } = await import("./workspace-kampagne.js");
/* ZEHN ABSCHNITTE, rund 600 Zeichen. Beim ersten Versuch standen
hier drei Abschnitte mit 61 Zeichen und eine Grenze von 200 --
der Text passte also hinein und wurde zu Recht nicht gekürzt.
Die Prüfung meldete „sagt nicht, dass gekürzt wurde" und hatte
formal recht: Es wurde nichts gekürzt. Eine Prüfung, deren
Voraussetzung nicht erfüllt ist, misst nichts (CLAUDE.md,
03.09.2026) -- deshalb muss der Text WIRKLICH zu lang sein,
und genau das wird vorher festgestellt. */
const lang = Array.from({ length: 10 }, (x, i) =>
`## ABSCHNITT ${i + 1}\nEine Zeile mit Inhalt, Nummer ${i + 1}.`).join("\n\n");
ok(lang.length > 400,
`der Probetext ist ${lang.length} Zeichen lang, die Grenze 400 `
+ "-- er MUSS also gekürzt werden");
const kurz = kuerzen(lang, 400);
ok(kurz.length <= 400 && kurz.length < lang.length,
`kuerzen(…, 400) hält die Grenze ein (${kurz.length} statt ${lang.length})`);
ok(kurz.includes("Der Regeltext war länger"),
" und sagt, dass gekürzt wurde");
/* AN EINER ABSCHNITTSGRENZE, nicht mitten im Wort: Ein
abgeschnittenes Wort sieht nicht nach einer Grenze aus,
sondern nach einem Fehler beim Einlesen. */
const inhalt = kurz.split("\n\n*")[0];
ok(/\.$|^## [A-ZÄÖÜ]+$/m.test(inhalt.split("\n").pop() || ""),
` an einer Zeilengrenze: …${JSON.stringify(inhalt.slice(-24))}`);
ok(kuerzen(lang, 20000) === lang,
" und ein Text, der passt, bleibt unangetastet");
} }
ok((z.event_regeln || "").includes("Der Regeltext war länger"),
"und wo gekürzt wurde, sagt die Kachel es selbst");
/* DER AUSZUG BEWAHRT, WAS NICHT INS FELD PASST. */ /* DER AUSZUG BEWAHRT, WAS NICHT INS FELD PASST. */
const auszug = JSON.parse(z.kampagne_daten || "{}"); const auszug = JSON.parse(z.kampagne_daten || "{}");
@@ -919,16 +990,41 @@ melde("=== 12b. Ein Event nach draußen teilen ===");
&& /noindex/.test(String(seiteOffen.kopf["x-robots-tag"] || "")), && /noindex/.test(String(seiteOffen.kopf["x-robots-tag"] || "")),
"Suchmaschinen bleiben draußen — im HTML UND im Kopf der Antwort"); "Suchmaschinen bleiben draußen — im HTML UND im Kopf der Antwort");
/* ---- UND NUR DAS GEHT HINAUS ---------------------------------- */ /* ---- WAS DIE KAMPAGNE SAGT, GEHT HINAUS -- WER WIR SIND, NICHT
DIE GRENZE IST AM 07.10.2026 VERSCHOBEN WORDEN, auf Filipes
Ansage: „auch wenn man teilt soll es einfach perfekt sein. man
soll alle infos sehen die man braucht."
Vorher standen „eine Aufgabe" und „der Regeltext" in dieser
Liste, und das war zu eng gedacht. Diese Angaben stammen von
einer OEFFENTLICHEN TikTok-Seite; sie weiterzugeben verraet
nichts ueber uns, und ohne sie ist der Link eine Einladung,
die nicht sagt, wozu.
DIE GRENZE LIEGT BEIM GEGENSTAND, NICHT BEI DER MENGE: nichts
ueber Personen (Name, Rolle, Haken), nichts ueber die
Verwaltung (Feldnamen, Haus, Keks), nichts aus der Buchhaltung
(Kampagnennummer, Steckbrief). Jede dieser Zeilen steht hier
weiterhin -- die Liste ist kuerzer geworden, nicht weicher.
UND NEU: KEINE HINWEISZEILE. „Die Preise konnten nicht gelesen
werden — bitte nachtragen" ist eine Notiz AN DOGFATHER. Auf
einer Seite, die er in Discord postet, stuende damit vor
seinen Creatorn, dass seine Kachel unfertig ist. Geprueft wird
das weiter unten an einer Kampagne, die wirklich einen Hinweis
hat -- an „Gipfelstuermer" waere es ein gruener Haken ohne
Messung, denn die hat gar keinen. */
const verboten = [ const verboten = [
["eine Aufgabe", "1 Diamant = 1 Punkt"],
["der Regeltext", "KAMPAGNENGESCHENKE"],
["ein Name", "Dogfather"], ["ein Name", "Dogfather"],
["eine Rolle", "admin"], ["eine Rolle", "admin"],
["die Kampagnennummer", ID_GIPFEL], ["die Kampagnennummer", ID_GIPFEL],
["die Kurznummer", "1140116740"],
["ein Feldname aus der Datenbank", "erstellt_von"], ["ein Feldname aus der Datenbank", "erstellt_von"],
["die Hausangabe", "\"haus\""], ["die Hausangabe", "\"haus\""],
["ein Keks", "workspace_sitzung"], ["ein Keks", "workspace_sitzung"],
["ein Hinweis an DogFather", "bitte nachtragen"],
["die Kuerzungsfussnote", "länger als das Feld fasst"],
]; ];
/* NUR DER KOERPER, NICHT DAS STILBLATT (berichtigt 07.10.2026). /* NUR DER KOERPER, NICHT DAS STILBLATT (berichtigt 07.10.2026).
Hier stand zuerst schlicht "agentur" als verbotenes Wort -- und Hier stand zuerst schlicht "agentur" als verbotenes Wort -- und
@@ -949,6 +1045,80 @@ melde("=== 12b. Ein Event nach draußen teilen ===");
&& /04\.10\.2026/.test(seiteOffen.text), && /04\.10\.2026/.test(seiteOffen.text),
"Gegenprobe: Titel und Zeitraum stehen sehr wohl darauf"); "Gegenprobe: Titel und Zeitraum stehen sehr wohl darauf");
/* ---- UND JETZT ALLES, WAS MAN BRAUCHT (07.10.2026) ---------
Gemessen wird nicht „die Seite ist lang", sondern dass jede
der vier Auskünfte wirklich dasteht: Aufgaben, Geschenke mit
Preis, Überschriften, gegliederte Unterstufen. */
const koerper = ohneStil;
ok(/<h2 class="k__schild2">Aufgaben<\/h2>/.test(koerper)
&& koerper.includes("1 Diamant = 1 Punkt"),
"die Aufgaben stehen auf der Seite");
const wieVieleAufgaben = (koerper.match(/<li>/g) || []).length;
ok(wieVieleAufgaben >= 5,
`und zwar alle fünf, nicht nur eine (${wieVieleAufgaben} Zeilen insgesamt)`);
ok(/Diese Geschenke zählen/.test(koerper) && koerper.includes("Ahorn-Welt"),
"die Geschenke stehen darauf");
ok(/12\.000 Coins/.test(koerper),
"mit Coinpreis und Tausenderpunkt");
ok(/<p class="k__woher">KAMPAGNENGESCHENKE/.test(koerper),
"unter der Überschrift, die die Kampagne dafür vorgesehen hat");
const titel = [...koerper.matchAll(/<h3 class="k__titel">([^<]+)<\/h3>/g)]
.map((m) => m[1]);
ok(titel.includes("BELOHNUNGEN") && titel.includes("TEILNAHMEBEDINGUNGEN"),
`die Abschnitte sind als Überschriften gesetzt: ${JSON.stringify(titel.slice(0, 8))}`);
ok(titel.includes("Superstars") && titel.includes("1. Liga"),
"auch die Ligen aus dem Klapptext");
ok(/data-stufe="3"/.test(koerper) && /data-stufe="2"/.test(koerper),
"und sie stehen auf zwei Stufen, nicht alle gleichrangig");
/* DIE VORSCHAU IN DISCORD BLEIBT KURZ. Discord zeigt rund 300
Zeichen; eine Beschreibung von 7000 Zeichen wäre dort
abgeschnitten und hier nutzlos. Die LANGE Fassung steht im
Körper, die kurze im Kopf -- das ist der ganze Trick. */
const beschr = /<meta property="og:description" content="([^"]*)"/.exec(seiteOffen.text);
/* 300 IST DIE ZAHL, DIE ZAEHLT -- so viel zeigt Discord.
Beim ersten Lauf kam 304 heraus, weil die Textgrenze (280)
den Zeitraum davor nicht mitrechnete. */
ok(beschr && beschr[1].length <= 300,
`og:description bleibt unter 300 Zeichen (${beschr?.[1].length})`);
/* UND SIE IST NICHT LEER. „Kurz" ist auch eine leere
Beschreibung -- dann zeigte Discord nur den Titel. */
ok(beschr && beschr[1].length > 40,
` und sagt wirklich etwas (${beschr?.[1].length} Zeichen)`);
ok(koerper.length > 3000,
`der Körper trägt dagegen alles (${koerper.length} Zeichen)`);
/* ---- EINE KAMPAGNE MIT HINWEIS ------------------------------
Das ist die Messung, die „keine Notiz an DogFather geht
hinaus" überhaupt erst zu einer Messung macht:
„Gipfelstürmer" hat null Hinweise, „ohne Preise" hat einen.
Ohne diesen Abschnitt wäre die Zeile oben ein grüner Haken
über einem leeren Feld (CLAUDE.md, 01.09.2026). */
const opId = dbLesen("SELECT id FROM eintraege WHERE kampagne_id = ?", ID_OHNEPREISE)[0]?.id;
ok(!!opId, `die Kampagne ohne Preise liegt in der Datenbank (#${opId})`);
const opRegeln = dbLesen("SELECT event_regeln FROM eintraege WHERE id = ?", opId)[0];
ok(/bitte nachtragen|keine Preisliste/.test(opRegeln?.event_regeln || ""),
"und ihre Kachel trägt wirklich einen Hinweis (sonst misst das Folgende nichts)");
const opT = await roh(`/workspace/api/eintrag/${opId}/teilen`,
{ methode: "POST", keks: keksDogi, rumpf: {} });
const opS = String(JSON.parse(opT.text || "{}").weg || "").split("/e/")[1];
ok(!!opS, `sie lässt sich teilen (HTTP ${opT.code})`);
const opSeite = await offen(`/e/${opS}`);
const opKoerper = opSeite.text.split("<style")[0]
+ (opSeite.text.split("</style>")[1] || "");
ok(opSeite.code === 200, `die Seite steht (${opSeite.code})`);
ok(!/bitte nachtragen/.test(opKoerper) && !/keine Preisliste/.test(opKoerper),
"KEIN Hinweis an DogFather steht auf der geteilten Seite");
/* GEGENPROBE: Der Rest der Kampagne steht sehr wohl darauf --
sonst wäre die Zeile darüber auch bei einer leeren Seite grün. */
ok(/Diese Geschenke zählen/.test(opKoerper) && /<h3 class="k__titel">/.test(opKoerper),
"Gegenprobe: Geschenke und Überschriften stehen sehr wohl darauf");
await roh(`/workspace/api/eintrag/${opId}/teilen`, { methode: "DELETE", keks: keksDogi });
/* ---- Das Banner ------------------------------------------------ */ /* ---- Das Banner ------------------------------------------------ */
const bild = await offen(`/e/${schluessel}/bild`); const bild = await offen(`/e/${schluessel}/bild`);
ok(bild.code === 200 && /^image\//.test(String(bild.kopf["content-type"] || "")), ok(bild.code === 200 && /^image\//.test(String(bild.kopf["content-type"] || "")),
+135 -3
View File
@@ -46,6 +46,11 @@ import { terminVon } from "./workspace-termin-regel.js";
-- und dann der Link). Die vierte ist immer die, die etwas -- und dann der Link). Die vierte ist immer die, die etwas
durchlaesst. */ durchlaesst. */
import { darfSeite } from "./rechte.js"; import { darfSeite } from "./rechte.js";
/* Die Gliederung des Regeltextes kommt aus demselben Modul, das
ihn schreibt. Sie im Browser zu bauen waere die zweite Fassung
derselben Regel -- und die Teilen-Seite (workspace-teilen.js)
braeuchte dann eine dritte. */
import { gliederung } from "./kampagne-lesen.mjs";
/* EIN KATALOG AUS ZWEI QUELLEN (17.09.2026) /* EIN KATALOG AUS ZWEI QUELLEN (17.09.2026)
@@ -766,6 +771,38 @@ const MIT_ANTWORTEN = new Set(["treff", "wunsch"]);
const TITEL_MAX = 160; const TITEL_MAX = 160;
const TEXT_MAX = 6000; const TEXT_MAX = 6000;
/* SO VIEL FASSEN DIE ZWEI EVENT-TEXTFELDER (07.10.2026).
Filipe: „ich will alle informationen die möglich sind wenn man
den link hochlädt." Gemessen stehen dem 6000 Zeichen im Weg:
Der Regeltext von „Gipfelstuermer" ist 7605 Zeichen lang --
Geschenke, sechs Preisgruppen, Rangliste mit sechs Ligen,
Teilnahmebedingungen, Themenideen. Er wurde beim Einlesen an
einer Abschnittsgrenze abgeschnitten, und abgeschnitten wurde
das Ende: rund 1600 Zeichen echter Inhalt, jedes Mal.
WARUM NICHT EINFACH TEXT_MAX ERHOEHEN: Weil die 6000 fuer das
Beschreibungsfeld eines Aushangs eine sinnvolle Grenze sind --
wer dort mehr tippt, schreibt keinen Aushang mehr. Diese zwei
Felder sind etwas anderes: Sie halten das Regelwerk einer
fremden Kampagne, und wie lang das ist, entscheidet TikTok.
Eine Grenze fuer zwei verschiedene Dinge wird immer fuer eines
von beiden falsch sein.
WARUM 20000 UND NICHT UNBEGRENZT: Eine Grenze muss es geben,
sonst ist das Feld ein Ablageplatz fuer beliebig viel Text aus
dem Netz. 20000 ist das Zweieinhalbfache des laengsten
gemessenen Regeltextes -- Platz fuer eine Kampagne, die
doppelt so ausfuehrlich ist, und keiner fuer ein Buch.
DIE ZAHL STEHT NUR HIER. workspace-kampagne.js holt sie sich
von hier (`FELD_MAX`), statt sie abzuschreiben: Zwei Fassungen
derselben Grenze laufen auseinander, und dann kuerzt der eine
Weg auf 6000, waehrend der andere 20000 erlaubt -- eine Kachel,
die man speichern kann, aber nicht so, wie sie eingelesen
wurde. */
export const EVENT_TEXT_MAX = 20000;
/* ===================================================================== /* =====================================================================
DIE PUNKTE EINES AGENTUR-EVENTS (03.10.2026) DIE PUNKTE EINES AGENTUR-EVENTS (03.10.2026)
@@ -1485,6 +1522,15 @@ const SPALTEN = `
KEINE BACKTICKS IN DIESEM BLOCK: SPALTEN ist selbst ein Template. KEINE BACKTICKS IN DIESEM BLOCK: SPALTEN ist selbst ein Template.
Ein Backtick im Kommentar beendet es mitten im SQL. */ Ein Backtick im Kommentar beendet es mitten im SQL. */
e.kampagne_id, e.kampagne_quelle, e.kampagne_id, e.kampagne_quelle,
/* DAS GELESENE SELBST (07.10.2026, zweite Runde). Daraus baut
der Server die Geschenkliste mit Coinpreisen, den Steckbrief
und die Beschreibung je Missionsgruppe. Die Spalte selbst
geht NICHT an die Oberflaeche -- sie wird unten gelesen und
dann aus der Antwort entfernt: ein Auszug ist mehrere
Kilobyte gross, und davon braucht die Kachel drei Felder.
KEINE BACKTICKS IN DIESEM BLOCK, siehe oben. */
e.kampagne_daten,
/* OB DIESES EVENT GETEILT IST (07.10.2026). Die Oberflaeche baut /* OB DIESES EVENT GETEILT IST (07.10.2026). Die Oberflaeche baut
daraus entweder "Teilen" oder "Link kopieren" plus "nicht mehr daraus entweder "Teilen" oder "Link kopieren" plus "nicht mehr
teilen". Der Schluessel selbst geht NICHT mit: Er steht in der teilen". Der Schluessel selbst geht NICHT mit: Er steht in der
@@ -1814,9 +1860,90 @@ bereicheRouter.get("/workspace/api/bereich/:bereich", (req, res) => {
for (const e of events) { for (const e of events) {
const haken = meine.get(e.id) || new Set(); const haken = meine.get(e.id) || new Set();
/* ---- WAS AUS DER KAMPAGNE GELESEN WURDE -------------
`kampagne_daten` ist der Auszug, den der Leser beim
Einlesen abgelegt hat. Drei Dinge daraus stehen auf
der Kachel, und alle drei sind GELESENE Daten, nicht
aus dem Text zurueckgewonnene:
geschenke Name, Coinpreis und Hinweis je Geschenk
steckbrief Kurznummer, Region, Sprache, Zeitzone
gruppen die Beschreibung je Missionsgruppe
WARUM NICHT AUS DEM TEXT: Im Regeltext steht
„· Feuerwerk (Sammle Feuerwerk in deinem LIVE!)" --
ohne Preis, weil es ein Kampagnengeschenk ist. Wer
daraus den Preis lesen will, muss raten; wer die
Liste hat, weiss es. Genau danach hat Filipe
gefragt: „ich will auch dass man sieht welche
geschenke bei dem event zaehlen und so."
IN EIN try: Die Spalte ist Text, und ein Eintrag von
Hand oder aus einer alten Fassung muss kein JSON
sein. Eine Kachel darf daran nicht scheitern -- ohne
Auszug fehlen drei Felder, der Regeltext steht
trotzdem da. */
let auszug = null;
if (e.kampagne_daten) {
try { auszug = JSON.parse(e.kampagne_daten); } catch { auszug = null; }
}
delete e.kampagne_daten;
const gruppen = new Map();
for (const g of auszug?.aufgabenGruppen || []) {
if (g?.titel) gruppen.set(String(g.titel).trim(), g);
}
e.event_punkte = punkteAusText(e.event_aufgaben) e.event_punkte = punkteAusText(e.event_aufgaben)
.map((p) => ({ ...p, meiner: haken.has(p.schluessel) })); .map((p) => {
e.event_regelpunkte = punkteAusText(e.event_regeln); const g = gruppen.get(p.text.trim());
return {
...p,
meiner: haken.has(p.schluessel),
/* Nur wenn es etwas zu sagen gibt. Ein leeres
Feld an jeder Aufgabe ist eine Zeile Luft
unter jedem Haken. */
...(g?.beschreibung ? { beschreibung: g.beschreibung } : {}),
...(g?.von_tag && g?.bis_tag
? { von_tag: g.von_tag, bis_tag: g.bis_tag } : {}),
};
});
/* DIE GLIEDERUNG STATT EINER FLACHEN LISTE (07.10.2026).
Hier stand `punkteAusText(e.event_regeln)` -- jede
Zeile derselbe Strich. Damit sah „INFOS ZUR
KAMPAGNE" genauso aus wie der Absatz darunter, und
die Hinweise standen zwischen den Regeln, als waeren
sie welche. Die Begruendung in Laenge steht bei
`gliederung()` in kampagne-lesen.mjs.
DER DRITTE AUSGANG BLEIBT: `event_regeln` geht
weiterhin als Rohtext mit (siehe SPALTEN). Kommt
hier nichts heraus, zeigt die Kachel den Text --
schlicht, aber vollstaendig. Eine zweite
Punkteliste daneben waere eine dritte Abschrift
desselben Inhalts in derselben Antwort. */
e.event_regelabschnitte = gliederung(e.event_regeln,
{ geschenke: auszug?.geschenke || [] });
if (auszug?.geschenke?.length) {
e.event_geschenke = {
titel: auszug.geschenkTitel || null,
hinweis: auszug.geschenkHinweis || null,
liste: auszug.geschenke,
};
}
/* Leere Felder fallen weg, statt als „—" zu erscheinen:
`union_name` ist in allen gemessenen Kampagnen leer. */
if (auszug?.steckbrief) {
const s = Object.fromEntries(Object.entries(auszug.steckbrief)
.filter(([, v]) => v !== null && v !== undefined && v !== ""));
if (Object.keys(s).length) e.event_steckbrief = s;
}
/* DIE ZAHL KOMMT MIT, statt im Browser gezaehlt zu werden -- /* DIE ZAHL KOMMT MIT, statt im Browser gezaehlt zu werden --
sie steht sonst an zwei Stellen, und „2 von 3" ist genau sie steht sonst an zwei Stellen, und „2 von 3" ist genau
die Sorte Angabe, die man nicht zweimal haben will. */ die Sorte Angabe, die man nicht zweimal haben will. */
@@ -2187,7 +2314,12 @@ function pruefe(bereich, körper, { neu }) {
for (const [feld, name] of [["event_aufgaben", "Die Aufgaben"], ["event_regeln", "Die Regeln"]]) { for (const [feld, name] of [["event_aufgaben", "Die Aufgaben"], ["event_regeln", "Die Regeln"]]) {
if (körper[feld] === undefined) continue; if (körper[feld] === undefined) continue;
const t = String(körper[feld] ?? "").trim(); const t = String(körper[feld] ?? "").trim();
if (t.length > TEXT_MAX) fehler.push(`${name} sind zu lang.`); /* EVENT_TEXT_MAX, nicht TEXT_MAX -- Begruendung oben bei der
Zahl. Eine Kachel, die der Server beim Speichern ablehnt,
laesst den Menschen davor sich selbst fuer den Fehler
halten: Er hat ein Wort geaendert und bekommt eine
Meldung ueber 1600 Zeichen, die er nie getippt hat. */
if (t.length > EVENT_TEXT_MAX) fehler.push(`${name} sind zu lang.`);
else aus[feld] = t || null; else aus[feld] = t || null;
} }
/* "Bis" vor "von" ist kein Zeitraum, sondern ein Tippfehler -- und /* "Bis" vor "von" ist kein Zeitraum, sondern ein Tippfehler -- und
+23 -3
View File
@@ -76,6 +76,7 @@ import {
db, protokolliere, echteIp, sitzungLesen, istLeitung, db, protokolliere, echteIp, sitzungLesen, istLeitung,
DATEN_ORDNER, hausFuerNeuenEintrag } from "./workspace.js"; DATEN_ORDNER, hausFuerNeuenEintrag } from "./workspace.js";
import { bildArtVon } from "./workspace-dateien.js"; import { bildArtVon } from "./workspace-dateien.js";
import { EVENT_TEXT_MAX } from "./workspace-bereiche.js";
import { import {
linkAusText, folgeUmleitung, holeSeite, holeMitFrist, bildAdresseErlaubt, linkAusText, folgeUmleitung, holeSeite, holeMitFrist, bildAdresseErlaubt,
hostVon, FRIST_SEITE_MS } from "./helfer-tiktok.mjs"; hostVon, FRIST_SEITE_MS } from "./helfer-tiktok.mjs";
@@ -116,8 +117,15 @@ const DRAENGT_AB_TAGEN = 7;
OEFFNEN, aber nicht SPEICHERN kann: Er aendert ein Wort, drueckt OEFFNEN, aber nicht SPEICHERN kann: Er aendert ein Wort, drueckt
Speichern und bekommt eine Fehlermeldung ueber etwas, das er nie Speichern und bekommt eine Fehlermeldung ueber etwas, das er nie
getippt hat. Ein Formular, das der Server ablehnt, laesst den getippt hat. Ein Formular, das der Server ablehnt, laesst den
Menschen davor sich selbst fuer den Fehler halten. */ Menschen davor sich selbst fuer den Fehler halten.
const FELD_MAX = 6000;
SEIT DEM 07.10.2026 STEHT DIE ZAHL NUR NOCH AN EINER STELLE
(`EVENT_TEXT_MAX` in workspace-bereiche.js) und ist dort von
6000 auf 20000 gehoben worden -- der Regeltext von
„Gipfelstuermer" passt jetzt vollstaendig hinein. Abgeschrieben
wird sie nicht: Zwei Fassungen derselben Grenze laufen
auseinander. */
const FELD_MAX = EVENT_TEXT_MAX;
/** Kuerzt an einer ABSCHNITTSGRENZE und sagt, dass gekuerzt wurde. /** Kuerzt an einer ABSCHNITTSGRENZE und sagt, dass gekuerzt wurde.
* *
@@ -127,7 +135,19 @@ const FELD_MAX = 6000;
* *
* Verloren geht nichts: Das Gelesene steht gegliedert in * Verloren geht nichts: Das Gelesene steht gegliedert in
* kampagne_daten. */ * kampagne_daten. */
function kuerzen(text, max = FELD_MAX) { /* AUSGEFUEHRT SEIT DEM 07.10.2026, damit sie pruefbar bleibt.
Seit das Feld 20000 Zeichen fasst, wird der laengste gemessene
Regeltext (7679 Zeichen) nicht mehr gekuerzt -- und damit liefe
diese Funktion im echten Betrieb nie. Eine Funktion, die nie
laeuft, ist eine Funktion, die beim naechsten Umbau still
kaputtgeht: Kommt eine Kampagne mit 25000 Zeichen, soll sie
genau dann richtig arbeiten.
Also wird sie einzeln geprueft, mit einer kleinen Grenze
(pruef-kampagne.mjs, Abschnitt 4). Das ist der Unterschied
zwischen „laeuft gerade nicht" und „ist ungeprueft". */
export function kuerzen(text, max = FELD_MAX) {
const s = String(text || ""); const s = String(text || "");
if (s.length <= max) return s; if (s.length <= max) return s;
const fussnote = "\n\n* Der Regeltext war länger als das Feld fasst und endet " const fussnote = "\n\n* Der Regeltext war länger als das Feld fasst und endet "
+252 -6
View File
@@ -66,6 +66,14 @@ import {
db, protokolliere, echteIp, sitzungLesen, istLeitung, db, protokolliere, echteIp, sitzungLesen, istLeitung,
DATEN_ORDNER } from "./workspace.js"; DATEN_ORDNER } from "./workspace.js";
import { liefereDatei } from "./helfer-ausliefern.mjs"; import { liefereDatei } from "./helfer-ausliefern.mjs";
/* DIESELBE ZERLEGUNG WIE DIE KACHEL, nicht eine zweite.
`gliederung` macht aus dem Regeltext Abschnitte, `punkteAusText`
aus den Aufgaben eine Liste. Beides hier nachzubauen hiesse: Die
geteilte Seite zeigt irgendwann etwas anderes als die Kachel,
und niemand koennte sagen, welche von beiden recht hat. */
import { gliederung } from "./kampagne-lesen.mjs";
import { punkteAusText } from "./workspace-bereiche.js";
export const teilenRouter = express.Router(); export const teilenRouter = express.Router();
@@ -193,7 +201,14 @@ function eventZu(token) {
if (!/^[A-Za-z0-9_-]{10,64}$/.test(String(token || ""))) return null; if (!/^[A-Za-z0-9_-]{10,64}$/.test(String(token || ""))) return null;
try { try {
return db().prepare( return db().prepare(
`SELECT id, titel, text, datum, event_ende, kampagne_id, teilen_seit /* NEU SEIT DEM 07.10.2026: event_aufgaben, event_regeln,
kampagne_daten. Siehe die Begruendung bei `seitePlakat`,
wo die Grenze gezogen wird -- was die KAMPAGNE sagt, geht
hinaus; was ueber UNS etwas sagt, nicht. Deshalb steht
`erstellt_von` hier weiterhin nicht, und `haus` auch
nicht. */
`SELECT id, titel, text, datum, event_ende, kampagne_id, teilen_seit,
event_aufgaben, event_regeln, kampagne_daten
FROM eintraege FROM eintraege
WHERE teilen_token = ? AND bereich = 'agentur' AND art = 'kampagne'`) WHERE teilen_token = ? AND bereich = 'agentur' AND art = 'kampagne'`)
.get(String(token)) || null; .get(String(token)) || null;
@@ -240,12 +255,77 @@ teilenRouter.get("/e/:token", (req, res) => {
const bis = tagText(e.event_ende); const bis = tagText(e.event_ende);
const zeitraum = von && bis && bis !== von ? `${von} – ${bis}` : (von || ""); const zeitraum = von && bis && bis !== von ? `${von} – ${bis}` : (von || "");
const text = String(e.text || "").replace(/\s+/g, " ").trim(); const text = String(e.text || "").replace(/\s+/g, " ").trim();
/* Discord zeigt rund 300 Zeichen. Ein Satz, der mitten im Wort /* DISCORD ZEIGT RUND 300 ZEICHEN -- UND DER ZEITRAUM STEHT MIT
endet, sieht nach einem Fehler aus -- also an einer Wortgrenze. */ DAVOR (berichtigt 07.10.2026).
const kurz = text.length > 280
? text.slice(0, text.lastIndexOf(" ", 277)) + " …" Hier stand 280. Gemessen kam `og:description` auf 304
Zeichen heraus: Die Beschreibung ist `zeitraum · kurz`, und
der Zeitraum („04.10.2026 – 07.10.2026 · ") braucht allein
26 Zeichen. Wer nur den Text begrenzt, begrenzt nicht die
Angabe, die wirklich hinausgeht.
250 statt 280: 250 + 26 = 276, mit Luft fuer einen laengeren
Zeitraum. Eine Wortgrenze bleibt es -- ein Satz, der mitten
im Wort endet, sieht nach einem Fehler aus. */
const kurz = text.length > 250
? text.slice(0, text.lastIndexOf(" ", 247)) + " …"
: text; : text;
const beschreibung = [zeitraum, kurz].filter(Boolean).join(" · "); /* ---- WAS DIE KAMPAGNE SAGT (07.10.2026) ------------------
Filipe: „auch wenn man teilt soll es einfach perfekt sein.
man soll alle infos sehen die man braucht."
Bis heute standen hier Titel, Zeitraum, Einleitung und Banner
-- also gerade so viel, dass Discord eine Vorschau bauen kann.
Wer dem Link folgte, erfuhr nicht, was zu tun ist.
DIE GRENZE LIEGT NICHT BEI DER MENGE, SONDERN BEIM GEGENSTAND:
Was die KAMPAGNE sagt, darf hinaus -- Aufgaben, Geschenke,
Regeln. Was ueber UNS etwas sagt, nicht: kein Name, keine
Rolle, kein Haken, kein Haus, keine Kampagnennummer, kein
Steckbrief. Der Grund ist nicht Geheimhaltung um ihrer selbst
willen: Wer den Link weitergibt, gibt eine EINLADUNG weiter,
und eine Einladung nennt die Sache, nicht die Einladenden.
DIE HINWEISE BLEIBEN DRINNEN, und das ist die wichtigste
Zeile hier. „Die Preise konnten nicht gelesen werden -- bitte
nachtragen" ist eine Notiz AN DOGFATHER. Auf einer Seite, die
er in Discord postet, stuende damit vor seinen Creatorn, dass
seine Kachel unfertig ist. Dasselbe gilt fuer die Fussnote
ueber einen gekuerzten Regeltext. Beides sind Hinweiszeilen,
beide fallen hier weg -- an einer Stelle, mit einer Regel.
DER STECKBRIEF BLEIBT EBENFALLS DRINNEN. Kurznummer, Markt
und Zeitzone sind Buchhaltung, keine Einladung; sie stehen
auf der Kachel, wo sie gebraucht werden. */
let auszug = null;
if (e.kampagne_daten) {
try { auszug = JSON.parse(e.kampagne_daten); } catch { auszug = null; }
}
const aufgaben = punkteAusText(e.event_aufgaben).map((p) => p.text);
const abschnitte = gliederung(e.event_regeln,
{ geschenke: auszug?.geschenke || [] })
/* Kein Hinweisabschnitt, und auch keine Hinweiszeile in einem
anderen Abschnitt. */
.filter((a) => a.art !== "hinweise")
.map((a) => ({ ...a, zeilen: a.zeilen.filter((z) => z.art !== "hinweis") }))
.filter((a, i, alle) => a.zeilen.length
|| (alle[i + 1] && alle[i + 1].stufe > a.stufe));
/* Die Geschenke werden aus den Abschnitten herausgezogen -- wie
auf der Kachel: Sie stehen oben, nicht mitten im Regeltext. */
const gabenAb = abschnitte.find((a) => a.art === "geschenke") || null;
const rest = abschnitte.filter((a) => a !== gabenAb);
const gaben = (gabenAb?.zeilen || []).filter((z) => z.art === "geschenk");
/* DIE VORSCHAU IN DISCORD SOLL AUCH OHNE EINLEITUNG ETWAS SAGEN.
Gemessen an „LIVE Glow Up": Die Kampagne hat gar keinen
Einleitungstext (der Inhalt steht in den Klapptexten) -- in der
Vorschau stand damit nur ein Zeitraum. Die ersten Aufgaben
sagen in derselben Zeile mehr als jede Umschreibung. */
const aufgabenKurz = aufgaben.slice(0, 3).join(" · ");
const beschreibung = [zeitraum, kurz || aufgabenKurz]
.filter(Boolean).join(" · ");
const basis = `${TEILEN_BASIS}/e/${encodeURIComponent(req.params.token)}`; const basis = `${TEILEN_BASIS}/e/${encodeURIComponent(req.params.token)}`;
const hatBild = (() => { const hatBild = (() => {
try { try {
@@ -262,6 +342,11 @@ teilenRouter.get("/e/:token", (req, res) => {
basis, basis,
bild: hatBild ? `${basis}/bild` : null, bild: hatBild ? `${basis}/bild` : null,
vonTikTok: !!e.kampagne_id, vonTikTok: !!e.kampagne_id,
aufgaben,
gaben,
gabenTitel: gabenAb?.titel || null,
gabenUnter: gabenAb?.unter || null,
abschnitte: rest,
})); }));
}); });
@@ -312,8 +397,166 @@ const STIL = `
color: #7f95ad; font-size: .85rem; color: #7f95ad; font-size: .85rem;
} }
a { color: #7fd2fb; } a { color: #7fd2fb; }
/* ---- Alles, was seit dem 07.10.2026 dazugekommen ist ----
Dieselbe Gliederung wie auf der Kachel, nur ohne Aufklappen:
Wer einem geteilten Link folgt, will lesen und nicht
bedienen. Augenschonend bleibt es trotzdem -- der
Unterschied laeuft ueber Groesse, Gewicht und Sperrung,
nicht ueber grelle Farben. */
.k__block {
margin: 20px 0 0; padding-top: 16px;
border-top: 1px solid rgba(255,255,255,.08);
}
.k__schild2 {
margin: 0 0 10px;
font-size: .74rem; font-weight: 700;
letter-spacing: .1em; text-transform: uppercase;
color: #9fb2c6;
}
.k__liste { margin: 0 0 8px; padding-left: 20px; color: #c5d4e3; }
.k__liste li { margin: 3px 0; font-size: .93rem; }
.k__liste:last-child { margin-bottom: 0; }
.k__woher { margin: 0 0 10px; color: #c5d4e3; font-size: .9rem; }
.k__unter { display: block; margin-top: 2px; color: #9fb2c6; font-size: .88rem; }
.k__gaben { display: flex; flex-wrap: wrap; gap: 7px; margin: 0; padding: 0; list-style: none; }
.k__gabe {
display: inline-flex; align-items: center; gap: 7px;
padding: 5px 11px; border-radius: 999px;
border: 1px solid rgba(226,193,90,.28);
background: rgba(226,193,90,.08);
font-size: .9rem; max-width: 100%;
}
.k__gname { font-weight: 600; overflow-wrap: anywhere; }
.k__coins {
padding: 1px 8px; border-radius: 999px;
background: rgba(226,193,90,.18); color: #e8d08f;
font-size: .82rem; font-weight: 700; white-space: nowrap;
}
.k__gzusatz { color: #9fb2c6; font-size: .84rem; overflow-wrap: anywhere; }
.k__abschnitt { margin: 0 0 14px; }
.k__abschnitt:last-child { margin-bottom: 0; }
.k__abschnitt[data-stufe="3"] {
margin-left: 2px; padding-left: 12px;
border-left: 2px solid rgba(255,255,255,.1);
}
.k__titel {
margin: 0 0 6px;
font-size: .8rem; font-weight: 700;
letter-spacing: .08em; text-transform: uppercase;
color: #d7e3f0;
}
.k__abschnitt[data-stufe="3"] > .k__titel {
font-size: .92rem; font-weight: 650;
letter-spacing: .01em; text-transform: none;
}
.k__absatz { margin: 0 0 7px; color: #c5d4e3; font-size: .93rem; }
.k__absatz:last-child { margin-bottom: 0; }
.k__frage { margin: 11px 0 5px; color: #e8f1fa; font-weight: 650; font-size: .93rem; }
.k__frage:first-child { margin-top: 0; }
@media (max-width: 480px) {
.k__abschnitt[data-stufe="3"] { padding-left: 9px; }
}
`; `;
/* ===================================================================
DIE BAUTEILE DER OFFENEN SEITE (07.10.2026)
ALLES GEHT DURCH `sicher()`. Diese Seite ist die einzige im
Haus ohne Anmeldung, und ihr Inhalt kommt aus einer FREMDEN
Quelle (einer TikTok-Seite). Ein `<script>` im Titel einer
Kampagne waere sonst ein Skript auf unserer Domain.
LEERE BLOECKE ERSCHEINEN NICHT. Eine Ueberschrift „Aufgaben"
ueber einer leeren Flaeche sieht aus, als sei das Laden
abgebrochen -- auf einer Seite, die jemand gerade in Discord
gepostet hat, ist das das Letzte, was man will.
=================================================================== */
/** Eine Zahl mit Tausenderpunkt, wie man sie liest. */
function coinText(n) {
/* `Number(null)` IST 0 -- und „0 Coins" waere eine falsche
Preisangabe. Derselbe Fehler ist mir am 07.10.2026 an einem Tag
zweimal passiert (Server und Kachel); hier steht er zum
dritten Mal und deshalb mit derselben Sicherung. */
if (n === null || n === undefined || n === "") return null;
const z = Number(n);
if (!Number.isFinite(z)) return null;
return `${z.toLocaleString("de-DE")} ${z === 1 ? "Coin" : "Coins"}`;
}
function blockAufgaben(liste) {
const a = (liste || []).filter(Boolean);
if (!a.length) return "";
return `<section class="k__block">
<h2 class="k__schild2">${a.length === 1 ? "Aufgabe" : "Aufgaben"}</h2>
<ul class="k__liste">${a.map((x) => `<li>${sicher(x)}</li>`).join("")}</ul>
</section>`;
}
function blockGaben(d) {
const g = (d.gaben || []).filter(Boolean);
if (!g.length) return "";
const kopf = d.gabenTitel
? `<p class="k__woher">${sicher(d.gabenTitel)}${
d.gabenUnter ? ` <span class="k__unter">${sicher(d.gabenUnter)}</span>` : ""}</p>`
: "";
return `<section class="k__block">
<h2 class="k__schild2">Diese Geschenke zählen</h2>
${kopf}
<ul class="k__gaben">${g.map((x) => {
const c = coinText(x.coins);
return `<li class="k__gabe"><span class="k__gname">${sicher(x.text)}</span>${
c ? `<span class="k__coins">${sicher(c)}</span>` : ""}${
x.zusatz ? `<span class="k__gzusatz">${sicher(x.zusatz)}</span>` : ""}</li>`;
}).join("")}</ul>
</section>`;
}
/** Die Zeilen eines Abschnitts in ihrer Reihenfolge.
* Aufeinanderfolgende Punkte kommen in EINE Liste; ein Absatz
* dazwischen beendet sie -- dieselbe Regel wie auf der Kachel, und
* aus demselben Grund: Bei den Teilnahmebedingungen steht zwischen
* zwei Aufzaehlungen ein Satz, der sich auf die ZWEITE bezieht. */
function zeilenHtml(zeilen) {
const teile = [];
let liste = null;
for (const z of zeilen || []) {
if (z.art === "punkt" || z.art === "geschenk") {
if (!liste) { liste = []; teile.push({ liste }); }
liste.push(z);
continue;
}
liste = null;
teile.push({ zeile: z });
}
return teile.map((s) => {
if (s.liste) {
return `<ul class="k__liste">${s.liste.map((z) => {
const c = coinText(z.coins);
return `<li>${sicher(z.text)}${c ? ` <span class="k__coins">${sicher(c)}</span>` : ""}</li>`;
}).join("")}</ul>`;
}
const klasse = s.zeile.art === "frage" ? "k__frage" : "k__absatz";
return `<p class="${klasse}">${sicher(s.zeile.text)}</p>`;
}).join("");
}
function blockRegeln(abschnitte) {
const a = (abschnitte || []).filter(Boolean);
if (!a.length) return "";
return `<section class="k__block">
<h2 class="k__schild2">Regeln &amp; Infos</h2>
${a.map((s) => `<div class="k__abschnitt" data-stufe="${s.stufe === 3 ? 3 : 2}">${
s.titel ? `<h3 class="k__titel">${sicher(s.titel)}</h3>` : ""}${
s.unter ? `<p class="k__unter">${sicher(s.unter)}</p>` : ""}${
zeilenHtml(s.zeilen)}</div>`).join("")}
</section>`;
}
function seitePlakat(d) { function seitePlakat(d) {
const bildMarke = d.bild const bildMarke = d.bild
? `<meta property="og:image" content="${sicher(d.bild)}" /> ? `<meta property="og:image" content="${sicher(d.bild)}" />
@@ -345,6 +588,9 @@ ${d.bild ? `<img class="k__bild" src="${sicher(d.bild)}" alt="" />` : ""}
<h1>${sicher(d.titel)}</h1> <h1>${sicher(d.titel)}</h1>
${d.zeitraum ? `<p class="k__zeit">${sicher(d.zeitraum)}</p>` : ""} ${d.zeitraum ? `<p class="k__zeit">${sicher(d.zeitraum)}</p>` : ""}
${d.text ? `<p class="k__text">${sicher(d.text)}</p>` : ""} ${d.text ? `<p class="k__text">${sicher(d.text)}</p>` : ""}
${blockAufgaben(d.aufgaben)}
${blockGaben(d)}
${blockRegeln(d.abschnitte)}
<p class="k__fuss">Geteilt aus dem Creator Workspace von Spicy Media.</p> <p class="k__fuss">Geteilt aus dem Creator Workspace von Spicy Media.</p>
</div> </div>
</main> </main>
+14 -14
View File
@@ -7,17 +7,17 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/anleitung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/anleitung.css?v=202610072238" />
<!-- module.css an VORLETZTER, haus.css an LETZTER Stelle. Die <!-- module.css an VORLETZTER, haus.css an LETZTER Stelle. Die
Reihenfolge ist keine Geschmacksfrage: haus.css setzt nur Reihenfolge ist keine Geschmacksfrage: haus.css setzt nur
Variablen, und bei Variablen entscheidet allein, wer zuletzt Variablen, und bei Variablen entscheidet allein, wer zuletzt
kommt. pruef-css-klassen.mjs misst genau dieses Paar nach. --> kommt. pruef-css-klassen.mjs misst genau dieses Paar nach. -->
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="halle"> <body class="start" data-buehne="halle">
@@ -289,16 +289,16 @@
Und anleitung.js braucht es doppelt: Symbol, Farbe, Name und Und anleitung.js braucht es doppelt: Symbol, Farbe, Name und
Adresse jeder Karte kommen von dort, nicht vom Server. --> Adresse jeder Karte kommen von dort, nicht vom Server. -->
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<!-- wahl.js gehört zu kopf.js: Der Sicht-Umschalter baut daraus seine <!-- wahl.js gehört zu kopf.js: Der Sicht-Umschalter baut daraus seine
Liste. pruef-css-klassen setzt das durch, und zwar auch die Liste. pruef-css-klassen setzt das durch, und zwar auch die
Reihenfolge -- kopf.js fragt beim Aufbau nach dem fertigen Reihenfolge -- kopf.js fragt beim Aufbau nach dem fertigen
Bedienelement. --> Bedienelement. -->
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/anleitung.js?v=202610071845" defer></script> <script src="assets/js/anleitung.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+5 -5
View File
@@ -16,8 +16,8 @@
verdeckt. Gefunden hat es pruef-struktur. verdeckt. Gefunden hat es pruef-struktur.
Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. --> Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. -->
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
<!-- Ohne diese drei nimmt ein iPhone einen Bildschirmausschnitt als <!-- Ohne diese drei nimmt ein iPhone einen Bildschirmausschnitt als
Symbol, Android zeichnet die Leiste weiss, und die Seite oeffnet Symbol, Android zeichnet die Leiste weiss, und die Seite oeffnet
ausserhalb der App -- auf einer Seite, die genau erklaeren soll, ausserhalb der App -- auf einer Seite, die genau erklaeren soll,
@@ -33,7 +33,7 @@
schreibt jede Seite app.webmanifest -- auch die hier. --> schreibt jede Seite app.webmanifest -- auch die hier. -->
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<style> <style>
/* Bewusst in der Seite und nicht in einer eigenen Datei: Diese Seite /* Bewusst in der Seite und nicht in einer eigenen Datei: Diese Seite
soll auch dann noch etwas anzeigen, wenn genau das schiefgeht, soll auch dann noch etwas anzeigen, wenn genau das schiefgeht,
@@ -135,7 +135,7 @@
<script src="assets/js/anruf-probe.js?v=202610071845" defer></script> <script src="assets/js/anruf-probe.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+4 -4
View File
@@ -23,25 +23,25 @@
}, },
"icons": [ "icons": [
{ {
"src": "/assets/img/app-symbole/workspace-192.png?v=202610071845", "src": "/assets/img/app-symbole/workspace-192.png?v=202610072238",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png", "type": "image/png",
"purpose": "any" "purpose": "any"
}, },
{ {
"src": "/assets/img/app-symbole/workspace-512.png?v=202610071845", "src": "/assets/img/app-symbole/workspace-512.png?v=202610072238",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png", "type": "image/png",
"purpose": "any" "purpose": "any"
}, },
{ {
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202610071845", "src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202610072238",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png", "type": "image/png",
"purpose": "maskable" "purpose": "maskable"
}, },
{ {
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202610071845", "src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202610072238",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png", "type": "image/png",
"purpose": "maskable" "purpose": "maskable"
+414 -14
View File
@@ -2679,7 +2679,12 @@ body[data-ton] .kopf-zeile::before {
Deshalb bekommt die reine Anzeigezeile dasselbe Raster wie der Deshalb bekommt die reine Anzeigezeile dasselbe Raster wie der
Knopf -- nur eben ohne Hintergrund und ohne Zeiger. */ Knopf -- nur eben ohne Hintergrund und ohne Zeiger. */
.ev-punkt:not(:has(.ev-punkt__knopf)):not(.ev-punkt--regel) { /* `:not(.ev-punkt--regel)` ist am 07.10.2026 entfallen: Diese
Klasse gibt es nicht mehr (sie heisst jetzt `.ev-zeile` und ist
kein `.ev-punkt`). Ein `:not()` auf eine Klasse, die niemand
mehr setzt, schliesst nichts aus und liest sich wie eine
Bedingung, die es noch gibt. */
.ev-punkt:not(:has(.ev-punkt__knopf)) {
display: flex; align-items: flex-start; gap: 10px; display: flex; align-items: flex-start; gap: 10px;
padding: 9px 10px; padding: 9px 10px;
font-size: .9rem; line-height: 1.45; font-size: .9rem; line-height: 1.45;
@@ -2768,22 +2773,30 @@ body[data-ton] .kopf-zeile::before {
.ev-punkt[data-abgehakt="ja"] .ev-punkt__text { color: var(--text-leise); } .ev-punkt[data-abgehakt="ja"] .ev-punkt__text { color: var(--text-leise); }
.ev-punkt__text { flex: 1 1 auto; overflow-wrap: anywhere; } .ev-punkt__text { flex: 1 1 auto; overflow-wrap: anywhere; }
/* Die Regelliste: dasselbe Raster, aber ein Strich statt eines /* Eine Zeile in einem Abschnitt: dasselbe Raster wie bei den
Kaestchens -- man sieht auf einen Blick, dass hier nichts zu tun Aufgaben, aber ein Strich statt eines Kaestchens -- man sieht auf
ist. */ einen Blick, dass hier nichts zu tun ist.
.ev-punkt--regel {
HIESS BIS ZUM 07.10.2026 `.ev-punkt--regel` und stand in einer
flachen Regelliste. Die gibt es nicht mehr (siehe `gliederung()`
in kampagne-lesen.mjs); umbenannt statt verdoppelt, damit nicht
zwei Zeilenstile mit demselben Aussehen nebeneinander liegen. */
.ev-zeile {
display: flex; align-items: flex-start; gap: 10px; display: flex; align-items: flex-start; gap: 10px;
padding: 6px 10px; margin: 0; padding: 5px 2px;
font-size: .87rem; line-height: 1.5; font-size: .87rem; line-height: 1.55;
color: var(--text-leise); color: var(--text-leise);
} }
.ev-punkt__zeichen { .ev-zeile__text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.ev-punkt__zeichen,
.ev-zeile__zeichen {
flex: none; flex: none;
width: 19px; height: 19px; width: 19px; height: 19px;
margin-top: 1px; margin-top: 1px;
position: relative; position: relative;
} }
.ev-punkt__zeichen::before { .ev-punkt__zeichen::before,
.ev-zeile__zeichen::before {
content: ""; content: "";
position: absolute; left: 3px; top: 9px; position: absolute; left: 3px; top: 9px;
width: 11px; height: 1.5px; width: 11px; height: 1.5px;
@@ -3152,12 +3165,36 @@ body[data-ton] .kopf-zeile::before {
/* Der Abstand kommt jetzt vom Raster. Zwei Abstandsquellen ergeben /* Der Abstand kommt jetzt vom Raster. Zwei Abstandsquellen ergeben
einen doppelten Abstand -- und zwar nur zwischen den Zeilen, was einen doppelten Abstand -- und zwar nur zwischen den Zeilen, was
schwer zu finden ist. */ schwer zu finden ist. */
/* ==== `.kv-seite` OHNE `>` -- DAS FUENFTE MAL (07.10.2026) =====
Hier stand `.kv-seite > .eintrag-karte`. Seit die Spalten nach
Zustand und Alter gegliedert sind, haengen die Karten in einem
`.ev-gruppe__inhalt` -- also eine Ebene tiefer, und der
Direktkind-Selektor haette aufgehoert zu greifen. Folge waere
gewesen: Eventkacheln ohne `display: flex`, also ohne den Fuss
unten und mit der Buehne eingerahmt vom Kartenpolster.
DAS IST IM PROJEKT SCHON VIERMAL PASSIERT -- zuletzt am
07.10.2026 bei `#liste > .eintrag-karte[data-event=...]`, wo
alle drei Zustandsfarben ausfielen und ein Feature vom Vortag
rueckgaengig gemacht wurde, ohne dass eine Pruefung rot wurde.
Diesmal vorher gesehen, nicht hinterher gemessen.
WAS DIREKTKIND BLEIBT UND WARUM: `> *:not(.ev-buehne)` meint
die eigenen Kinder DER KARTE. Die Karte ist nach wie vor der
Vater ihres Inhalts; dieser Selektor beschreibt also wirklich
eine Vater-Kind-Beziehung und nicht eine Lage im Baum. Nur die
Strecke von der SPALTE zur Karte wird frei.
Dass `.kv-seite` ueberhaupt davorsteht, bleibt noetig: Ohne sie
hiesse die Regel „jede Eventkachel ueberall" -- und `#liste >`
darueber waere dann sinnlos. */
#liste > .eintrag-karte, #liste > .eintrag-karte,
.kv-seite > .eintrag-karte, .kv-seite .eintrag-karte,
.kv-rest > .eintrag-karte { margin-bottom: 0; } .kv-rest > .eintrag-karte { margin-bottom: 0; }
#liste > .eintrag-karte[data-eventkarte="ja"], #liste > .eintrag-karte[data-eventkarte="ja"],
.kv-seite > .eintrag-karte[data-eventkarte="ja"] { .kv-seite .eintrag-karte[data-eventkarte="ja"] {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
/* Die Buehne liegt am oberen Rand und soll ihn beruehren -- das /* Die Buehne liegt am oberen Rand und soll ihn beruehren -- das
@@ -3166,13 +3203,13 @@ body[data-ton] .kopf-zeile::before {
padding: 0 0 15px; padding: 0 0 15px;
} }
#liste > .eintrag-karte[data-eventkarte="ja"] > *:not(.ev-buehne), #liste > .eintrag-karte[data-eventkarte="ja"] > *:not(.ev-buehne),
.kv-seite > .eintrag-karte[data-eventkarte="ja"] > *:not(.ev-buehne) { .kv-seite .eintrag-karte[data-eventkarte="ja"] > *:not(.ev-buehne) {
padding-left: 18px; padding-right: 18px; padding-left: 18px; padding-right: 18px;
} }
#liste > .eintrag-karte[data-eventkarte="ja"] .ev-block, #liste > .eintrag-karte[data-eventkarte="ja"] .ev-block,
.kv-seite > .eintrag-karte[data-eventkarte="ja"] .ev-block { margin-left: 0; margin-right: 0; } .kv-seite .eintrag-karte[data-eventkarte="ja"] .ev-block { margin-left: 0; margin-right: 0; }
#liste > .eintrag-karte[data-eventkarte="ja"] .eintrag-fuss, #liste > .eintrag-karte[data-eventkarte="ja"] .eintrag-fuss,
.kv-seite > .eintrag-karte[data-eventkarte="ja"] .eintrag-fuss { margin-top: auto; } .kv-seite .eintrag-karte[data-eventkarte="ja"] .eintrag-fuss { margin-top: auto; }
/* ===================================================================== /* =====================================================================
DIE KARTE SAGT, OB DAS EVENT LAEUFT (07.10.2026) DIE KARTE SAGT, OB DAS EVENT LAEUFT (07.10.2026)
@@ -3740,3 +3777,366 @@ body[data-ton] .kopf-zeile::before {
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.kampagneholen textarea { transition: none; } .kampagneholen textarea { transition: none; }
} }
/* =====================================================================
GEGLIEDERTE INFOS, GESCHENKE, AUFKLAPPEN, GRUPPEN (07.10.2026)
Filipe, mit einem Bildschirmfoto des Regelblocks: „man soll die
titel in den texten besser erkennen und sehen was zu was passt.
ich will auch dass man sieht welche geschenke bei dem event zaehlen
und so. ... die kacheln von den events soll auch nicht so lang
sein, die soll man aufklappen koennen damit die nicht zu lange
sind. genau wie eine trennung fuer heute, letzte woche letzten
monat und so."
AUGENSCHONEND (CLAUDE.md, 12.08.2026): Die Unterscheidung laeuft
ueber GROESSE, GEWICHT, SPERRUNG und EINRUECKUNG -- nicht ueber
grelle Farben. Eine Ueberschrift darf man sehen, ohne dass sie
leuchtet. Die einzige kraeftige Farbe im ganzen Block ist der
Coinpreis, und der ist ein gedecktes Gold, kein Neon.
===================================================================== */
/* ---- Der Abschnitt -------------------------------------------------- */
.ev-abschnitte {
display: flex; flex-direction: column;
gap: 14px;
}
.ev-abschnitt { margin: 0; }
/* STUFE 2 IST EIN EIGENER ABSCHNITT (KAMPAGNENGESCHENKE, BELOHNUNGEN,
TEILNAHME), STUFE 3 EINE UEBERSCHRIFT AUS EINEM FREMDEN TEXT
("Superstars", "1. Liga", "WELCHE LIGA BIN ICH?").
Die Einrueckung macht die Verschachtelung sichtbar, ohne dass der
HTML-Baum sie nachbauen muss -- die Gliederung bleibt eine flache
Liste (Begruendung bei `gliederung()` in kampagne-lesen.mjs). */
.ev-abschnitt[data-stufe="3"] {
margin-left: 2px;
padding-left: 11px;
border-left: 2px solid color-mix(in srgb, var(--rand) 85%, transparent);
}
.ev-abschnitt__titel {
margin: 0 0 6px;
/* .78rem = 12,5 px. In Grossbuchstaben mit Sperrung wirkt eine
Schrift kleiner, als die Zahl sagt -- dieselbe Begruendung steht
schon bei .ev-block__schild. */
font-size: .78rem; font-weight: 700;
letter-spacing: .08em; text-transform: uppercase;
line-height: 1.4;
color: color-mix(in srgb, var(--ev-ton, var(--akzent, #3fbdf5)) 55%, var(--text));
}
/* Die Unterstufe ist KEINE Grossschreibung: Sie kommt aus einem
fremden Text, und "Du kannst an dieser Liga teilnehmen" in
Grossbuchstaben waere geschrien. Sie unterscheidet sich durch
Gewicht und Farbe, nicht durch Lautstaerke. */
.ev-abschnitt[data-stufe="3"] > .ev-abschnitt__titel {
font-size: .85rem; font-weight: 650;
letter-spacing: .01em; text-transform: none;
color: var(--text);
}
.ev-abschnitt__unter {
margin: -2px 0 7px;
font-size: .82rem; line-height: 1.5;
color: var(--text-still);
}
/* Ein Absatz. `--text-leise` und nicht `--text`: Der Regeltext ist
Nachschlagewerk, nicht das, weswegen jemand die Kachel aufmacht. */
.ev-absatz {
margin: 0 0 7px;
font-size: .87rem; line-height: 1.6;
color: var(--text-leise);
overflow-wrap: anywhere;
}
.ev-absatz:last-child { margin-bottom: 0; }
/* Eine Frage mit Antwort darunter -- die Gliederung der
Teilnahmebedingungen. Begruendung bei `gliederung()`. */
.ev-frage {
margin: 11px 0 5px;
font-size: .87rem; font-weight: 650; line-height: 1.5;
color: var(--text);
overflow-wrap: anywhere;
}
.ev-frage:first-child { margin-top: 0; }
.ev-zeilen {
list-style: none;
margin: 0 0 7px; padding: 0;
display: flex; flex-direction: column; gap: 1px;
}
.ev-zeilen:last-child { margin-bottom: 0; }
/* ---- Die Geschenke -------------------------------------------------- */
/* DER EINE BLOCK, DER NICHT HINTER DEM AUFKLAPPEN LIEGT.
"Welche Geschenke zaehlen" ist die Frage, die am haeufigsten
gestellt wird, und die Antwort passt in eine Zeile. Der Rahmen in
gedecktem Gold grenzt ihn vom Aufgabenblock daneben ab, ohne mit
ihm zu konkurrieren. */
.ev-block--gaben {
border-color: color-mix(in srgb, #e2c15a 26%, var(--rand));
background:
linear-gradient(180deg,
color-mix(in srgb, #e2c15a 6%, transparent),
transparent 60%),
rgba(255, 255, 255, .022);
}
.ev-gaben__woher {
margin: 0 0 9px;
font-size: .82rem; line-height: 1.45;
color: var(--text-leise);
}
.ev-gaben__unter {
display: block; margin-top: 2px;
font-size: .8rem; color: var(--text-still);
}
.ev-gaben {
display: flex; flex-wrap: wrap; gap: 7px;
margin: 0; padding: 0; list-style: none;
}
/* Eine Plakette je Geschenk, nicht eine Zeile: Fuenf Geschenke
untereinander sind fuenf Zeilen, nebeneinander eine. Genau darum
ging es -- die Kachel soll kuerzer werden. */
.ev-gabe {
display: inline-flex; align-items: center; gap: 7px;
margin: 0; padding: 5px 10px;
border: 1px solid color-mix(in srgb, #e2c15a 22%, var(--rand));
border-radius: 999px;
background: color-mix(in srgb, #e2c15a 7%, rgba(255, 255, 255, .02));
font-size: .84rem; line-height: 1.35;
color: var(--text);
max-width: 100%;
}
.ev-gabe__name { font-weight: 600; overflow-wrap: anywhere; }
/* Der Coinpreis mit einem Muenzpunkt davor. `tabular-nums`, damit
1 und 12.000 untereinander dieselbe Breite je Ziffer haben. */
.ev-gabe__coins {
display: inline-flex; align-items: center; gap: 5px;
flex: none;
padding: 1px 8px 1px 7px;
border-radius: 999px;
background: color-mix(in srgb, #e2c15a 17%, transparent);
color: color-mix(in srgb, #e2c15a 78%, var(--text));
font-size: .79rem; font-weight: 700;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.ev-gabe__coins::before {
content: "";
flex: none;
width: 8px; height: 8px;
border-radius: 50%;
background: color-mix(in srgb, #e2c15a 72%, transparent);
}
/* Der Hinweis zum Geschenk ("Sammle Feuerwerk in deinem LIVE!") --
bei einem Kampagnengeschenk steht hier, WIE man es bekommt, und
das ist die Auskunft anstelle eines Preises. */
.ev-gabe__zusatz {
font-size: .79rem; color: var(--text-still);
overflow-wrap: anywhere;
}
.ev-gabe__zusatz::before {
content: "·";
margin-right: 5px;
opacity: .7;
}
/* ---- Die Hinweise --------------------------------------------------- */
/* WAS BEIM EINLESEN GEFEHLT HAT -- still, aber nicht versteckt.
Im Bildschirmfoto vom 07.10.2026 standen diese Zeilen MITTEN in
der Regelliste, mit demselben Strich davor wie eine Regel. "Diese
Kampagne hat keine Preisliste auf der Seite" las sich damit wie
eine Teilnahmebedingung. Jetzt stehen sie unter den Bloecken, in
einem eigenen Kasten, mit einem Fragezeichen-Kreis davor. */
.ev-hinweise {
display: flex; flex-direction: column; gap: 5px;
margin: 12px 0 0;
padding: 10px 13px;
border: 1px dashed color-mix(in srgb, var(--rand) 85%, transparent);
border-radius: var(--radius-klein);
background: rgba(255, 255, 255, .012);
}
.ev-hinweis {
display: flex; align-items: flex-start; gap: 8px;
margin: 0;
font-size: .81rem; line-height: 1.5;
color: var(--text-still);
overflow-wrap: anywhere;
}
.ev-hinweis::before {
content: "i";
flex: none;
width: 16px; height: 16px; margin-top: 2px;
border: 1px solid color-mix(in srgb, var(--text-still) 45%, transparent);
border-radius: 50%;
/* .74rem = 11,84 px. NICHT kleiner: pruef-css-klassen zieht die
Grenze bei 11,5 px, und sie gilt auch fuer ein einzelnes Zeichen
-- wer eine Ausnahme fuer "nur ein i" macht, macht die naechste
fuer "nur eine Zahl". Der Kreis ist dafuer 16 px statt 15. */
font-size: .74rem; font-weight: 700; font-style: italic;
line-height: 14px; text-align: center;
color: var(--text-still);
}
/* ---- Der Steckbrief ------------------------------------------------- */
.ev-block--steck { background: rgba(255, 255, 255, .012); }
.ev-steck {
display: grid;
/* Name und Wert in zwei Spalten: `auto` fuer den Namen, damit er
nur so breit wird, wie der laengste ihn braucht. */
grid-template-columns: auto 1fr;
gap: 4px 14px;
margin: 0;
}
.ev-steck__name {
margin: 0;
font-size: .78rem; font-weight: 600;
letter-spacing: .03em;
color: var(--text-still);
}
.ev-steck__wert {
margin: 0;
font-size: .82rem;
font-variant-numeric: tabular-nums;
color: var(--text-leise);
overflow-wrap: anywhere;
}
/* ---- Aufklappen: alles Uebrige hinter einem Druck ------------------- */
.ev-mehr { margin-top: 12px; }
.ev-mehr__knopf {
display: flex; align-items: center; gap: 9px;
width: 100%;
padding: 8px 12px;
font: inherit; font-size: .78rem; font-weight: 600;
letter-spacing: .04em; text-transform: uppercase;
color: var(--text-leise);
text-align: left; cursor: pointer;
background: color-mix(in srgb, var(--tinte) 88%, #ffffff 12%);
border: 1px dashed color-mix(in srgb, var(--rand) 80%, transparent);
border-radius: 9px;
}
.ev-mehr__knopf:hover, .ev-mehr__knopf:focus-visible {
color: var(--text);
border-style: solid;
border-color: color-mix(in srgb, var(--ev-ton, var(--akzent)) 45%, transparent);
}
.ev-mehr__wort { flex: 0 0 auto; }
/* DIE ZAHL SAGT, WAS DAHINTER STEHT. Ein zugeklappter Kasten ohne
Zahl sieht aus, als sei er leer -- und dann drueckt niemand. */
.ev-mehr__zahl {
flex: 1 1 auto; min-width: 0;
font-size: .76rem; font-weight: 500;
letter-spacing: .01em; text-transform: none;
color: var(--text-still);
}
.ev-mehr__knopf .klapp-pfeil { flex: none; }
.ev-mehr__inhalt { margin-top: 10px; }
.ev-mehr__inhalt > .ev-block + .ev-block { margin-top: 10px; }
/* ---- Die Gruppen einer Spalte --------------------------------------- */
/* Dasselbe Aussehen wie die Zeitgruppen der anderen Bretter
(.zeitgruppe__kopf) -- ein zweiter Stil fuer dieselbe Sache waere
eine zweite Wahrheit darueber, wie eine Gruppe aussieht. Nur der
Ton kommt aus der Spalte (`--kv-ton`): So gehoeren die Gruppen
sichtbar zu ihrer Seite der Trennung. */
.ev-gruppe__kopf {
display: flex; align-items: center; gap: 9px;
width: 100%; margin: 10px 0 0;
padding: 7px 10px;
font: inherit; font-size: .76rem; font-weight: 600;
letter-spacing: .04em; text-transform: uppercase;
color: var(--text-leise);
text-align: left; cursor: pointer;
background: color-mix(in srgb, var(--tinte) 88%, #ffffff 12%);
border: 1px solid color-mix(in srgb, var(--rand) 70%, transparent);
border-left: 3px solid color-mix(in srgb, var(--kv-ton, var(--akzent)) 45%, transparent);
border-radius: 9px;
}
.ev-gruppe__kopf:hover, .ev-gruppe__kopf:focus-visible {
color: var(--text);
border-color: color-mix(in srgb, var(--kv-ton, var(--akzent)) 45%, transparent);
}
/* Die erste Gruppe braucht oben keinen Abstand -- darueber steht
schon der Kopf der Spalte. */
.kv-seite__kopf + .ev-gruppe__kopf { margin-top: 2px; }
/* WAS LAEUFT, STEHT KRAEFTIGER DA ALS WAS VORBEI IST. Ein
zugeklappter Kasten „Vorbei — aelter" soll nicht so aussehen wie
„Laeuft gerade": Die Ueberschrift ist zugleich die Auskunft. */
.ev-gruppe__kopf[data-gruppe="laeuft"] {
color: var(--text);
background: color-mix(in srgb, var(--kv-ton, var(--akzent)) 9%, var(--tinte));
}
.ev-gruppe__kopf[data-gruppe^="vorbei"] { opacity: .78; }
.ev-gruppe__wort { flex: 1 1 auto; min-width: 0; }
.ev-gruppe__zahl {
flex: none;
padding: 1px 8px;
font-size: .76rem; font-variant-numeric: tabular-nums;
color: var(--text);
background: color-mix(in srgb, var(--tinte) 70%, #ffffff 30%);
border-radius: 999px;
}
.ev-gruppe__inhalt {
display: flex; flex-direction: column; gap: 12px;
margin-top: 8px;
}
/* ---- Was zu einer Aufgabe noch dasteht ------------------------------ */
/* Beschreibung und Zeitraum einer Missionsgruppe. EINGERUECKT auf die
Hoehe des Aufgabentextes (19 px Kaestchen + 10 px Luecke = 29 px),
damit es unter dem Text haengt und nicht unter dem Kaestchen. */
.ev-punkt__zusatz {
display: flex; flex-wrap: wrap; align-items: baseline;
gap: 4px 9px;
margin: -2px 0 6px 39px;
font-size: .79rem; line-height: 1.45;
color: var(--text-still);
}
.ev-punkt__spanne {
flex: none;
padding: 1px 7px;
border-radius: 999px;
background: color-mix(in srgb, var(--rand) 55%, transparent);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.ev-punkt__worte { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
/* ---- Handy ---------------------------------------------------------- */
@media (max-width: 560px) {
/* Auf dem Handy wird das Polster knapp. Die Einrueckung der
Unterabschnitte halbiert sich, damit der Text nicht in einer
Spalte von acht Zeichen endet. */
.ev-abschnitt[data-stufe="3"] { padding-left: 8px; }
.ev-punkt__zusatz { margin-left: 0; }
/* 44 px Tippziel -- dieselbe Vorgabe wie bei .ev-punkt__knopf. */
.ev-mehr__knopf, .ev-gruppe__kopf { min-height: 44px; }
}
/* ---- Starker Kontrast ----------------------------------------------- */
@media (forced-colors: active) {
/* `color-mix` auf Systemfarben ergibt im Zwangsmodus nichts
Sichtbares -- deshalb echte Raender und Systemfarben. */
.ev-gabe, .ev-hinweise, .ev-mehr__knopf, .ev-gruppe__kopf {
border: 1px solid CanvasText;
}
.ev-gabe__coins { border: 1px solid CanvasText; }
.ev-abschnitt[data-stufe="3"] { border-left: 2px solid CanvasText; }
.ev-abschnitt__titel { color: CanvasText; }
}
/* ---- Weniger Bewegung ----------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
.ev-mehr__knopf, .ev-gruppe__kopf { transition: none; }
}
+391 -22
View File
@@ -375,6 +375,61 @@
} }
/* ==== IN WELCHE GRUPPE EINER SPALTE GEHOERT DIESES EVENT? ====
Filipe: „genau wie eine trennung fuer heute, letzte woche
letzten monat und so."
DIE ERSTE FRAGE AN EIN EVENT IST NICHT SEIN ALTER, SONDERN
SEIN ZUSTAND. „Laeuft gerade" und „ist vorbei" entscheiden,
ob man heute etwas tut; ob es vorgestern oder vorletzten
Monat vorbei war, entscheidet nichts mehr. Deshalb zuerst
laeuft / kommt / vorbei.
DAS ALTER TEILT DANN DAS AUF, WAS SICH ANSAMMELT -- und das
ist genau eines: das Vergangene. Von „laeuft gerade" gibt es
nie viele, von „vorbei" mit der Zeit beliebig viele. Genau
dort setzt Filipes Wunsch an, und nur dort traegt er etwas.
GERECHNET WIRD AUF DEM ENDE, nicht auf dem Anlagedatum: Ein
Event, das letzte Woche angelegt wurde und naechsten Monat
endet, ist nicht „letzte Woche".
KALENDERBEZOGEN, NICHT IN TAGEN GEZAEHLT -- dieselbe
Ueberlegung wie bei ZEITSTUFEN weiter unten: „diese Woche"
heisst seit Montag. Am Dienstag ist der Freitag davor nicht
„diese Woche".
EINE LEERE GRUPPE ERSCHEINT NICHT (siehe `continue` beim
Bauen) -- fuenf leere Ueberschriften waeren schlimmer als
eine lange Spalte. */
const EV_GRUPPEN = [
['laeuft', 'Läuft gerade', false],
['kommt', 'Kommt noch', false],
['vorbei_woche', 'Vorbei — diese Woche', true],
['vorbei_monat', 'Vorbei — dieser Monat', true],
['vorbei_alt', 'Vorbei — älter', true],
];
function evGruppeVon(e) {
const st = eventStand(e);
if (!st) return 'vorbei_alt';
if (st.schluessel !== 'vorbei') return st.schluessel;
const heute = window.heuteLokal
? window.heuteLokal() : new Date().toISOString().slice(0, 10);
const tag = new Date(heute + 'T00:00:00');
const ende = new Date(String(e.event_ende || e.datum || '').slice(0, 10) + 'T00:00:00');
if (Number.isNaN(ende.getTime()) || Number.isNaN(tag.getTime())) return 'vorbei_alt';
/* Montag als Wochenanfang -- getDay() liefert Sonntag = 0, und
in Deutschland steht der am Ende. */
const montag = new Date(tag);
montag.setDate(tag.getDate() - ((tag.getDay() + 6) % 7));
if (ende >= montag) return 'vorbei_woche';
const ersterDesMonats = new Date(tag.getFullYear(), tag.getMonth(), 1);
if (ende >= ersterDesMonats) return 'vorbei_monat';
return 'vorbei_alt';
}
/* ===================================================================== /* =====================================================================
WIE LANGE NOCH? (03.10.2026) WIE LANGE NOCH? (03.10.2026)
@@ -798,6 +853,30 @@
kopf.append(zaehler); kopf.append(zaehler);
block.append(kopf); block.append(kopf);
/* WAS ZU EINER AUFGABE NOCH DASTEHT (07.10.2026).
Bei „LIVE Glow Up" hat jede Missionsgruppe eine eigene
Beschreibung (`displayInfo.descText`) und einen eigenen
Zeitraum (`config[0].startTime/endTime`) -- beides stand in
der Kampagnenseite und wurde bis heute nicht gelesen. Es
gehoert UNTER die Aufgabe, nicht in den Regeltext: „Erfuelle
diese Missionen taeglich" ist eine Auskunft ueber DIESE
Aufgabe.
NICHT IN DEN KNOPF. Der Knopf ist die Beschriftung des
Hakens; was darunter steht, hakt man nicht ab. Wer auf die
Beschreibung tippt, soll nichts umschalten. */
const zusatzAnhaengen = (zeile, p) => {
if (!p.beschreibung && !(p.von_tag && p.bis_tag)) return;
const z = el('div', 'ev-punkt__zusatz');
if (p.von_tag && p.bis_tag) {
z.append(el('span', 'ev-punkt__spanne',
datum(p.von_tag) + ' – ' + datum(p.bis_tag)));
}
if (p.beschreibung) z.append(el('span', 'ev-punkt__worte', p.beschreibung));
zeile.append(z);
};
const liste = el('ul', 'ev-liste'); const liste = el('ul', 'ev-liste');
for (const p of punkte) { for (const p of punkte) {
const zeile = el('li', 'ev-punkt'); const zeile = el('li', 'ev-punkt');
@@ -807,6 +886,7 @@
zeile.dataset.abgehakt = p.meiner ? 'ja' : ''; zeile.dataset.abgehakt = p.meiner ? 'ja' : '';
zeile.append(el('span', 'ev-punkt__kaestchen')); zeile.append(el('span', 'ev-punkt__kaestchen'));
zeile.append(el('span', 'ev-punkt__text', p.text)); zeile.append(el('span', 'ev-punkt__text', p.text));
zusatzAnhaengen(zeile, p);
liste.append(zeile); liste.append(zeile);
continue; continue;
} }
@@ -851,6 +931,7 @@
} finally { knopf.disabled = false; } } finally { knopf.disabled = false; }
}); });
zeile.append(knopf); zeile.append(knopf);
zusatzAnhaengen(zeile, p);
liste.append(zeile); liste.append(zeile);
} }
block.append(liste); block.append(liste);
@@ -873,30 +954,229 @@
return block; return block;
} }
/** Die Regeln -- dieselbe Liste, aber ohne Haken. /* =====================================================================
DIE INFOS EINER KACHEL -- GEGLIEDERT STATT FLACH (07.10.2026)
Filipe, mit einem Bildschirmfoto des Regelblocks: „man soll die
titel in den texten besser erkennen und sehen was zu was passt.
ich will auch dass man sieht welche geschenke bei dem event
zaehlen und so. ... die kacheln von den events soll auch nicht
so lang sein, die soll man aufklappen koennen."
HIER STAND EINE EINZIGE FLACHE LISTE. Jede Zeile derselbe
Strich, gleiche Groesse, gleiche Farbe -- „INFOS ZUR KAMPAGNE"
sah aus wie der Absatz darunter, und die Hinweise („Diese
Kampagne hat keine Preisliste") standen zwischen den Regeln,
als waeren sie welche.
DIE GLIEDERUNG KOMMT VOM SERVER (`event_regelabschnitte`, aus
`gliederung()` in kampagne-lesen.mjs). Hier wird nur gemalt --
und zwar genau einmal im Haus: Die Teilen-Seite benutzt
dieselbe Zerlegung. Haetten Kachel und Teilen-Seite je ihre
eigene, wuerden sie irgendwann Verschiedenes zeigen, und
niemand koennte sagen, welche von beiden recht hat.
DREI SORTEN INHALT, DREI ORTE:
Geschenke immer sichtbar, als Plaketten mit Coinpreis --
das ist die Frage, die am haeufigsten gestellt
wird, und sie passt in eine Zeile.
Hinweise immer sichtbar, aber still und abgesetzt. „Die
Preise konnten nicht gelesen werden -- bitte
nachtragen" ist eine Aufgabe; sie hinter einen
Druck zu legen heisst, sie zu verstecken.
Alles Uebrige hinter EINEM Druck. Bei „Gipfelstuermer" sind
das rund 7000 Zeichen -- sechs Preisgruppen, sechs
Ligen, Teilnahmebedingungen. Das ist die Laenge,
die Filipe gemeint hat.
===================================================================== */
/** Eine Coinzahl, wie man sie liest: 12000 -> 12.000 Coins.
* *
* BEWUSST ANDERS ALS DIE AUFGABEN. Vorher waren beide derselbe graue * `Number(null)` IST 0, UND 0 IST ENDLICH -- derselbe Fehler
* Kasten mit derselben Ueberschriftzeile; nebeneinander sahen "was * wie in `gliederung()`, und er ist mir am 07.10.2026 an EINEM
* ich tun soll" und "was dabei gilt" gleich wichtig aus. Eine Regel * Tag ZWEIMAL passiert: einmal auf dem Server, einmal hier. Der
* ist aber kein Schritt: Man hakt sie nicht ab, man haelt sie ein. */ * Server lieferte danach brav `coins: null`, und die Kachel
function eventRegeln(e) { * schrieb „0 Coins" an das Feuerwerk -- also eine Preisangabe,
const punkte = Array.isArray(e.event_regelpunkte) ? e.event_regelpunkte : []; * und zwar eine falsche.
if (!punkte.length) return null; *
const block = el('section', 'ev-block ev-block--regeln'); * Gefunden hat es nicht die Serverpruefung (die war gruen),
* sondern die Gegenprobe im Browser. Zwei Stellen, zwei
* Messungen -- eine Pruefung auf der einen Seite beweist nichts
* ueber die andere. */
function coinText(n) {
if (n === null || n === undefined || n === '') return null;
const z = Number(n);
if (!Number.isFinite(z)) return null;
/* „1 Coins" ist der Schoenheitsfehler, der wie ein Fehler im
Einlesen aussieht -- und das Erste, was jemand meldet. */
return z.toLocaleString('de-DE') + (z === 1 ? ' Coin' : ' Coins');
}
/** Ein Geschenk als Plakette: Name, Coinpreis, Hinweis. */
function evGabe(z) {
const li = el('li', 'ev-gabe');
li.append(el('span', 'ev-gabe__name', z.text));
const c = coinText(z.coins);
/* KEIN PREIS IST NICHT NULL COINS. „Feuerwerk" bei LIVE Glow Up
ist ein Kampagnengeschenk ohne Coinpreis; „0 Coins" daneben
waere eine Preisangabe, und zwar eine falsche. */
if (c) li.append(el('span', 'ev-gabe__coins', c));
if (z.zusatz) li.append(el('span', 'ev-gabe__zusatz', z.zusatz));
return li;
}
/** Eine Zeile eines Abschnitts -- Absatz, Frage, Punkt, Geschenk. */
function evZeile(z) {
if (z.art === 'geschenk') return evGabe(z);
if (z.art === 'punkt') {
const li = el('li', 'ev-zeile');
li.append(el('span', 'ev-zeile__zeichen'));
li.append(el('span', 'ev-zeile__text', z.text));
return li;
}
if (z.art === 'hinweis') return el('p', 'ev-hinweis', z.text);
if (z.art === 'frage') return el('p', 'ev-frage', z.text);
return el('p', 'ev-absatz', z.text);
}
/** Zeilen in ihrer REIHENFOLGE anhaengen.
*
* Aufeinanderfolgende Punkte kommen in EINE Liste, ein Absatz
* dazwischen beendet sie. Erst alle Absaetze und dann alle Punkte
* zu malen waere einfacher und falsch: Bei den
* Teilnahmebedingungen steht zwischen zwei Aufzaehlungen ein
* Satz, der sich auf die ZWEITE bezieht -- umsortiert bezieht er
* sich auf nichts. */
function evZeilenAnhaengen(ziel, zeilen) {
let liste = null;
for (const z of zeilen || []) {
const k = evZeile(z);
if (k.tagName === 'LI') {
if (!liste) {
liste = el('ul', 'ev-zeilen');
if (z.art === 'geschenk') liste.classList.add('ev-gaben');
ziel.append(liste);
}
liste.append(k);
continue;
}
liste = null;
ziel.append(k);
}
}
/** Ein Abschnitt mit seiner Ueberschrift. */
function evAbschnitt(a) {
const s = el('section', 'ev-abschnitt');
s.dataset.stufe = String(a.stufe || 2);
if (a.art) s.dataset.art = a.art;
if (a.titel) s.append(el('h5', 'ev-abschnitt__titel', a.titel));
if (a.unter) s.append(el('p', 'ev-abschnitt__unter', a.unter));
evZeilenAnhaengen(s, a.zeilen);
return s;
}
/** Die Geschenke, die zaehlen -- der eigene Block oben auf der Kachel. */
function evGabenBlock(a) {
const gaben = (a.zeilen || []).filter((z) => z.art === 'geschenk');
if (!gaben.length) return null;
const block = el('section', 'ev-block ev-block--gaben');
const kopf = el('div', 'ev-block__kopf'); const kopf = el('div', 'ev-block__kopf');
kopf.append(el('h4', 'ev-block__schild', 'Regeln')); kopf.append(el('h4', 'ev-block__schild', 'Diese Geschenke zählen'));
kopf.append(el('span', 'ev-zaehler', String(gaben.length)));
block.append(kopf); block.append(kopf);
const liste = el('ul', 'ev-liste ev-liste--still'); /* DIE UEBERSCHRIFT DER KAMPAGNE BLEIBT STEHEN. „Sammle dein
for (const p of punkte) { Feuerwerk -- Erhalte doppelte Punkte fuer die Rangliste" sagt,
const zeile = el('li', 'ev-punkt ev-punkt--regel'); WARUM diese Geschenke zaehlen. Ein selbst erfundenes
zeile.append(el('span', 'ev-punkt__zeichen')); „Geschenke" daneben haette diese Auskunft verschluckt. */
zeile.append(el('span', 'ev-punkt__text', p.text)); if (a.titel) {
liste.append(zeile); const woher = el('p', 'ev-gaben__woher', a.titel);
if (a.unter) woher.append(el('span', 'ev-gaben__unter', a.unter));
block.append(woher);
}
const liste = el('ul', 'ev-zeilen ev-gaben');
for (const z of gaben) liste.append(evGabe(z));
block.append(liste);
/* Was im Geschenkabschnitt steht und KEIN Geschenk ist, geht
nicht verloren -- sonst haengt die Vollstaendigkeit der
Kachel daran, dass TikTok dort nie etwas anderes hinschreibt. */
evZeilenAnhaengen(block, (a.zeilen || []).filter((z) => z.art !== 'geschenk'));
return block;
}
/** Was beim Einlesen gefehlt hat -- still, aber sichtbar. */
function evHinweisBlock(zeilen) {
if (!zeilen.length) return null;
const kasten = el('aside', 'ev-hinweise');
for (const z of zeilen) kasten.append(el('p', 'ev-hinweis', z.text));
return kasten;
}
/** Der Steckbrief: die harten Tatsachen aus der Kampagnenseite. */
const STECK_NAME = {
nummer: 'Kampagnennummer', kurznummer: 'Kurznummer',
region: 'Markt', sprache: 'Sprache', verbund: 'Verbund',
zeitzone: 'Zeitzone',
anmeldung_ab: 'Anmeldung ab', ergebnis_bis: 'Ergebnisse bis',
};
const STECK_DATUM = new Set(['anmeldung_ab', 'ergebnis_bis']);
function evSteckbrief(e) {
const s = e.event_steckbrief;
if (!s || typeof s !== 'object') return null;
/* UEBER STECK_NAME GELAUFEN, nicht ueber die Felder des Servers:
So bestimmt die Reihenfolge dieser Liste die Reihenfolge auf
dem Bildschirm, und ein neues Feld erscheint erst, wenn es
hier auch einen deutschen Namen hat. Sonst stuende irgendwann
„activity_kind_flag: 1" auf der Kachel. */
const paare = Object.keys(STECK_NAME)
.filter((f) => s[f] !== null && s[f] !== undefined && s[f] !== '')
.map((f) => [STECK_NAME[f], STECK_DATUM.has(f) ? datum(s[f]) : String(s[f])]);
if (!paare.length) return null;
const block = el('section', 'ev-block ev-block--steck');
const kopf = el('div', 'ev-block__kopf');
kopf.append(el('h4', 'ev-block__schild', 'Steckbrief'));
block.append(kopf);
const liste = el('dl', 'ev-steck');
for (const [name, wert] of paare) {
liste.append(el('dt', 'ev-steck__name', name));
liste.append(el('dd', 'ev-steck__wert', wert));
} }
block.append(liste); block.append(liste);
return block; return block;
} }
/** Die Abschnitte des Regeltextes -- Hinweise und Geschenke heraus. */
function evInfos(e) {
const alle = Array.isArray(e.event_regelabschnitte) ? e.event_regelabschnitte : null;
if (!alle) return null;
const hinweise = [];
let gaben = null;
const rest = [];
for (const a of alle) {
/* Der Block GANZ OBEN ohne Ueberschrift sind die Hinweise des
Lesers. Ein Hinweis MITTEN in einem Abschnitt bleibt dort --
er bezieht sich dann auf diesen Abschnitt. */
if (a.art === 'hinweise' && !a.titel) { hinweise.push(...(a.zeilen || [])); continue; }
if (!gaben && a.art === 'geschenke') { gaben = a; continue; }
rest.push(a);
}
return { hinweise, gaben, rest };
}
/** Alles Uebrige in einem Block. */
function evRegelBlock(abschnitte) {
if (!abschnitte.length) return null;
const block = el('section', 'ev-block ev-block--regeln');
const kopf = el('div', 'ev-block__kopf');
kopf.append(el('h4', 'ev-block__schild', 'Regeln & Infos'));
kopf.append(el('span', 'ev-zaehler', String(abschnitte.length)));
block.append(kopf);
const kasten = el('div', 'ev-abschnitte');
for (const a of abschnitte) kasten.append(evAbschnitt(a));
block.append(kasten);
return block;
}
/** Der dritte Ausgang fuer die Event-Bloecke. /** Der dritte Ausgang fuer die Event-Bloecke.
* *
* Kommt vom Server keine Punkteliste (alte Fassung im Netz, Feld * Kommt vom Server keine Punkteliste (alte Fassung im Netz, Feld
@@ -1340,8 +1620,27 @@
schlichte Liste als eine Karte, auf der die Aufgaben fehlen. */ schlichte Liste als eine Karte, auf der die Aufgaben fehlen. */
const auf = eventAufgaben(e, stand) const auf = eventAufgaben(e, stand)
|| (e.event_aufgaben ? eventRueckfall('Aufgaben', e.event_aufgaben) : null); || (e.event_aufgaben ? eventRueckfall('Aufgaben', e.event_aufgaben) : null);
const reg = eventRegeln(e)
|| (e.event_regeln ? eventRueckfall('Regeln', e.event_regeln) : null); /* DER DRITTE AUSGANG (CLAUDE.md, 03.09.2026): in Ordnung /
nicht in Ordnung / konnte nicht nachsehen.
`evInfos` gibt `null`, wenn der Server keine Gliederung
geschickt hat -- alte Serverfassung, leeres Feld, Abfrage
gescheitert. Dann steht der ROHTEXT da (`event_regeln`
kommt weiterhin mit). Eine Kachel ohne Regeln waere der
schlechteste von drei Ausgaengen; eine schlichte Textwand
ist ein Schoenheitsfehler.
GLIEDERUNG DA, ABER LEER ist etwas anderes als „keine
Gliederung": Dann hat das Event wirklich keine Regeln, und
dann soll auch nichts dastehen. */
const infos = evInfos(e);
const gaben = infos?.gaben ? evGabenBlock(infos.gaben) : null;
const hinweise = infos ? evHinweisBlock(infos.hinweise) : null;
const reg = infos
? evRegelBlock(infos.rest)
: (e.event_regeln ? eventRueckfall('Regeln', e.event_regeln) : null);
const steck = evSteckbrief(e);
/* BEIDE IN EINE REIHE, nicht untereinander an die Karte. /* BEIDE IN EINE REIHE, nicht untereinander an die Karte.
Auf 1280 px ist eine Karte rund 1160 px breit. "30 Auf 1280 px ist eine Karte rund 1160 px breit. "30
@@ -1353,12 +1652,57 @@
zwar durch MESSEN (auto-fit mit Mindestbreite), nicht durch zwar durch MESSEN (auto-fit mit Mindestbreite), nicht durch
eine Fensterbreite in einer Zahl. Eine feste Schwelle waere eine Fensterbreite in einer Zahl. Eine feste Schwelle waere
beim naechsten zusaetzlichen Block wieder falsch. */ beim naechsten zusaetzlichen Block wieder falsch. */
if (auf || reg) { /* IMMER SICHTBAR: was man tun soll, und was dabei zaehlt. */
if (auf || gaben) {
const reihe = el('div', 'ev-bloecke'); const reihe = el('div', 'ev-bloecke');
if (auf) reihe.append(auf); if (auf) reihe.append(auf);
if (reg) reihe.append(reg); if (gaben) reihe.append(gaben);
k.append(reihe); k.append(reihe);
} }
/* HINTER EINEM DRUCK: alles Uebrige (07.10.2026).
Filipe: „die kacheln von den events soll auch nicht so
lang sein, die soll man aufklappen koennen damit die nicht
zu lange sind." Gemessen an „Gipfelstuermer" sind das rund
7000 Zeichen -- sechs Preisgruppen, sechs Ligen,
Teilnahmebedingungen, Themenideen.
DERSELBE KLAPPMECHANISMUS WIE IM GANZEN HAUS
(`abschnittKlappbar` aus kopf.js). Ein eigener waere ein
zweiter, der sich anders verhaelt -- und er merkt sich die
eigene Wahl, was hier wichtig ist: Das Brett baut sich bei
jeder Aktualisierung neu auf, und eine Kachel, die dabei
wieder zuklappt, waehrend man liest, ist aergerlicher als
eine lange.
ZU ALS VORGABE. Das ist der Zweck der Uebung. Die Zahl im
Knopf sagt, was dahinter steht -- ein zugeklappter Kasten
ohne Zahl sieht aus, als sei er leer. */
if (reg || steck) {
const mehr = el('div', 'ev-mehr');
const knopf = el('button', 'ev-mehr__knopf');
knopf.type = 'button';
knopf.append(el('span', 'ev-mehr__wort', 'Alle Infos'));
const n = (infos?.rest.length || 0) + (steck ? 1 : 0);
knopf.append(el('span', 'ev-mehr__zahl',
n === 1 ? '1 Abschnitt' : n + ' Abschnitte'));
if (window.klappPfeil) knopf.append(window.klappPfeil());
const inhalt = el('div', 'ev-mehr__inhalt');
if (reg) inhalt.append(reg);
if (steck) inhalt.append(steck);
mehr.append(knopf);
mehr.append(inhalt);
k.append(mehr);
window.abschnittKlappbar?.(knopf, inhalt, 'event:' + e.id + ':infos', true);
}
/* DIE HINWEISE ZULETZT UND SICHTBAR. „Die Preise konnten
nicht gelesen werden -- bitte nachtragen" ist eine
Aufgabe; hinter dem Aufklappen waere sie verborgen. Sie
stehen unter den Bloecken, nicht zwischen den Regeln --
genau das war der Fehler im Bildschirmfoto. */
if (hinweise) k.append(hinweise);
} }
/* ---- STEHT IM KALENDER (17.09.2026) ------------------------------ /* ---- STEHT IM KALENDER (17.09.2026) ------------------------------
@@ -3353,11 +3697,36 @@
? 'Noch keine Einladung eingelesen.' ? 'Noch keine Einladung eingelesen.'
: 'Noch kein eigenes Event angelegt.')); : 'Noch kein eigenes Event angelegt.'));
} }
for (const e of drin) { /* NACH ZUSTAND UND ALTER GEGLIEDERT (07.10.2026) --
Begruendung bei EV_GRUPPEN. „Laeuft gerade" steht
offen, Vergangenes zu: Es ist Nachschlagewerk, nicht
das, weswegen jemand die Seite oeffnet.
DER MERKER TRAEGT DIE SEITE MIT. Ohne ihn teilten sich
„Laeuft gerade" links und rechts denselben Zustand,
und wer die eine zuklappt, klappt die andere mit --
derselbe Fehler, der bei den Zeitstufen schon einmal
behoben wurde. */
for (const [schluessel, name, zuVorgabe] of EV_GRUPPEN) {
const fach = drin.filter((e) => evGruppeVon(e) === schluessel);
if (!fach.length) continue;
const gkopf = el('button', 'ev-gruppe__kopf');
gkopf.type = 'button';
gkopf.dataset.gruppe = schluessel;
gkopf.append(el('span', 'ev-gruppe__wort', name));
gkopf.append(el('span', 'ev-gruppe__zahl', String(fach.length)));
if (window.klappPfeil) gkopf.append(window.klappPfeil());
spalte.append(gkopf);
const kasten = el('div', 'ev-gruppe__inhalt');
for (const e of fach) {
const k = karte(e); const k = karte(e);
k.dataset.quelle = seite; k.dataset.quelle = seite;
karten.set(e.id, k); karten.set(e.id, k);
spalte.append(k); kasten.append(k);
}
spalte.append(kasten);
window.abschnittKlappbar?.(gkopf, kasten,
'agentur:' + seite + ':' + schluessel, zuVorgabe);
} }
trennung.append(spalte); trennung.append(spalte);
} }
+19 -19
View File
@@ -12,19 +12,19 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="garage"> <body class="start" data-buehne="garage">
@@ -430,22 +430,22 @@
<!-- TEXTFORM ZUERST (21.09.2026): Die Seite fragt beim Zeichnen nach <!-- TEXTFORM ZUERST (21.09.2026): Die Seite fragt beim Zeichnen nach
`window.Textform`. Kaeme sie spaeter, waere der erste Eintrag ohne `window.Textform`. Kaeme sie spaeter, waere der erste Eintrag ohne
Gestaltung. Kein `defer`, damit sie wirklich vorher dasteht. --> Gestaltung. Kein `defer`, damit sie wirklich vorher dasteht. -->
<script src="assets/js/textform.js?v=202610071845"></script> <script src="assets/js/textform.js?v=202610072238"></script>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/hochladen.js?v=202610071845" defer></script> <script src="assets/js/hochladen.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/zuteilung.js?v=202610071845" defer></script> <script src="assets/js/zuteilung.js?v=202610072238" defer></script>
<!-- Der Vorlagenkatalog stand bis zum 23.09.2026 mitten in <!-- Der Vorlagenkatalog stand bis zum 23.09.2026 mitten in
aufgaben.js. Er bedient jetzt zwei Seiten und hat deshalb eine aufgaben.js. Er bedient jetzt zwei Seiten und hat deshalb eine
eigene Datei; hier zeigt er den Creator-Katalog. eigene Datei; hier zeigt er den Creator-Katalog.
Vor aufgaben.js, weil die Seite ihn beim Start einrichtet. --> Vor aufgaben.js, weil die Seite ihn beim Start einrichtet. -->
<script src="assets/js/vorlagenbrett.js?v=202610071845" defer></script> <script src="assets/js/vorlagenbrett.js?v=202610072238" defer></script>
<script src="assets/js/aufgaben.js?v=202610071845" defer></script> <script src="assets/js/aufgaben.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+16 -16
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/automation.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/automation.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="garage"> <body class="start" data-buehne="garage">
@@ -168,15 +168,15 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/automation.js?v=202610071845" defer></script> <script src="assets/js/automation.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+17 -17
View File
@@ -7,14 +7,14 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/entwicklung.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -197,16 +197,16 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/holen.js?v=202610071845" defer></script> <script src="assets/js/holen.js?v=202610072238" defer></script>
<script src="assets/js/befinden.js?v=202610071845" defer></script> <script src="assets/js/befinden.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+24 -24
View File
@@ -12,29 +12,29 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/bereich.css?v=202610072238" />
<!-- Wegen des Blocks „Willst du dazugehoeren?" auf dem Brett <!-- Wegen des Blocks „Willst du dazugehoeren?" auf dem Brett
„Mitmachen". Eine eigene Kopie der Regel hier waere die zweite „Mitmachen". Eine eigene Kopie der Regel hier waere die zweite
Stelle, an der dieselbe Sache anders aussieht. --> Stelle, an der dieselbe Sache anders aussieht. -->
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/bewerbung.css?v=202610072238" />
<!-- Die Bildschau: ein Banner wird darin mittig gezeigt statt als nackte <!-- Die Bildschau: ein Banner wird darin mittig gezeigt statt als nackte
Datei in einem neuen Tab. Sie ist seit dem 07.10.2026 eine eigene Datei in einem neuen Tab. Sie ist seit dem 07.10.2026 eine eigene
Datei und keine Kopie aus chat.css -- dieselbe Schau im Chat und Datei und keine Kopie aus chat.css -- dieselbe Schau im Chat und
hier, ein Stil. --> hier, ein Stil. -->
<link rel="stylesheet" href="assets/css/bildschau.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/bildschau.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -381,20 +381,20 @@
<!-- TEXTFORM ZUERST (21.09.2026): Die Seite fragt beim Zeichnen nach <!-- TEXTFORM ZUERST (21.09.2026): Die Seite fragt beim Zeichnen nach
`window.Textform`. Kaeme sie spaeter, waere der erste Eintrag ohne `window.Textform`. Kaeme sie spaeter, waere der erste Eintrag ohne
Gestaltung. Kein `defer`, damit sie wirklich vorher dasteht. --> Gestaltung. Kein `defer`, damit sie wirklich vorher dasteht. -->
<script src="assets/js/textform.js?v=202610071845"></script> <script src="assets/js/textform.js?v=202610072238"></script>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/checkliste.js?v=202610071845" defer></script> <script src="assets/js/checkliste.js?v=202610072238" defer></script>
<script src="assets/js/ampel.js?v=202610071845" defer></script> <script src="assets/js/ampel.js?v=202610072238" defer></script>
<script src="assets/js/schulung.js?v=202610071845" defer></script> <script src="assets/js/schulung.js?v=202610072238" defer></script>
<script src="assets/js/holen.js?v=202610071845" defer></script> <script src="assets/js/holen.js?v=202610072238" defer></script>
<script src="assets/js/bildschau.js?v=202610071845"></script> <script src="assets/js/bildschau.js?v=202610072238"></script>
<script src="assets/js/bereich.js?v=202610071845" defer></script> <script src="assets/js/bereich.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+19 -19
View File
@@ -7,17 +7,17 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/bereich.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/treff.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/treff.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/bewerbung.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein - siehe treff-regeln.html. --> <!-- haus.css MUSS die letzte Stilvorlage sein - siehe treff-regeln.html. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -108,15 +108,15 @@
</main> </main>
<!-- Der Fuß gehört zu jeder Seite - siehe pruef-css-klassen. --> <!-- Der Fuß gehört zu jeder Seite - siehe pruef-css-klassen. -->
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/auskunft.js?v=202610071845" defer></script> <script src="assets/js/auskunft.js?v=202610072238" defer></script>
<script src="assets/js/bewerben.js?v=202610071845" defer></script> <script src="assets/js/bewerben.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+18 -18
View File
@@ -7,16 +7,16 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/entwicklung.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/bewerbung.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein - siehe treff-regeln.html. --> <!-- haus.css MUSS die letzte Stilvorlage sein - siehe treff-regeln.html. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -80,16 +80,16 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/auskunft.js?v=202610071845" defer></script> <script src="assets/js/auskunft.js?v=202610072238" defer></script>
<script src="assets/js/bewerbungen.js?v=202610071845" defer></script> <script src="assets/js/bewerbungen.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+3 -3
View File
@@ -59,11 +59,11 @@
<!-- DIE WERBEEINBLENDUNG. Dieselbe Datei wie in der Reaction-Seite: <!-- DIE WERBEEINBLENDUNG. Dieselbe Datei wie in der Reaction-Seite:
Zwei Fassungen altern unterschiedlich, und dann sieht der Stream Zwei Fassungen altern unterschiedlich, und dann sieht der Stream
anders aus als das, worueber geredet wird. --> anders aus als das, worueber geredet wird. -->
<link rel="stylesheet" href="assets/css/werbung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/werbung.css?v=202610072238" />
</head> </head>
<body class="obs-buehne"> <body class="obs-buehne">
<div id="platz"></div> <div id="platz"></div>
<script src="assets/js/werbung.js?v=202610071845" defer></script> <script src="assets/js/werbung.js?v=202610072238" defer></script>
<script src="assets/js/buehne.js?v=202610071845" defer></script> <script src="assets/js/buehne.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+17 -17
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/calls.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/calls.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="lounge"> <body class="start" data-buehne="lounge">
@@ -89,15 +89,15 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/ki.js?v=202610071845" defer></script> <script src="assets/js/ki.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/calls.js?v=202610071845" defer></script> <script src="assets/js/calls.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+21 -21
View File
@@ -12,31 +12,31 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog, <!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem .neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html. zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
Dialog zu sehen bekam. --> Dialog zu sehen bekam. -->
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/chat.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/chat.css?v=202610072238" />
<!-- Die Bildschau stand bis zum 07.10.2026 in chat.css. Sie wird jetzt <!-- Die Bildschau stand bis zum 07.10.2026 in chat.css. Sie wird jetzt
auch vom Event-Banner benutzt (bereich.html) und hat deshalb eine auch vom Event-Banner benutzt (bereich.html) und hat deshalb eine
eigene Datei. Sie steht GENAU HIER -- direkt hinter chat.css, also eigene Datei. Sie steht GENAU HIER -- direkt hinter chat.css, also
an derselben Stelle im Ladeweg wie der Block vorher. --> an derselben Stelle im Ladeweg wie der Block vorher. -->
<link rel="stylesheet" href="assets/css/bildschau.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/bildschau.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="lounge"> <body class="start" data-buehne="lounge">
@@ -744,24 +744,24 @@
</form> </form>
</dialog> </dialog>
<script src="assets/js/bereiche.js?v=202610071845"></script> <script src="assets/js/bereiche.js?v=202610072238"></script>
<!-- TEXTFORM VOR ALLEM ANDEREN (21.09.2026): chat.js und bereich.js <!-- TEXTFORM VOR ALLEM ANDEREN (21.09.2026): chat.js und bereich.js
fragen beim Zeichnen nach `window.Textform`. Kaeme sie spaeter, fragen beim Zeichnen nach `window.Textform`. Kaeme sie spaeter,
waere die erste gezeichnete Nachricht ohne Gestaltung -- und das waere die erste gezeichnete Nachricht ohne Gestaltung -- und das
faellt nur dem auf, der genau hinsieht. KEIN `defer`, damit sie faellt nur dem auf, der genau hinsieht. KEIN `defer`, damit sie
wirklich vor den anderen dasteht. --> wirklich vor den anderen dasteht. -->
<script src="assets/js/textform.js?v=202610071845"></script> <script src="assets/js/textform.js?v=202610072238"></script>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/hochladen.js?v=202610071845" defer></script> <script src="assets/js/hochladen.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845"></script> <script src="assets/js/kopf.js?v=202610072238"></script>
<script src="assets/js/glocke.js?v=202610071845"></script> <script src="assets/js/glocke.js?v=202610072238"></script>
<!-- ANRUF VOR CHAT (18.09.2026): anruf.js stellt `window.anrufStarten` <!-- ANRUF VOR CHAT (18.09.2026): anruf.js stellt `window.anrufStarten`
bereit, und chat.js baut die Knoepfe nur, wenn es das gibt. bereit, und chat.js baut die Knoepfe nur, wenn es das gibt.
Andersherum waere die Reihenfolge eine stille Abhaengigkeit -- Andersherum waere die Reihenfolge eine stille Abhaengigkeit --
die Knoepfe fehlten, und niemand wuesste warum. --> die Knoepfe fehlten, und niemand wuesste warum. -->
<script src="assets/js/anruf.js?v=202610071845"></script> <script src="assets/js/anruf.js?v=202610072238"></script>
<!-- DIE BILDSCHAU (25.09.2026). Eigene Datei, weil sie nichts vom <!-- DIE BILDSCHAU (25.09.2026). Eigene Datei, weil sie nichts vom
Chat weiss: Sie bekommt einen Weg und einen Namen und zeigt ein Chat weiss: Sie bekommt einen Weg und einen Namen und zeigt ein
Bild. Jede andere Seite mit Fotos kann sie ebenso benutzen, ohne Bild. Jede andere Seite mit Fotos kann sie ebenso benutzen, ohne
@@ -772,8 +772,8 @@
sie beim ersten Foto noch nicht da -- und der erste Klick ginge sie beim ersten Foto noch nicht da -- und der erste Klick ginge
wie frueher in den Tab. Ein Fehler, der nur beim ERSTEN Mal wie frueher in den Tab. Ein Fehler, der nur beim ERSTEN Mal
auftritt, ist der, den man nie nachstellen kann. --> auftritt, ist der, den man nie nachstellen kann. -->
<script src="assets/js/bildschau.js?v=202610071845"></script> <script src="assets/js/bildschau.js?v=202610072238"></script>
<script src="assets/js/chat.js?v=202610071845"></script> <script src="assets/js/chat.js?v=202610072238"></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+17 -17
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/content.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/content.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="portal"> <body class="start" data-buehne="portal">
@@ -179,15 +179,15 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/checkliste.js?v=202610071845" defer></script> <script src="assets/js/checkliste.js?v=202610072238" defer></script>
<script src="assets/js/content.js?v=202610071845" defer></script> <script src="assets/js/content.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+6 -6
View File
@@ -36,15 +36,15 @@
<link rel="preload" as="image" href="assets/img/buehne/crew-gate-hoch-580.webp" <link rel="preload" as="image" href="assets/img/buehne/crew-gate-hoch-580.webp"
type="image/webp" media="(max-width: 899px)" /> type="image/webp" media="(max-width: 899px)" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<!-- Diese Wand steht NUR auf der Adresse von Team Dogi (ueberall sonst <!-- Diese Wand steht NUR auf der Adresse von Team Dogi (ueberall sonst
404, siehe crewWeiche). Sie darf das Haus deshalb direkt nennen 404, siehe crewWeiche). Sie darf das Haus deshalb direkt nennen
statt ueber den Platzhalter haus.css zu gehen -- und sie MUSS es: statt ueber den Platzhalter haus.css zu gehen -- und sie MUSS es:
Eine Zugangswand, die sich erst nach dem Umbiegen faerbt, waere Eine Zugangswand, die sich erst nach dem Umbiegen faerbt, waere
die eine Seite, auf der man das Umfaerben sieht. --> die eine Seite, auf der man das Umfaerben sieht. -->
<link rel="stylesheet" href="assets/css/crew-haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/crew-haus.css?v=202610072238" />
</head> </head>
<body class="gate gate--crew"> <body class="gate gate--crew">
@@ -459,7 +459,7 @@
<script src="assets/js/gate.js?v=202610071845" defer></script> <script src="assets/js/gate.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+4 -4
View File
@@ -23,25 +23,25 @@
}, },
"icons": [ "icons": [
{ {
"src": "/assets/img/app-symbole/crew-192.png?v=202610071845", "src": "/assets/img/app-symbole/crew-192.png?v=202610072238",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png", "type": "image/png",
"purpose": "any" "purpose": "any"
}, },
{ {
"src": "/assets/img/app-symbole/crew-512.png?v=202610071845", "src": "/assets/img/app-symbole/crew-512.png?v=202610072238",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png", "type": "image/png",
"purpose": "any" "purpose": "any"
}, },
{ {
"src": "/assets/img/app-symbole/crew-192-maskable.png?v=202610071845", "src": "/assets/img/app-symbole/crew-192-maskable.png?v=202610072238",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png", "type": "image/png",
"purpose": "maskable" "purpose": "maskable"
}, },
{ {
"src": "/assets/img/app-symbole/crew-512-maskable.png?v=202610071845", "src": "/assets/img/app-symbole/crew-512-maskable.png?v=202610072238",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png", "type": "image/png",
"purpose": "maskable" "purpose": "maskable"
+17 -17
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/dateien.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/dateien.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="arena"> <body class="start" data-buehne="arena">
@@ -108,15 +108,15 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/hochladen.js?v=202610071845" defer></script> <script src="assets/js/hochladen.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/dateien.js?v=202610071845" defer></script> <script src="assets/js/dateien.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+18 -18
View File
@@ -7,14 +7,14 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/entwicklung.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -306,20 +306,20 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/holen.js?v=202610071845" defer></script> <script src="assets/js/holen.js?v=202610072238" defer></script>
<!-- VOR entwicklung.js: Die Seite richtet den Katalog beim Start ein, <!-- VOR entwicklung.js: Die Seite richtet den Katalog beim Start ein,
also muss es ihn zu dem Zeitpunkt geben. Mit "defer" laufen beide also muss es ihn zu dem Zeitpunkt geben. Mit "defer" laufen beide
in Dokumentreihenfolge. --> in Dokumentreihenfolge. -->
<script src="assets/js/vorlagenbrett.js?v=202610071845" defer></script> <script src="assets/js/vorlagenbrett.js?v=202610072238" defer></script>
<script src="assets/js/entwicklung.js?v=202610071845" defer></script> <script src="assets/js/entwicklung.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+15 -15
View File
@@ -7,13 +7,13 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/hilfe.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/hilfe.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -146,14 +146,14 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/hilfe.js?v=202610071845" defer></script> <script src="assets/js/hilfe.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+5 -5
View File
@@ -30,9 +30,9 @@
<link rel="preload" as="image" href="assets/img/buehne/gate-hoch-580.webp" <link rel="preload" as="image" href="assets/img/buehne/gate-hoch-580.webp"
type="image/webp" media="(max-width: 899px)" /> type="image/webp" media="(max-width: 899px)" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
</head> </head>
<body class="gate"> <body class="gate">
@@ -427,7 +427,7 @@
<script src="assets/js/gate.js?v=202610071845" defer></script> <script src="assets/js/gate.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+17 -17
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/kalender.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/kalender.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="skyline"> <body class="start" data-buehne="skyline">
@@ -383,15 +383,15 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/kalender.js?v=202610071845" defer></script> <script src="assets/js/kalender.js?v=202610072238" defer></script>
<script src="assets/js/kalender-abo.js?v=202610071845" defer></script> <script src="assets/js/kalender-abo.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+16 -16
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/leistung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/leistung.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="arena"> <body class="start" data-buehne="arena">
@@ -444,13 +444,13 @@
</form> </form>
</dialog> </dialog>
<script src="assets/js/meldung.js?v=202610071845"></script> <script src="assets/js/meldung.js?v=202610072238"></script>
<script src="assets/js/nachfrage.js?v=202610071845"></script> <script src="assets/js/nachfrage.js?v=202610072238"></script>
<script src="assets/js/wahl.js?v=202610071845"></script> <script src="assets/js/wahl.js?v=202610072238"></script>
<script src="assets/js/bereiche.js?v=202610071845"></script> <script src="assets/js/bereiche.js?v=202610072238"></script>
<script src="assets/js/kopf.js?v=202610071845"></script> <script src="assets/js/kopf.js?v=202610072238"></script>
<script src="assets/js/glocke.js?v=202610071845"></script> <script src="assets/js/glocke.js?v=202610072238"></script>
<script src="assets/js/leistung.js?v=202610071845"></script> <script src="assets/js/leistung.js?v=202610072238"></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+14 -14
View File
@@ -7,17 +7,17 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/manager-ziele.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/manager-ziele.css?v=202610072238" />
<!-- module.css an VORLETZTER, haus.css an LETZTER Stelle. Die <!-- module.css an VORLETZTER, haus.css an LETZTER Stelle. Die
Reihenfolge ist keine Geschmacksfrage: haus.css setzt nur Reihenfolge ist keine Geschmacksfrage: haus.css setzt nur
Variablen, und bei Variablen entscheidet allein, wer zuletzt Variablen, und bei Variablen entscheidet allein, wer zuletzt
kommt. pruef-css-klassen.mjs misst genau dieses Paar nach. --> kommt. pruef-css-klassen.mjs misst genau dieses Paar nach. -->
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="arena"> <body class="start" data-buehne="arena">
@@ -230,16 +230,16 @@
kopf.js liest daraus die Leitungsrollen. Ohne sie stürzt jede kopf.js liest daraus die Leitungsrollen. Ohne sie stürzt jede
Seite ab, und zwar nur in der Browserkonsole -- die Kopfleiste Seite ab, und zwar nur in der Browserkonsole -- die Kopfleiste
kommt trotzdem. --> kommt trotzdem. -->
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<!-- wahl.js gehört zu kopf.js: Der Sicht-Umschalter baut daraus seine <!-- wahl.js gehört zu kopf.js: Der Sicht-Umschalter baut daraus seine
Liste. pruef-css-klassen setzt das durch, und zwar auch die Liste. pruef-css-klassen setzt das durch, und zwar auch die
Reihenfolge -- kopf.js fragt beim Aufbau nach dem fertigen Reihenfolge -- kopf.js fragt beim Aufbau nach dem fertigen
Bedienelement. --> Bedienelement. -->
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/manager-ziele.js?v=202610071845" defer></script> <script src="assets/js/manager-ziele.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+16 -16
View File
@@ -12,13 +12,13 @@
vergessen wird. --> vergessen wird. -->
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/material.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/material.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-ton="42" data-buehne="studio"> <body class="start" data-ton="42" data-buehne="studio">
@@ -138,14 +138,14 @@
<p class="m-laedt" id="laedt">Einen Moment …</p> <p class="m-laedt" id="laedt">Einen Moment …</p>
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/hochladen.js?v=202610071845" defer></script> <script src="assets/js/hochladen.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/material.js?v=202610071845" defer></script> <script src="assets/js/material.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+14 -14
View File
@@ -7,13 +7,13 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/notizen.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/notizen.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-ton="46"> <body class="start" data-ton="46">
@@ -181,12 +181,12 @@
<!-- bereiche.js VOR kopf.js: Dort steht `window.Bereiche`, und kopf.js <!-- bereiche.js VOR kopf.js: Dort steht `window.Bereiche`, und kopf.js
liest daraus die Leitungsrollen. --> liest daraus die Leitungsrollen. -->
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/notizen.js?v=202610071845" defer></script> <script src="assets/js/notizen.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+16 -16
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/personen.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/personen.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="halle"> <body class="start" data-buehne="halle">
@@ -453,14 +453,14 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/personen.js?v=202610071845" defer></script> <script src="assets/js/personen.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+18 -18
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/profil.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/profil.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="halle"> <body class="start" data-buehne="halle">
@@ -214,16 +214,16 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/hochladen.js?v=202610071845" defer></script> <script src="assets/js/hochladen.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/steckbrief.js?v=202610071845" defer></script> <script src="assets/js/steckbrief.js?v=202610072238" defer></script>
<script src="assets/js/profil.js?v=202610071845" defer></script> <script src="assets/js/profil.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+19 -19
View File
@@ -7,15 +7,15 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/reaktion.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/reaktion.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/spendenkarte.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/spendenkarte.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/werbung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/werbung.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start reaktion-seite" data-stand="laedt"> <body class="start reaktion-seite" data-stand="laedt">
@@ -905,15 +905,15 @@
<!-- bereiche.js VOR kopf.js: Dort steht `window.Bereiche`, und kopf.js <!-- bereiche.js VOR kopf.js: Dort steht `window.Bereiche`, und kopf.js
liest daraus die Leitungsrollen. --> liest daraus die Leitungsrollen. -->
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/spendenkarte.js?v=202610071845" defer></script> <script src="assets/js/spendenkarte.js?v=202610072238" defer></script>
<script src="assets/js/werbung.js?v=202610071845" defer></script> <script src="assets/js/werbung.js?v=202610072238" defer></script>
<script src="assets/js/reaktion.js?v=202610071845" defer></script> <script src="assets/js/reaktion.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+17 -17
View File
@@ -7,14 +7,14 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/treff.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/treff.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -134,14 +134,14 @@
Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen
Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen, Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen,
die seit dem 07.09. jede Seite danach absucht. --> die seit dem 07.09. jede Seite danach absucht. -->
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/holen.js?v=202610071845" defer></script> <script src="assets/js/holen.js?v=202610072238" defer></script>
<script src="assets/js/rechte.js?v=202610071845" defer></script> <script src="assets/js/rechte.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+18 -18
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/report.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/report.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="showbuehne"> <body class="start" data-buehne="showbuehne">
@@ -106,17 +106,17 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/ki.js?v=202610071845" defer></script> <script src="assets/js/ki.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/holen.js?v=202610071845" defer></script> <script src="assets/js/holen.js?v=202610072238" defer></script>
<script src="assets/js/report.js?v=202610071845" defer></script> <script src="assets/js/report.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+16 -16
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/scouting.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/scouting.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="wald"> <body class="start" data-buehne="wald">
@@ -194,14 +194,14 @@
</div> </div>
</div> </div>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/scouting.js?v=202610071845" defer></script> <script src="assets/js/scouting.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+19 -19
View File
@@ -12,26 +12,26 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe <!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe
den Hinweis in start.css -- dort stand es bis zum 07.09.2026. --> den Hinweis in start.css -- dort stand es bis zum 07.09.2026. -->
<link rel="stylesheet" href="assets/css/heim.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/heim.css?v=202610072238" />
<!-- Die Einweisung bringt ihr Aussehen mit. Sie steht in <!-- Die Einweisung bringt ihr Aussehen mit. Sie steht in
anleitung.css, weil sie dorthin gehoert -- und nicht in anleitung.css, weil sie dorthin gehoert -- und nicht in
start.css, wo sie zwischen 48 Kachelfarben verschwinden start.css, wo sie zwischen 48 Kachelfarben verschwinden
wuerde. --> wuerde. -->
<link rel="stylesheet" href="assets/css/anleitung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/anleitung.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -768,22 +768,22 @@
</p> </p>
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/live.js?v=202610071845" defer></script> <script src="assets/js/live.js?v=202610072238" defer></script>
<script src="assets/js/start.js?v=202610071845" defer></script> <script src="assets/js/start.js?v=202610072238" defer></script>
<!-- Die Begruessung und der Rundgang beim ersten Anmelden. <!-- Die Begruessung und der Rundgang beim ersten Anmelden.
EIGENE DATEI und nicht ein Stueck start.js: Sie laeuft auf der EIGENE DATEI und nicht ein Stueck start.js: Sie laeuft auf der
meistbenutzten Seite des Hauses, und wenn hier etwas schiefgeht, meistbenutzten Seite des Hauses, und wenn hier etwas schiefgeht,
darf davon nichts anderes betroffen sein. --> darf davon nichts anderes betroffen sein. -->
<script src="assets/js/rundgang.js?v=202610071845" defer></script> <script src="assets/js/rundgang.js?v=202610072238" defer></script>
<script src="assets/js/aufstieg.js?v=202610071845" defer></script> <script src="assets/js/aufstieg.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+17 -17
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/startcheck.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/startcheck.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="wald"> <body class="start" data-buehne="wald">
@@ -85,16 +85,16 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/ki.js?v=202610071845" defer></script> <script src="assets/js/ki.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/startcheck.js?v=202610071845" defer></script> <script src="assets/js/startcheck.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+18 -18
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/profil.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/profil.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="halle"> <body class="start" data-buehne="halle">
@@ -150,16 +150,16 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/hochladen.js?v=202610071845" defer></script> <script src="assets/js/hochladen.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/auskunft.js?v=202610071845" defer></script> <script src="assets/js/auskunft.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/steckbrief.js?v=202610071845" defer></script> <script src="assets/js/steckbrief.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+14 -14
View File
@@ -7,13 +7,13 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/support.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/support.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="studio"> <body class="start" data-buehne="studio">
@@ -154,16 +154,16 @@
(reading 'LEITUNG')" ab -- gemessen am 24.09.2026, als diese (reading 'LEITUNG')" ab -- gemessen am 24.09.2026, als diese
Zeile beim Anlegen der Seite fehlte. Die Kopfleiste kam Zeile beim Anlegen der Seite fehlte. Die Kopfleiste kam
trotzdem, deshalb sah man es nur in der Browserkonsole. --> trotzdem, deshalb sah man es nur in der Browserkonsole. -->
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<!-- wahl.js gehoert zu kopf.js: Der Sicht-Umschalter in der <!-- wahl.js gehoert zu kopf.js: Der Sicht-Umschalter in der
Kopfleiste baut daraus seine Liste. pruef-css-klassen setzt das Kopfleiste baut daraus seine Liste. pruef-css-klassen setzt das
seit dem 07.09.2026 durch -- damals fehlte es auf chat.html und seit dem 07.09.2026 durch -- damals fehlte es auf chat.html und
uebersicht.html, und heute hier. --> uebersicht.html, und heute hier. -->
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/support.js?v=202610071845" defer></script> <script src="assets/js/support.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+3 -3
View File
@@ -23,7 +23,7 @@
„Eigenes CSS" leer lassen (der Grund ist schon „Eigenes CSS" leer lassen (der Grund ist schon
durchsichtig). durchsichtig).
====================================================================== --> ====================================================================== -->
<link rel="stylesheet" href="assets/css/spendenkarte.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/spendenkarte.css?v=202610072238" />
<style> <style>
/* DURCHSICHTIG BIS AUF DIE KARTEN. OBS legt die Quelle über das /* DURCHSICHTIG BIS AUF DIE KARTEN. OBS legt die Quelle über das
Bild; jede Fläche, die hier steht, deckt dort etwas zu. */ Bild; jede Fläche, die hier steht, deckt dort etwas zu. */
@@ -80,7 +80,7 @@
<body> <body>
<div class="obs-tafel spendentafel" id="tafel" aria-live="polite"></div> <div class="obs-tafel spendentafel" id="tafel" aria-live="polite"></div>
<script src="assets/js/spendenkarte.js?v=202610071845" defer></script> <script src="assets/js/spendenkarte.js?v=202610072238" defer></script>
<script src="assets/js/tafel.js?v=202610071845" defer></script> <script src="assets/js/tafel.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+18 -18
View File
@@ -7,18 +7,18 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/entwicklung.css?v=202610072238" />
<!-- Wegen des Kastens „Anfragen“ ueber der Liste. Eine eigene Kopie <!-- Wegen des Kastens „Anfragen“ ueber der Liste. Eine eigene Kopie
der Regel hier waere die zweite Stelle, an der dieselbe Sache der Regel hier waere die zweite Stelle, an der dieselbe Sache
anders aussieht. --> anders aussieht. -->
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/bewerbung.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -111,15 +111,15 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/holen.js?v=202610071845" defer></script> <script src="assets/js/holen.js?v=202610072238" defer></script>
<script src="assets/js/talente.js?v=202610071845" defer></script> <script src="assets/js/talente.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+16 -16
View File
@@ -7,20 +7,20 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/team.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/team.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="halle"> <body class="start" data-buehne="halle">
@@ -110,15 +110,15 @@
</main> </main>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/holen.js?v=202610071845" defer></script> <script src="assets/js/holen.js?v=202610072238" defer></script>
<script src="assets/js/team.js?v=202610071845" defer></script> <script src="assets/js/team.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+17 -17
View File
@@ -7,20 +7,20 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/team.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/team.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<!-- `data-buehne` steht hier fest und nicht im Skript: Die Kachel zu <!-- `data-buehne` steht hier fest und nicht im Skript: Die Kachel zu
@@ -84,15 +84,15 @@
Kaputt ist dabei nichts -- er funktioniert. Genau deshalb faellt Kaputt ist dabei nichts -- er funktioniert. Genau deshalb faellt
es niemandem auf, und deshalb prueft pruef-css-klassen die es niemandem auf, und deshalb prueft pruef-css-klassen die
ABHAENGIGKEIT statt der Optik. Hier hat sie angeschlagen. --> ABHAENGIGKEIT statt der Optik. Hier hat sie angeschlagen. -->
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/holen.js?v=202610071845" defer></script> <script src="assets/js/holen.js?v=202610072238" defer></script>
<script src="assets/js/teamlage.js?v=202610071845" defer></script> <script src="assets/js/teamlage.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+15 -15
View File
@@ -7,13 +7,13 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/bereich.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -112,15 +112,15 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/teilen.js?v=202610071845" defer></script> <script src="assets/js/teilen.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+16 -16
View File
@@ -7,14 +7,14 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/treff.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/treff.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -126,14 +126,14 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/treff-moderation.js?v=202610071845" defer></script> <script src="assets/js/treff-moderation.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+16 -16
View File
@@ -7,18 +7,18 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/treff.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/treff.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein. Auf der Adresse des <!-- haus.css MUSS die letzte Stilvorlage sein. Auf der Adresse des
Rudels biegt der Server genau diesen Dateinamen auf crew-haus.css Rudels biegt der Server genau diesen Dateinamen auf crew-haus.css
um, und die Seite kommt schon beim ERSTEN Abruf in Lila und um, und die Seite kommt schon beim ERSTEN Abruf in Lila und
Babyblau an -- ohne Umfaerben im Browser, ohne Flackern. --> Babyblau an -- ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -248,14 +248,14 @@
Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen
Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen, Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen,
die seit dem 07.09. jede Seite danach absucht. --> die seit dem 07.09. jede Seite danach absucht. -->
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/auskunft.js?v=202610071845" defer></script> <script src="assets/js/auskunft.js?v=202610072238" defer></script>
<script src="assets/js/treff-regeln.js?v=202610071845" defer></script> <script src="assets/js/treff-regeln.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+16 -16
View File
@@ -12,22 +12,22 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der <!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
Dialog "Creator anlegen". --> Dialog "Creator anlegen". -->
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/uebersicht.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="showbuehne"> <body class="start" data-buehne="showbuehne">
@@ -149,16 +149,16 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/uebersicht.js?v=202610071845" defer></script> <script src="assets/js/uebersicht.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+15 -15
View File
@@ -7,13 +7,13 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/unsere-seiten.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/unsere-seiten.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -335,14 +335,14 @@
</main> </main>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/live.js?v=202610071845" defer></script> <script src="assets/js/live.js?v=202610072238" defer></script>
<script src="assets/js/unsere-seiten.js?v=202610071845" defer></script> <script src="assets/js/unsere-seiten.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+16 -16
View File
@@ -7,13 +7,13 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/unterstuetzen.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/unterstuetzen.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -106,17 +106,17 @@
<!-- bereiche.js VOR kopf.js: Dort steht `window.Bereiche`, und kopf.js <!-- bereiche.js VOR kopf.js: Dort steht `window.Bereiche`, und kopf.js
liest daraus die Leitungsrollen. Ohne sie stürzt jede Seite mit liest daraus die Leitungsrollen. Ohne sie stürzt jede Seite mit
„Cannot read properties of undefined (reading 'LEITUNG')" ab. --> „Cannot read properties of undefined (reading 'LEITUNG')" ab. -->
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/holen.js?v=202610071845" defer></script> <script src="assets/js/holen.js?v=202610072238" defer></script>
<!-- Die Satz-Tafel. Ohne sie stand auf dieser Seite das Maschinenwort <!-- Die Satz-Tafel. Ohne sie stand auf dieser Seite das Maschinenwort
des Servers (z.B. "nicht_gefunden") statt eines Satzes -- gemeldet des Servers (z.B. "nicht_gefunden") statt eines Satzes -- gemeldet
von pruef-meldungen, nicht beim Ansehen. --> von pruef-meldungen, nicht beim Ansehen. -->
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/unterstuetzen.js?v=202610071845" defer></script> <script src="assets/js/unterstuetzen.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+16 -16
View File
@@ -7,15 +7,15 @@
<meta name="robots" content="noindex, nofollow" /> <meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/entwicklung.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/werdegang.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/werdegang.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start"> <body class="start">
@@ -144,14 +144,14 @@
</main> </main>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/werdegang.js?v=202610071845" defer></script> <script src="assets/js/werdegang.js?v=202610072238" defer></script>
</body> </body>
</html> </html>
+1 -1
View File
@@ -56,7 +56,7 @@
veraltet beim naechsten Umbau. --> veraltet beim naechsten Umbau. -->
<link rel="manifest" href="/workspace/app.webmanifest" /> <link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<style> <style>
/* Bewusst ohne eigenes Stilblatt: Die Seite ist in aller Regel nur /* Bewusst ohne eigenes Stilblatt: Die Seite ist in aller Regel nur
+17 -17
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. --> eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" /> <meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610071845" /> <link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202610072238" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610071845" /> <link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202610072238" />
<link rel="stylesheet" href="assets/css/gate.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/gate.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/start.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/start.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/aufgaben.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/wissen.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/wissen.css?v=202610072238" />
<link rel="stylesheet" href="assets/css/module.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/module.css?v=202610072238" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026). <!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an -- kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. --> ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202610071845" /> <link rel="stylesheet" href="assets/css/haus.css?v=202610072238" />
</head> </head>
<body class="start" data-buehne="arena"> <body class="start" data-buehne="arena">
@@ -190,15 +190,15 @@
</main> </main>
<script src="assets/js/meldung.js?v=202610071845" defer></script> <script src="assets/js/meldung.js?v=202610072238" defer></script>
<script src="assets/js/hochladen.js?v=202610071845" defer></script> <script src="assets/js/hochladen.js?v=202610072238" defer></script>
<script src="assets/js/nachfrage.js?v=202610071845" defer></script> <script src="assets/js/nachfrage.js?v=202610072238" defer></script>
<script src="assets/js/wahl.js?v=202610071845" defer></script> <script src="assets/js/wahl.js?v=202610072238" defer></script>
<script src="assets/js/bereiche.js?v=202610071845" defer></script> <script src="assets/js/bereiche.js?v=202610072238" defer></script>
<script src="assets/js/installieren.js?v=202610071845" defer></script> <script src="assets/js/installieren.js?v=202610072238" defer></script>
<script src="assets/js/kopf.js?v=202610071845" defer></script> <script src="assets/js/kopf.js?v=202610072238" defer></script>
<script src="assets/js/glocke.js?v=202610071845" defer></script> <script src="assets/js/glocke.js?v=202610072238" defer></script>
<script src="assets/js/wissen.js?v=202610071845" defer></script> <script src="assets/js/wissen.js?v=202610072238" defer></script>
</body> </body>
</html> </html>