"Wer sieht was" auf beiden Adressen — und die Tafel lässt sich umstellen

Filipe: "ich will die kategorie auch auf der team dogi seite für die
dogfather und rechte hand rollen ... und wenn man drauf drückt,
perfektionieren so dass ich die da auch manuell wechseln und speichern
kann."

DIE KACHEL STAND NUR AUF DER AGENTURSEITE. Auf der Team-Dogi-Seite
arbeitet ihr aber täglich; dort nicht nachsehen zu können, wer was darf,
hieße, die Frage an dem Ort zu stellen, an dem man gerade nicht ist. Sie
steht jetzt auf beiden — einmal beschrieben, zweimal benutzt.

ANSEHEN BEIDE, UMSTELLEN NUR DOGFATHER. Die rechte Hand bekommt
dieselbe Tafel und keine Knöpfe; nicht weil das Skript sie versteckt,
sondern weil der Server darf_aendern: false schickt. Wer Rechte vergeben
kann, kann sich Rechte vergeben — diese eine Tür bleibt bei ihm.

WAS GESPEICHERT WIRD, IST NUR DER UNTERSCHIED. In der Datenbank steht
eine Zeile nur, wenn sie vom Grundstand in rechte.js abweicht. Damit
bedeutet der Code weiterhin etwas: Man sieht, was gedacht war, und
daneben, was jemand daraus gemacht hat. "Zurück auf Grundstand" ist ein
DELETE, und eine neue Seite erbt automatisch den Grundstand — eine
vollständige Kopie in der Datenbank hätte sie nicht gekannt und sie wäre
für alle zu gewesen, ohne dass es jemand entschieden hätte.

DREI FELDER SIND FEST: DogFather kann sich die Rechte-, die Personen-
und die Startseite nicht selbst wegnehmen. Eine Einstellung, aus der man
sich aussperren kann, ist keine Einstellung, sondern eine Falle — und
sie wäre genau einen Fehlklick entfernt gewesen.

GESPEICHERT WIRD SOFORT, mit jedem Klick. Kein "Speichern" am Ende: Bei
zweihundert Feldern ist das die Stelle, an der eine halbe Änderung
verlorengeht, und eine halbe Änderung an Rechten ist die gefährlichste
Lage von allen. Rückfrage gibt es nur beim ÖFFNEN — etwas wegzunehmen
sieht sofort jemand, etwas aufzumachen unter Umständen lange niemand.

DER BEFUND, DEN DIE PRÜFUNG GEFUNDEN HAT: Nimmt man einem Modi eine
Seite weg, greift die Schranke sofort — und die KACHEL blieb stehen. Ein
Knopf, der auf die Startseite zurückwirft. Der Satz "Kachel und Tür
gehören zusammen" steht seit dem 06.09. im Code; bis heute war er eine
Bitte an den, der beides pflegt. Jetzt ist er eine Rechnung: Beide
Kachellisten — die des Servers und die im Browser — werden gegen
dieselbe Tafel gefiltert, aus der die Schranke ihre Entscheidung holt.

Geprüft wird nicht, ob die Antwort 200 lautet, sondern ob sich das
VERHALTEN ändert: Der Modi kommt danach wirklich nicht mehr hinein —
mit Gegenprobe davor. Und eine Umstellung überlebt einen Neustart; ohne
tafelLaden() beim Start hätte wieder der Grundstand gegolten, und es
hätte ausgesehen wie vorher.

Geprüft: rollen 315 · start-ansicht 147 · crew-adresse 132 · sicht 84 ·
modi-verborgen 80 · neue-seiten 70 · treff-werkzeuge 70 · nachwuchs 69 ·
treff 58 · rechte-umstellen 46 · entwicklung 40 · css-klassen 30 ·
zwischenspeicher 21 · alle-wege 19 · rechtetafel 19 — alles grün.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-11 22:42:21 +02:00
co-authored by Claude Opus 5
parent dfd951861a
commit 44e5ace109
38 changed files with 1485 additions and 520 deletions
+3
View File
@@ -25,6 +25,7 @@ import { dateienRouter } from "./workspace-dateien.js";
import { bereicheRouter } from "./workspace-bereiche.js";
import { treffRouter } from "./workspace-treff.js";
import { entwicklungRouter } from "./workspace-entwicklung.js";
import { rechteRouter, tafelLaden } from "./workspace-rechte.js";
import { reportRouter } from "./workspace-reports.js";
import { scoutRouter } from "./workspace-scouts.js";
import { callRouter } from "./workspace-calls.js";
@@ -249,6 +250,8 @@ app.use(treffRouter);
-- beides nebeneinander ist Absicht: Der Katalog ist das, was man
regelmaessig durchgeht, die Notiz das, was dazwischen auffaellt. */
app.use(entwicklungRouter);
/* Die Rechtetafel -- ansehen und umstellen (11.09.2026). */
app.use(rechteRouter);
app.use(reportRouter);
app.use(scoutRouter);
app.use(callRouter);
+338
View File
@@ -0,0 +1,338 @@
/* =====================================================================
Die Rechtetafel von Hand umstellen (11.09.2026)
Seit heute kann DogFather die Tafel auf der Seite „Wer sieht was"
ändern. Das ist die gefährlichste Einstellung im ganzen Haus: Ein
Klick öffnet oder schließt eine Seite für eine ganze Rolle.
Deshalb wird hier nicht geprüft, ob die Antwort 200 lautet, sondern
ob sich das VERHALTEN ändert — also ob ein Modi die Seite danach
wirklich nicht mehr aufbekommt. Mit Gegenprobe davor, sonst beweist
es nichts.
Und die drei Riegel:
· Nur DogFather stellt um. Die rechte Hand sieht dieselbe Tafel
und bekommt 403.
· Drei Felder sind fest — er kann sich nicht selbst aussperren.
· Alles zurück auf Grundstand ist ein Knopf, und danach gilt genau
das, was im Code steht.
Aufrufen mit: node server/pruef-rechte-umstellen.mjs
===================================================================== */
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { request as httpAnfrage } from "node:http";
import { portMussFreiSein } from "./helfer-port.mjs";
const PORT = await portMussFreiSein(4405, "pruef-rechte-umstellen");
const ordner = mkdtempSync(join(tmpdir(), "ws-rechte-"));
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
process.env.PORT = String(PORT);
process.env.SITE_ACCESS_SECRET = "lokaler-test";
process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00";
const express = (await import("express")).default;
const ec = express.response.cookie;
express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); };
import { notbremse } from "./helfer-notbremse.mjs";
await import("./index.js");
notbremse(240_000, "pruef-rechte-umstellen");
await new Promise((r) => setTimeout(r, 700));
process.on("uncaughtException", (f) => { console.error("ABSTURZ:", f); process.exit(7); });
process.on("unhandledRejection", (f) => { console.error("ABSTURZ (Versprechen):", f); process.exit(7); });
let fehler = 0, geprueft = 0;
const melde = (t) => console.log(t);
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
const CREW = "crew.dogfather-universe.com";
const HAUS = "workspace.dogfather-universe.com";
const { DatabaseSync } = await import("node:sqlite");
const { scryptSync, randomBytes, createHmac } = await import("node:crypto");
const rechte = await import("./rechte.js");
const modul = await import("./workspace-rechte.js");
const d = new DatabaseSync(process.env.WORKSPACE_DB);
const jetzt = new Date().toISOString();
const schluessel = randomBytes(32).toString("hex");
d.prepare("INSERT INTO einstellungen (schluessel, wert, geaendert, von)"
+ " VALUES (?,?,?,NULL) ON CONFLICT(schluessel) DO NOTHING")
.run("code_kennung_schluessel", schluessel, jetzt);
function anlegen(name, rolle, code) {
const salt = randomBytes(16).toString("hex");
const hash = scryptSync(code, salt, 64, { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n, code_kennung, aktiv, erstellt)"
+ " VALUES (?,?,?,?,?,?,1,?)")
.run(name, rolle, hash, salt, 32768, createHmac("sha256", schluessel).update(code).digest("hex"), jetzt);
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
}
anlegen("Filipe", "admin", "CODE-DOGI-0001");
anlegen("Rieke", "hand", "CODE-HAND-0001");
anlegen("Frida", "modi", "CODE-MODI-0001");
anlegen("Mara", "manager", "CODE-MANA-0001");
d.close();
function roh(pfad, host, kopf = {}, koerper = null, methode = null) {
if (methode === "DELETE") koerper = null;
return new Promise((fertig, schief) => {
const a = httpAnfrage({
host: "127.0.0.1", port: PORT, path: pfad,
method: methode || (koerper ? "POST" : "GET"),
headers: { Host: host, ...(koerper ? { "Content-Type": "application/json" } : {}), ...kopf },
}, (antwort) => {
let text = "";
antwort.on("data", (s) => { text += s; });
antwort.on("end", () => fertig({ code: antwort.statusCode, text, kopf: antwort.headers }));
});
a.on("error", schief);
if (koerper) a.write(JSON.stringify(koerper));
a.end();
});
}
const json = (a) => { try { return JSON.parse(a.text); } catch { return {}; } };
async function anmelden(rolle, code, host) {
const r = await roh("/workspace/api/anmelden", host, {}, { rolle, code });
return [].concat(r.kopf?.["set-cookie"] || []).map((c) => c.split(";")[0]).join("; ");
}
const kDogi = await anmelden("admin", "CODE-DOGI-0001", HAUS);
const kHand = await anmelden("hand", "CODE-HAND-0001", CREW);
const kModi = await anmelden("modi", "CODE-MODI-0001", CREW);
const kMana = await anmelden("manager", "CODE-MANA-0001", HAUS);
ok(!!kDogi && !!kHand && !!kModi && !!kMana, "vier Rollen sind angemeldet");
/* =======================================================================
1. WER DIE SEITE BEKOMMT — und wer sie umstellen darf
======================================================================= */
melde("");
melde("=== 1. Wer die Tafel sieht ===");
{
for (const [wer, host, keks, soll] of [
["DogFather", HAUS, kDogi, true],
["die rechte Hand", CREW, kHand, true],
["ein Modi", CREW, kModi, false],
["eine Managerin", HAUS, kMana, false],
]) {
const seite = await roh("/workspace/rechte.html", host, { cookie: keks });
const api = await roh("/workspace/api/rechte/tafel", host, { cookie: keks });
ok((seite.code === 200) === soll && (api.code === 200) === soll,
`${wer}: Seite ${seite.code}, Tafel ${api.code} — ${soll ? "beides offen" : "beides zu"}`);
}
/* UMSTELLEN IST ETWAS ANDERES ALS SEHEN. */
const alsDogi = json(await roh("/workspace/api/rechte/tafel", HAUS, { cookie: kDogi }));
const alsHand = json(await roh("/workspace/api/rechte/tafel", CREW, { cookie: kHand }));
ok(alsDogi.darf_aendern === true, "DogFather darf umstellen");
ok(alsHand.darf_aendern === false, "die rechte Hand sieht dieselbe Tafel, darf aber nicht");
ok(alsHand.seiten?.length === alsDogi.seiten?.length,
`und sie bekommt wirklich dieselbe (${alsHand.seiten?.length} Seiten)`);
/* DIE KACHEL AUF BEIDEN ADRESSEN (Filipe, 11.09.2026). */
for (const [wer, host, keks, soll] of [
["DogFather auf crew.", CREW, kDogi, true],
["die rechte Hand auf crew.", CREW, kHand, true],
["ein Modi auf crew.", CREW, kModi, false],
]) {
const ich = json(await roh("/workspace/api/ich", host, { cookie: keks }));
const alle = [...(ich.bereiche || []), ...(ich.bereiche_zusatz || [])];
const da = alle.some((k) => k.ziel === "rechte.html");
ok(da === soll, `${wer}: Kachel ${da ? "da" : "nicht da"}`);
}
}
/* =======================================================================
2. UMSTELLEN ÄNDERT DAS VERHALTEN — nicht nur die Anzeige
======================================================================= */
melde("");
melde("=== 2. Ein Klick, und die Seite ist zu ===");
const PROBE = "/workspace/chat.html";
{
/* GEGENPROBE ZUERST. Ohne sie wäre „danach zu" auch dann grün, wenn
die Seite vorher schon zu gewesen wäre. */
const vorher = await roh(PROBE, CREW, { cookie: kModi });
ok(vorher.code === 200, `ein Modi kann chat.html öffnen (${vorher.code})`);
const stellen = await roh("/workspace/api/rechte/feld", HAUS, { cookie: kDogi },
{ pfad: PROBE, rolle: "modi", erlaubt: false }, "PUT");
ok(stellen.code === 200, `DogFather nimmt sie ihm weg (${stellen.code})`);
ok(json(stellen).abweichungen === 1, `eine Abweichung vom Grundstand (${json(stellen).abweichungen})`);
/* DAS IST DER EIGENTLICHE BEWEIS: nicht die Antwort, sondern die
Schranke. */
const nachher = await roh(PROBE, CREW, { cookie: kModi });
ok(nachher.code !== 200, `und die Schranke lässt ihn nicht mehr durch (${nachher.code})`);
/* Und die Kachel ist auch weg -- sonst führte ein Knopf ins Leere. */
const ich = json(await roh("/workspace/api/ich", CREW, { cookie: kModi }));
const alle = [...(ich.bereiche || []), ...(ich.bereiche_zusatz || [])];
ok(!alle.some((k) => (k.ziel || "").startsWith("chat.html")),
"und die Kachel dazu verschwindet mit");
/* DIE ANZEIGE SAGT, DASS ES VON HAND GESETZT IST. */
const tafel = json(await roh("/workspace/api/rechte/tafel", HAUS, { cookie: kDogi }));
const zeile = tafel.seiten.find((s) => s.pfad === PROBE);
ok(!zeile.rollen.includes("modi") && zeile.grund.includes("modi"),
"die Tafel zeigt: jetzt nein, im Grundstand ja");
const modi = tafel.rollen.find((r) => r.schluessel === "modi");
ok(modi.abweichend === 1, `und zählt es bei der Rolle (${modi.abweichend})`);
}
/* =======================================================================
3. ZURÜCK — auf zwei Wegen
======================================================================= */
melde("");
melde("=== 3. Zurück ===");
{
/* WEG EINS: dasselbe Feld wieder anklicken. Die Abweichung muss
dabei VERSCHWINDEN und nicht als „erlaubt" stehenbleiben -- sonst
stünde in der Datenbank eine Zeile, die dasselbe sagt wie der
Code, und die Anzeige meldete „von Hand gesetzt", obwohl nichts
verstellt ist. */
const zurueck = await roh("/workspace/api/rechte/feld", HAUS, { cookie: kDogi },
{ pfad: PROBE, rolle: "modi", erlaubt: true }, "PUT");
ok(zurueck.code === 200 && json(zurueck).abweichungen === 0,
`zurückgeklickt, keine Abweichung mehr (${json(zurueck).abweichungen})`);
const wieder = await roh(PROBE, CREW, { cookie: kModi });
ok(wieder.code === 200, `und der Modi kommt wieder hinein (${wieder.code})`);
/* WEG ZWEI: der Knopf. Erst wieder etwas verstellen, sonst prüft er
nichts. */
await roh("/workspace/api/rechte/feld", HAUS, { cookie: kDogi },
{ pfad: PROBE, rolle: "modi", erlaubt: false }, "PUT");
await roh("/workspace/api/rechte/feld", HAUS, { cookie: kDogi },
{ pfad: "/workspace/wissen.html", rolle: "modi", erlaubt: false }, "PUT");
const vorAllem = json(await roh("/workspace/api/rechte/tafel", HAUS, { cookie: kDogi }));
ok(vorAllem.abweichungen === 2, `zwei Felder verstellt (${vorAllem.abweichungen})`);
const alles = await roh("/workspace/api/rechte/abweichungen", HAUS, { cookie: kDogi },
null, "DELETE");
ok(alles.code === 200 && json(alles).zurueckgenommen === 2,
`der Knopf nimmt beide zurück (${json(alles).zurueckgenommen})`);
const danach = json(await roh("/workspace/api/rechte/tafel", HAUS, { cookie: kDogi }));
ok(danach.abweichungen === 0, "und die Tafel steht wieder auf Grundstand");
/* Und das Verhalten stimmt auch wieder -- die Zahl allein wäre nur
eine Behauptung. */
const chat = await roh(PROBE, CREW, { cookie: kModi });
const wissen = await roh("/workspace/wissen.html", CREW, { cookie: kModi });
ok(chat.code === 200 && wissen.code === 200,
`beide Seiten sind für den Modi wieder offen (${chat.code}/${wissen.code})`);
}
/* =======================================================================
4. DIE DREI FESTEN FELDER
======================================================================= */
melde("");
melde("=== 4. Aussperren geht nicht ===");
{
for (const u of rechte.UNANTASTBAR) {
const a = await roh("/workspace/api/rechte/feld", HAUS, { cookie: kDogi },
{ pfad: u.pfad, rolle: u.rolle, erlaubt: false }, "PUT");
ok(a.code === 400, `${u.pfad.replace("/workspace/", "")} bleibt bei ${u.rolle} (${a.code})`);
ok(/wegnehmen/.test(json(a).fehler || ""),
"und die Absage sagt, warum – nicht nur nein");
}
/* GEGENPROBE: Dieselbe Seite bei einer ANDEREN Rolle geht sehr wohl.
Ohne diese Zeile wäre „fest" auch dann grün, wenn gar nichts
umstellbar wäre. */
const andere = await roh("/workspace/api/rechte/feld", HAUS, { cookie: kDogi },
{ pfad: "/workspace/rechte.html", rolle: "hand", erlaubt: false }, "PUT");
ok(andere.code === 200, `dieselbe Seite bei der rechten Hand schon (${andere.code})`);
const handSeite = await roh("/workspace/rechte.html", CREW, { cookie: kHand });
ok(handSeite.code !== 200, `und sie kommt danach nicht mehr hinein (${handSeite.code})`);
await roh("/workspace/api/rechte/abweichungen", HAUS, { cookie: kDogi }, null, "DELETE");
/* Die Oberfläche bekommt die feste Liste mitgeliefert -- damit sie
den Knopf gar nicht erst als Knopf zeigt. */
const tafel = json(await roh("/workspace/api/rechte/tafel", HAUS, { cookie: kDogi }));
const zeile = tafel.seiten.find((s) => s.pfad === "/workspace/rechte.html");
ok(zeile?.fest?.includes("admin"), `und die Tafel sagt, welches Feld fest ist (${zeile?.fest})`);
}
/* =======================================================================
5. WER NICHT DARF, DARF AUCH ÜBER DIE SCHNITTSTELLE NICHT
======================================================================= */
melde("");
melde("=== 5. Nur DogFather stellt um ===");
{
const hand = await roh("/workspace/api/rechte/feld", CREW, { cookie: kHand },
{ pfad: PROBE, rolle: "modi", erlaubt: false }, "PUT");
ok(hand.code === 403, `die rechte Hand bekommt 403 (${hand.code})`);
ok(/DogFather/.test(json(hand).fehler || ""), "und erfährt, wer es darf");
const modi = await roh("/workspace/api/rechte/feld", CREW, { cookie: kModi },
{ pfad: PROBE, rolle: "modi", erlaubt: true }, "PUT");
ok(modi.code === 404, `ein Modi bekommt 404 – für ihn gibt es die Tafel nicht (${modi.code})`);
const alles = await roh("/workspace/api/rechte/abweichungen", CREW, { cookie: kHand },
null, "DELETE");
ok(alles.code === 403, `und zurücksetzen darf sie auch nicht (${alles.code})`);
/* GEGENPROBE: Nach all dem ist die Tafel unverändert. Ein
abgewiesener Versuch, der trotzdem etwas ändert, wäre der
schlimmste Ausgang von allen. */
const tafel = json(await roh("/workspace/api/rechte/tafel", HAUS, { cookie: kDogi }));
ok(tafel.abweichungen === 0, `nichts davon hat etwas verändert (${tafel.abweichungen})`);
}
/* =======================================================================
6. ERFUNDENES WIRD ABGEWIESEN
======================================================================= */
melde("");
melde("=== 6. Erfundenes ===");
{
const seite = await roh("/workspace/api/rechte/feld", HAUS, { cookie: kDogi },
{ pfad: "/workspace/gibt-es-nicht.html", rolle: "modi", erlaubt: true }, "PUT");
ok(seite.code === 400, `eine erfundene Seite (${seite.code})`);
const rolle = await roh("/workspace/api/rechte/feld", HAUS, { cookie: kDogi },
{ pfad: PROBE, rolle: "chef", erlaubt: true }, "PUT");
ok(rolle.code === 400, `eine erfundene Rolle (${rolle.code})`);
/* Und die Zugangswände stehen gar nicht erst in der Tafel -- sie
lassen sich deshalb auch nicht schließen. */
const wand = await roh("/workspace/api/rechte/feld", HAUS, { cookie: kDogi },
{ pfad: "/workspace/crew-index.html", rolle: "admin", erlaubt: false }, "PUT");
ok(wand.code === 400, `und eine Zugangswand auch nicht (${wand.code})`);
}
/* =======================================================================
7. EINE UMSTELLUNG ÜBERLEBT EINEN NEUSTART
======================================================================= */
melde("");
melde("=== 7. Nach einem Neustart ===");
{
await roh("/workspace/api/rechte/feld", HAUS, { cookie: kDogi },
{ pfad: PROBE, rolle: "modi", erlaubt: false }, "PUT");
/* DEN NEUSTART NACHSTELLEN: die Tafel auf den Grundstand
zuruecksetzen (so, wie sie nach einem frischen Start waere) und
dann den Ladevorgang aufrufen, den index.js beim Start macht.
OHNE DIESEN AUFRUF GAELTE NACH EINEM NEUSTART WIEDER DER
GRUNDSTAND -- und niemand haette es gemerkt, denn es saehe aus wie
vorher. Genau das ist die Sorte Fehler, die man nur findet, wenn
man danach sucht. */
rechte.tafelSetzen(null);
ok(rechte.darfSeite({ rolle: "modi" }, PROBE) === true,
"ohne geladene Abweichungen gilt der Grundstand");
const stand = modul.tafelLaden();
ok(stand.abweichungen === 1, `tafelLaden() findet die Abweichung (${stand.abweichungen})`);
ok(rechte.darfSeite({ rolle: "modi" }, PROBE) === false,
"und danach gilt sie wieder");
const nachher = await roh(PROBE, CREW, { cookie: kModi });
ok(nachher.code !== 200, `auch für den Modi im Browser (${nachher.code})`);
await roh("/workspace/api/rechte/abweichungen", HAUS, { cookie: kDogi }, null, "DELETE");
ok(rechte.darfSeite({ rolle: "modi" }, PROBE) === true, "und am Ende ist alles wieder offen");
}
/* ------------------------------------------------------------------- */
melde("");
melde(`${geprueft} Pruefungen, ${fehler} Fehler`);
melde(fehler ? "NICHT IN ORDNUNG" : "ALLES IN ORDNUNG");
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Aufraeumrest */ }
process.exit(fehler ? 1 : 0);
+15 -3
View File
@@ -533,10 +533,22 @@ melde("=== 8. Der Regeltext ===");
ok(!wer.includes("manager") && !wer.includes("creator") && !wer.includes("spicy"),
"und nicht fuer die Agentur");
/* "Wer sieht was" ist allein DogFathers Seite. */
/* "Wer sieht was" gehoert denen, die das Team fuehren.
STAND HIER BIS ZUM ABEND AUF `length === 1 && [0] === "admin"`.
Filipe danach: "ich will die kategorie auch auf der team dogi seite
fuer die dogfather und rechte hand rollen" -- die Zeile war rot,
obwohl genau das Richtige passierte.
Geprueft wird jetzt, was die Zeile eigentlich behaupten will: NICHT
die Agentur, NICHT die Modis, NICHT die Community. Wer dazukommt,
ist eine Entscheidung; wer NIE dazugehoert, ist der Riegel. */
const tafel = rechte.SEITEN["/workspace/rechte.html"] || [];
ok(tafel.length === 1 && tafel[0] === "admin",
`"Wer sieht was" nur fuer ${tafel.join(", ")}`);
const drin = ["admin", "hand"].filter((r) => tafel.includes(r));
const drausen = ["spicy", "manager", "scout", "creator", "modi", "gast"]
.filter((r) => tafel.includes(r));
ok(drin.length === 2 && drausen.length === 0,
`"Wer sieht was" fuer ${tafel.join(", ")} -- und fuer sonst niemanden`);
const fremd = await roh("/workspace/api/rechte/tafel", CREW, { cookie: kModi });
ok(fremd.code === 404, `ein Modi bekommt die Tafel nicht (${fremd.code})`);
+107 -6
View File
@@ -305,10 +305,21 @@ export const SEITEN = {
sondern eine Einladung zur Vergeltung. */
"/workspace/treff-moderation.html": ["modi", "hand", "admin"],
/* WER SIEHT WAS. Nur DogFather — die Seite ist eine vollstaendige
Karte dieses Hauses: jede Seite, jede Rolle. Wer sie hat, weiss,
wo er es versuchen muesste. */
"/workspace/rechte.html": ["admin"],
/* WER SIEHT WAS. DogFather und die rechte Hand (Filipe, 11.09.2026:
"ich will die kategorie auch auf der team dogi seite fuer die
dogfather und rechte hand rollen").
ANSEHEN BEIDE, UMSTELLEN NUR ER. Sie fuehren das Team zusammen;
wer mitentscheidet, muss nachsehen koennen. Wer aber Rechte
vergeben kann, kann sich Rechte vergeben -- und diese eine Tuer
bleibt bei ihm. Durchgesetzt wird das in workspace-rechte.js, nicht
hier: Diese Tafel beantwortet, wer die SEITE oeffnen darf, nicht
was er darauf tun kann.
Weiterhin ausdruecklich NICHT fuer die Agentur und nicht fuer die
Modis: Die Seite ist eine vollstaendige Karte dieses Hauses -- jede
Seite, jede Rolle. Wer sie hat, weiss, wo er es versuchen muesste. */
"/workspace/rechte.html": ["admin", "hand"],
};
/* Die Zugangswaende. Sie haben keine Rolle, weil sich dort noch
@@ -335,10 +346,62 @@ export const OHNE_ANMELDUNG = [
* @param {{rolle?: string}|null} person
* @param {string} pfad z. B. "/workspace/start.html"
*/
/* =====================================================================
DIE TAFEL IST SEIT DEM 11.09.2026 VERAENDERBAR — aber nur auf EINE Art
=====================================================================
Filipe: *"so dass ich die da auch manuel wechseln und speichern kann."*
WAS OBEN STEHT, IST AB JETZT DER GRUNDSTAND, nicht mehr die ganze
Wahrheit. Was DogFather auf der Seite "Wer sieht was" umstellt, liegt
in der Datenbank und wird hier darübergelegt.
DREI ENTSCHEIDUNGEN, DIE MAN DEM CODE NICHT ANSIEHT:
1. GESPEICHERT WIRD NUR DER UNTERSCHIED. In der Datenbank steht eine
Zeile nur dann, wenn sie vom Grundstand ABWEICHT. Damit bedeutet
der Code oben weiterhin etwas -- man sieht, was gedacht war, und
daneben, was jemand daraus gemacht hat. Eine vollstaendige Kopie
in der Datenbank waere nach einer Woche die einzige Wahrheit, und
der Code oben wuerde still veralten.
2. DER GRUNDSTAND IST DER RUECKFALL, wenn die Abweichungen nicht
gelesen werden koennen. Das ist die richtige Richtung: Er ist der
geprüfte Zustand, nicht der zufaellige. Ein Fehlschlag wird laut
protokolliert -- eine Tafel, die still auf etwas anderes
zurueckfaellt, waere die schlimmste Sorte Fehler in dieser Datei.
3. GEAENDERT WIRD NICHT HIER. Diese Datei kennt keine Datenbank; sie
nimmt eine fertige Tafel entgegen. Wer sie setzt und wer das darf,
steht in workspace-rechte.js. Zwei Dateien, zwei Fragen -- und die
Rechtepruefung bleibt ohne laufenden Server pruefbar.
===================================================================== */
/** Die Tafel, die gerade gilt. Am Anfang der Grundstand. */
let TAFEL = SEITEN;
/** Setzt die geltende Tafel. "null" stellt den Grundstand wieder her. */
export function tafelSetzen(neu) {
TAFEL = neu && typeof neu === "object" ? neu : SEITEN;
}
/** Die Tafel, die gerade gilt -- fuer die Anzeige und fuer Pruefungen. */
export function tafelJetzt() {
return TAFEL;
}
/** Weicht diese Seite/Rolle vom Grundstand ab? Fuer die Anzeige: Man
* soll sehen, was jemand von Hand geaendert hat. */
export function weichtAb(pfad, rolle) {
const grund = Array.isArray(SEITEN[pfad]) && SEITEN[pfad].includes(rolle);
const jetzt = Array.isArray(TAFEL[pfad]) && TAFEL[pfad].includes(rolle);
return grund !== jetzt;
}
export function darfSeite(person, pfad) {
const rolle = person?.rolle;
if (!rolle) return false;
const erlaubt = SEITEN[String(pfad || "")];
const erlaubt = TAFEL[String(pfad || "")];
/* Kein Eintrag heisst NEIN. Vorher hiess es ja -- und genau darin
lag das Loch, das man einer neuen Seite nicht ansieht. */
if (!Array.isArray(erlaubt)) return false;
@@ -353,7 +416,7 @@ export function darfSeite(person, pfad) {
* Stelle, an der die beiden auseinanderlaufen.
*/
export function seitenFuer(rolle) {
return Object.entries(SEITEN)
return Object.entries(TAFEL)
.filter(([, r]) => r.includes(rolle))
.map(([p]) => p)
.sort();
@@ -361,5 +424,43 @@ export function seitenFuer(rolle) {
/** Alle Seiten der Tafel -- fuer die Pruefung gegen die Platte. */
export function alleSeiten() {
/* AUS DEM GRUNDSTAND, nicht aus der geltenden Tafel.
Die Liste der SEITEN aendert sich durch eine Umstellung nicht --
nur, wer sie oeffnen darf. Wer hier TAFEL naehme, bekaeme bei einem
Fehler in der Zusammenfuehrung eine kuerzere Liste, und
pruef-rechtetafel ("jede Datei hat einen Eintrag") wuerde dann
stillschweigend weniger pruefen. */
return Object.keys(SEITEN).sort();
}
/** Nur DogFather darf die Tafel umstellen -- und selbst er nicht alles.
*
* DIESE DREI SEITEN KANN ER SICH NICHT SELBST WEGNEHMEN. Ohne
* "rechte.html" koennte er die Aenderung nicht zuruecknehmen, ohne
* "personen.html" keinen Zugang mehr vergeben, ohne "start.html"
* nirgends mehr hin. Eine Sperre, aus der man sich aussperren kann,
* ist keine Einstellung, sondern eine Falle -- und sie waere genau
* einen Fehlklick entfernt.
*
* Fuer JEDE andere Rolle darf er jede Seite umstellen. Das ist der
* Sinn der Sache: "soll jede rolle immer nur das sehen was sie sehen
* sollen" -- wer das entscheidet, ist er. */
export const UNANTASTBAR = [
{ pfad: "/workspace/rechte.html", rolle: "admin" },
{ pfad: "/workspace/personen.html", rolle: "admin" },
{ pfad: "/workspace/start.html", rolle: "admin" },
];
/** @returns {string|null} Grund, warum das nicht geht -- oder null. */
export function darfUmstellen(pfad, rolle, erlaubt) {
if (!Object.prototype.hasOwnProperty.call(SEITEN, pfad)) return "Diese Seite gibt es nicht.";
if (!ALLE_ROLLEN.includes(rolle)) return "Diese Rolle gibt es nicht.";
if (erlaubt) return null;
const fest = UNANTASTBAR.find((u) => u.pfad === pfad && u.rolle === rolle);
if (fest) {
return "Diese Seite kannst du dir nicht selbst wegnehmen – sonst käme "
+ "niemand mehr an die Rechte, an die Zugänge oder auf die Startseite.";
}
return null;
}
+268
View File
@@ -0,0 +1,268 @@
/* =====================================================================
Die Rechtetafel von Hand umstellen (11.09.2026)
=====================================================================
Filipe: *„ich will die kategorie auch auf der team dogi seite für die
dogfather und rechte hand rollen … und wenn man drauf drückt,
perfektionieren so dass ich die da auch manuell wechseln und
speichern kann."*
---------------------------------------------------------------------
WAS GESPEICHERT WIRD: NUR DER UNTERSCHIED
In `seitenrecht` steht eine Zeile nur, wenn sie vom Grundstand in
rechte.js ABWEICHT. Drei Gründe, und der dritte ist der wichtigste:
* Der Code oben bedeutet weiterhin etwas. Man sieht, was gedacht
war — und daneben, was daraus geworden ist.
* „Zurück auf Grundstand" ist ein DELETE, kein Umschreiben.
* Eine neue Seite erbt automatisch den Grundstand. Eine
vollständige Kopie in der Datenbank hätte sie nicht gekannt, und
sie wäre für alle zu gewesen — ohne dass es jemand entschieden
hätte.
---------------------------------------------------------------------
WER DARF WAS
SEHEN: DogFather und die rechte Hand. Sie führen das Team zusammen;
wer mitentscheidet, muss nachsehen können.
UMSTELLEN: nur DogFather. Das ist dieselbe Linie wie bei den
Zugängen („Zugang anlegen: nur DogFather") — wer Rechte vergeben
kann, kann sich Rechte vergeben, und diese eine Tür bleibt bei ihm.
Das stellt ihn nicht über sein Team; es ist die Aufgabe, die er
ohnehin hat.
UND DREI ZEILEN KANN AUCH ER NICHT ANFASSEN (rechte.js, UNANTASTBAR):
sich selbst die Rechte-, die Personen- oder die Startseite
wegzunehmen. Eine Einstellung, aus der man sich aussperren kann, ist
keine Einstellung, sondern eine Falle — und sie wäre genau einen
Fehlklick entfernt.
---------------------------------------------------------------------
WANN DIE TAFEL NEU GELADEN WIRD
Beim Start und nach jeder Änderung — in DIESEM Prozess. Der Server
läuft als ein Prozess; eine zweite Instanz gäbe es nur bei einem
Neustart, und der lädt ohnehin neu. Ein Zwischenspeicher mit
Laufzeit wäre hier falsch: Wer ein Recht wegnimmt, will nicht, dass
es noch zehn Minuten gilt.
FÄLLT DAS LESEN AUS, gilt der Grundstand — und es wird laut
protokolliert. Das ist die richtige Richtung: Der Grundstand ist der
geprüfte Zustand. Eine Tafel, die still auf etwas anderes
zurückfällt, wäre die schlimmste Sorte Fehler in diesem Haus.
===================================================================== */
import express from "express";
import {
db, sitzungLesen, protokolliere, echteIp, fuehrtTeamDogi, ROLLEN_NAME,
} from "./workspace.js";
import {
SEITEN, ALLE_ROLLEN, OHNE_ANMELDUNG, UNANTASTBAR,
tafelSetzen, tafelJetzt, darfUmstellen, alleSeiten,
} from "./rechte.js";
export const rechteRouter = express.Router();
const jetzt = () => new Date().toISOString();
let bereit = false;
function tabelle() {
if (bereit) return;
db().exec(`
/* Eine Abweichung vom Grundstand. NUR Abweichungen -- was hier
nicht steht, gilt so, wie es in rechte.js steht. */
CREATE TABLE IF NOT EXISTS seitenrecht (
pfad TEXT NOT NULL,
rolle TEXT NOT NULL,
erlaubt INTEGER NOT NULL CHECK (erlaubt IN (0, 1)),
von INTEGER REFERENCES personen(id) ON DELETE SET NULL,
am TEXT NOT NULL,
PRIMARY KEY (pfad, rolle)
);
`);
bereit = true;
}
/**
* Baut die geltende Tafel aus Grundstand + Abweichungen und setzt sie.
*
* WIRD BEIM START GERUFEN (index.js) UND NACH JEDER AENDERUNG. Ohne den
* Aufruf beim Start gaelte nach einem Neustart wieder der Grundstand --
* und niemand haette es gemerkt, denn es saehe aus wie vorher.
*/
export function tafelLaden() {
try {
tabelle();
const zeilen = db().prepare("SELECT pfad, rolle, erlaubt FROM seitenrecht").all();
if (!zeilen.length) { tafelSetzen(null); return { abweichungen: 0 }; }
/* Eine KOPIE, nicht der Grundstand selbst -- sonst waere er nach der
ersten Aenderung ueberschrieben und "zurueck auf Grundstand"
haette nichts mehr, wohin es zurueckkann. */
const neu = {};
for (const [pfad, rollen] of Object.entries(SEITEN)) neu[pfad] = [...rollen];
let gezaehlt = 0;
for (const z of zeilen) {
if (!Object.prototype.hasOwnProperty.call(neu, z.pfad)) continue; // Seite gibt es nicht mehr
if (!ALLE_ROLLEN.includes(z.rolle)) continue; // Rolle gibt es nicht mehr
const drin = neu[z.pfad].includes(z.rolle);
if (z.erlaubt && !drin) { neu[z.pfad].push(z.rolle); gezaehlt++; }
if (!z.erlaubt && drin) {
neu[z.pfad] = neu[z.pfad].filter((r) => r !== z.rolle);
gezaehlt++;
}
}
tafelSetzen(neu);
return { abweichungen: gezaehlt };
} catch (f) {
/* LAUT, NICHT LEISE. Eine Tafel, die still auf den Grundstand
zurueckfaellt, sieht aus wie eine Tafel, die stimmt. */
console.error("[rechte] Tafel konnte nicht geladen werden, es gilt der "
+ "Grundstand aus rechte.js:", f?.message);
tafelSetzen(null);
return { abweichungen: 0, fehler: String(f?.message || f) };
}
}
/* ---------------------------------------------------------------------
Die Wege
------------------------------------------------------------------- */
function angemeldet(req, res, next) {
const person = sitzungLesen(req);
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
req.person = person;
try { tabelle(); } catch (f) {
console.error("[rechte] Tabelle:", f?.message);
return res.status(503).json({ fehler: "nicht_verfuegbar" });
}
next();
}
/** Sehen: DogFather und die rechte Hand. 404 fuer alle anderen -- wer
* "kein Zugriff" liest, fragt als Naechstes, wer denn dann. */
function nurLeitung(req, res, next) {
if (fuehrtTeamDogi(req.person)) return next();
return res.status(404).json({ fehler: "nicht_gefunden" });
}
/** Umstellen: nur DogFather. */
function nurDogFather(req, res, next) {
if (req.person?.rolle === "admin") return next();
return res.status(403).json({
fehler: "Umstellen kann das nur DogFather. Ansehen darfst du alles.",
});
}
function gleicheHerkunft(req, res, next) {
const herkunft = req.get("origin");
if (!herkunft) return next();
let erlaubt;
try { erlaubt = new URL(herkunft).host === req.get("host"); } catch { erlaubt = false; }
if (!erlaubt) return res.status(403).json({ fehler: "fremde_herkunft" });
next();
}
rechteRouter.use("/workspace/api/rechte", angemeldet, nurLeitung);
rechteRouter.get("/workspace/api/rechte/tafel", (req, res) => {
try {
const tafel = tafelJetzt();
const seiten = alleSeiten().map((pfad) => ({
pfad,
datei: pfad.replace("/workspace/", ""),
rollen: [...(tafel[pfad] || [])],
grund: [...(SEITEN[pfad] || [])],
/* Was DogFather hier NICHT abwaehlen kann -- damit die Oberflaeche
den Knopf gar nicht erst als Knopf zeigt statt ihn eine Absage
bringen zu lassen. */
fest: UNANTASTBAR.filter((u) => u.pfad === pfad).map((u) => u.rolle),
}));
res.json({
darf_aendern: req.person.rolle === "admin",
rollen: ALLE_ROLLEN.map((r) => ({
schluessel: r,
name: ROLLEN_NAME[r] ?? r,
seiten: seiten.filter((s) => s.rollen.includes(r)).length,
/* Wie viele davon von Hand gesetzt sind -- die Zahl, an der man
sieht, ob jemand etwas verstellt hat. */
abweichend: seiten.filter((s) => s.rollen.includes(r) !== s.grund.includes(r)).length,
})),
seiten,
ohne_anmeldung: [...OHNE_ANMELDUNG],
abweichungen: seiten.reduce((n, s) => n
+ ALLE_ROLLEN.filter((r) => s.rollen.includes(r) !== s.grund.includes(r)).length, 0),
});
} catch (f) {
console.error("[rechte] Tafel lesen:", f?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
}
});
/* EIN FELD UMSTELLEN.
Gespeichert wird sofort. Ein "Speichern"-Knopf am Ende waere bei
einer Tafel mit 200 Feldern die Stelle, an der man eine halbe
Aenderung verliert -- und bei Rechten ist eine halbe Aenderung die
gefaehrlichste Lage von allen. Zurueckgenommen wird mit demselben
Klick. */
rechteRouter.put("/workspace/api/rechte/feld", gleicheHerkunft, nurDogFather, (req, res) => {
try {
const pfad = String(req.body?.pfad || "");
const rolle = String(req.body?.rolle || "");
const erlaubt = req.body?.erlaubt === true;
const nein = darfUmstellen(pfad, rolle, erlaubt);
if (nein) return res.status(400).json({ fehler: nein });
const imGrundstand = (SEITEN[pfad] || []).includes(rolle);
if (erlaubt === imGrundstand) {
/* WIEDER WIE IM GRUNDSTAND -> die Abweichung verschwindet.
Eine Zeile, die dasselbe sagt wie der Code, waere eine
Abweichung, die keine ist -- und sie wuerde in der Anzeige als
"von Hand gesetzt" stehen, obwohl nichts verstellt ist. */
db().prepare("DELETE FROM seitenrecht WHERE pfad = ? AND rolle = ?").run(pfad, rolle);
} else {
db().prepare(`INSERT INTO seitenrecht (pfad, rolle, erlaubt, von, am)
VALUES (?,?,?,?,?)
ON CONFLICT(pfad, rolle) DO UPDATE SET
erlaubt = excluded.erlaubt, von = excluded.von, am = excluded.am`)
.run(pfad, rolle, erlaubt ? 1 : 0, req.person.id, jetzt());
}
const stand = tafelLaden();
/* JEDE AENDERUNG INS PROTOKOLL. Rechte sind das Einzige im Haus,
bei dem man spaeter fragt "seit wann eigentlich?" -- und dann
muss es dastehen. */
protokolliere("rechte_umgestellt", {
personId: req.person.id, ip: echteIp(req),
detail: `${pfad.replace("/workspace/", "")} · ${rolle} · ${erlaubt ? "darf" : "darf nicht"}`,
});
res.json({ ok: true, erlaubt, abweichungen: stand.abweichungen });
} catch (f) {
console.error("[rechte] Feld:", f?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
}
});
/* ALLES ZURUECK AUF DEN GRUNDSTAND.
Ein Knopf, nicht 200 Klicks. Er ist der Grund, warum man sich hier
ueberhaupt trauen kann, etwas auszuprobieren. */
rechteRouter.delete("/workspace/api/rechte/abweichungen", gleicheHerkunft, nurDogFather,
(req, res) => {
try {
const { n } = db().prepare("SELECT COUNT(*) AS n FROM seitenrecht").get();
db().prepare("DELETE FROM seitenrecht").run();
tafelLaden();
protokolliere("rechte_zurueckgesetzt", {
personId: req.person.id, ip: echteIp(req), detail: `${n} Abweichungen`,
});
res.json({ ok: true, zurueckgenommen: n });
} catch (f) {
console.error("[rechte] Zuruecksetzen:", f?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
}
});
+80 -70
View File
@@ -27,7 +27,7 @@ import { dirname, join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { mkdirSync } from "node:fs";
import { createRequire } from "node:module";
import { darfSeite, OHNE_ANMELDUNG, SEITEN, ALLE_ROLLEN } from "./rechte.js";
import { darfSeite, OHNE_ANMELDUNG, seitenFuer } from "./rechte.js";
/* Ein Modul ohne eigene Importe -- deshalb entsteht hier kein Kreis,
obwohl workspace-treff.js seinerseits aus dieser Datei importiert.
Begruendung im Kopf von treff-tabellen.js. */
@@ -717,6 +717,28 @@ function vorGruppe(liste, gruppe, ...neue) {
Teil des Teams, nicht darueber -- wenn es jemandem zu viel wird, dann
ihnen genauso. Eine Kachel, die nur die anderen ausfuellen, waere
genau die Schieflage, die dieses Haus nicht haben soll. */
/* WER SIEHT WAS -- auf BEIDEN Adressen (Filipe, 11.09.2026).
Sie stand nur bei den Zusatzkacheln der Agenturseite. Auf der
Team-Dogi-Seite arbeiten DogFather und die rechte Hand aber taeglich;
dort nicht nachsehen zu koennen, wer was darf, hiesse, die Frage an
dem Ort zu stellen, an dem man gerade nicht ist.
EINMAL BESCHRIEBEN, ZWEIMAL BENUTZT. Zwei Kachelobjekte mit
demselben Ziel waeren zwei Gelegenheiten, eines davon umzubenennen.
EIN EIGENER TON, UND ZWAR EIN NEUER (11.09.2026): Erster Anlauf war
Ton 26 -- das waere "Entwicklung" ein zweites Mal gewesen. Zweiter
Anlauf war Ton 22, weil er frei AUSSAH: Ich hatte die Toene ueber
bereicheFuer() gezaehlt, und das liefert die Zusatzkacheln gar nicht
mit. Gefunden hat es pruef-start-ansicht, die ueber die ECHTE Ansicht
zaehlt ("34 Farben auf 35 Kacheln"). Ton 35 ist gemessen, nicht
geraten. */
const RECHTE_KACHEL = {
name: "Wer sieht was", unter: "Jede Rolle, jede Seite – ausgerechnet, nicht notiert",
zeichen: "schutz", ton: 35, ziel: "rechte.html", szene: "studio",
};
const BEFINDEN_KACHEL = {
gruppe: "Für dich", gruppeUnter: "Dein Platz im Team",
name: "Wie geht's dir?", unter: "Sechs Fragen – die Antworten sieht nur du",
@@ -727,7 +749,10 @@ const HAND_BEREICHE = vorGruppe(
[...MODI_BEREICHE, EINGANG_KACHEL, PERSONEN_KACHEL],
"Rund ums Live",
ENTWICKLUNG_KACHEL, TALENTE_KACHEL,
).concat(BEFINDEN_KACHEL);
).concat(BEFINDEN_KACHEL, {
gruppe: "Für dich", gruppeUnter: "Dein Platz im Team",
...RECHTE_KACHEL,
});
/* =====================================================================
@@ -829,6 +854,40 @@ const HAND_MIT_TREFF = [...HAND_BEREICHE, ...TREFF_BEREICHE, TREFF_MODERATION_KA
importiert von dort. Eine zweite Menge waere die, die auseinanderlaeuft. */
export { AUSSEN_ROLLEN };
/* =====================================================================
KACHEL UND TUER GEHOEREN ZUSAMMEN (11.09.2026)
=====================================================================
Seit DogFather die Rechtetafel von Hand umstellen kann, kann eine
Seite zugehen, ohne dass ihre Kachel verschwindet. Gefunden hat das
pruef-rechte-umstellen mit einer einzigen Zeile: Er nahm einem Modi
chat.html weg -- die Schranke griff sofort, und die Kachel stand
weiterhin da. Ein Knopf, der auf die Startseite zurueckwirft.
Der Satz "Kachel und Tuer gehoeren zusammen" steht seit dem
06.09.2026 in assets/js/bereiche.js. Bis heute war er eine Bitte an
den, der beides pflegt. Jetzt ist er eine Rechnung: Die Kachelliste
wird gegen dieselbe Tafel gefiltert, aus der die Schranke ihre
Entscheidung holt.
NACH DEM PFAD UND NICHT NACH DEM GANZEN ZIEL: "bereich.html?b=live"
ist die Seite bereich.html. Wer die Frage am Fragezeichen nicht
abschneidet, filtert jede Brett-Kachel weg -- lautlos, denn eine
fehlende Kachel sieht aus wie eine Rolle, die sie eben nicht hat.
WAS KEIN ZIEL HAT, BLEIBT. Eine Kachel ohne "ziel" ist kein Weg zu
einer Seite, und "ich kenne die Seite nicht" darf nicht "also weg
damit" heissen. */
function nurOffeneKacheln(liste, person) {
if (!Array.isArray(liste) || !person) return liste;
return liste.filter((k) => {
const ziel = String(k?.ziel || "");
if (!ziel) return true;
const pfad = "/workspace/" + ziel.split("?")[0];
return darfSeite(person, pfad);
});
}
export function bereicheFuer(person) {
if (person?.rolle === "modi") return MODI_MIT_TREFF;
if (person?.rolle === "hand") return HAND_MIT_TREFF;
@@ -1201,22 +1260,8 @@ const ZUSATZ_BEREICHE = {
IN "Team & System" UND NICHT BEI DEN PERSONEN: Es geht nicht um
Menschen, sondern um das Haus. */
{
...TEAM_GRUPPE,
name: "Wer sieht was", unter: "Jede Rolle, jede Seite – ausgerechnet, nicht notiert",
/* EIN EIGENER TON, UND ZWAR EIN NEUER (11.09.2026).
Erster Anlauf war Ton 26 -- das waere "Entwicklung" ein zweites
Mal gewesen. Zweiter Anlauf war Ton 22, weil er frei AUSSAH:
Ich hatte die Toene ueber bereicheFuer() gezaehlt, und das
liefert die Zusatzkacheln gar nicht mit. Beide Male haette
sich diese Kachel eine Farbe mit einer anderen geteilt, und
beide Male haette ich es fuer geprueft gehalten.
Gefunden hat es pruef-start-ansicht, die ueber die ECHTE
Ansicht zaehlt: "34 Farben auf 35 Kacheln". Der 35. Ton ist
jetzt gemessen (start.css, Abstand 31,4 in Lab zum naechsten
Nachbarn) -- und nicht mehr aus einer Luecke geraten. */
zeichen: "schutz", ton: 35, ziel: "rechte.html", szene: "studio",
...TEAM_GRUPPE, ...RECHTE_KACHEL,
gruppe: TEAM_GRUPPE.gruppe, gruppeUnter: TEAM_GRUPPE.gruppeUnter,
},
{ ...ENTWICKLUNG_KACHEL, gruppeNach: TEAM_GRUPPE.gruppe },
{ ...TALENTE_KACHEL, gruppeNach: TEAM_GRUPPE.gruppe },
@@ -3590,57 +3635,13 @@ function stillerZugang(code) {
}
}
/* =====================================================================
WER SIEHT WAS — die Tafel, wie sie wirklich ist (11.09.2026)
=====================================================================
/* DIE TAFEL LIEGT SEIT DEM 11.09.2026 IN workspace-rechte.js.
Filipe: *"egal welche Rolle oder Person hinzugefuegt wird, soll immer
nur das sehen, was ich erlaube."*
Dazu gehoert, dass er es NACHSEHEN kann. Eine Regel, die man nur
glauben kann, ist eine Zusage; eine, die man ansehen kann, ist eine
Auskunft.
DIESE ANTWORT WIRD GERECHNET, NICHT GEPFLEGT. Sie kommt aus
derselben Tafel, aus der auch die Schranke ihre Entscheidung holt
(rechte.js). Eine von Hand geschriebene Uebersicht waere eine zweite
Wahrheit — und ausgerechnet die, der man glaubt, weil sie wie eine
Auskunft aussieht. Sie wuerde altern, und man wuerde es ihr nicht
ansehen.
NUR DogFather. Nicht aus Geheimniskraemerei, sondern weil diese
Antwort eine vollstaendige Karte des Hauses ist: jede Seite, jede
Rolle, und wo die Luecken sind. Wer sie hat, weiss, wo er es
versuchen muesste.
===================================================================== */
workspaceRouter.get("/workspace/api/rechte/tafel", (req, res) => {
const person = sitzungLesen(req);
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
if (person.rolle !== "admin") return res.status(404).json({ fehler: "nicht_gefunden" });
const seiten = Object.entries(SEITEN).map(([pfad, rollen]) => ({
pfad,
datei: pfad.replace("/workspace/", ""),
rollen: [...rollen],
})).sort((a, b) => a.datei.localeCompare(b.datei, "de"));
res.json({
rollen: ALLE_ROLLEN.map((r) => ({
schluessel: r,
name: ROLLEN_NAME[r] ?? r,
/* Wie viele der Seiten diese Rolle oeffnen darf. Die Zahl ist
die eigentliche Antwort auf "sieht die neue Rolle zu viel?" */
seiten: seiten.filter((s) => s.rollen.includes(r)).length,
})),
seiten,
ohne_anmeldung: [...OHNE_ANMELDUNG],
/* Die Zahl, die den Unterschied macht: Wie viele Seiten stehen
fuer NIEMANDEN offen, ohne dass jemand das entschieden haette?
Seit dem 11.09. ist sie immer 0 — es gibt keinen Zustand mehr,
der "alle angemeldeten" bedeutet, ohne dass es dasteht. */
ohne_eintrag: 0,
});
});
Hier stand ein nur LESENDER Weg. Filipe: "so dass ich die da auch
manuell wechseln und speichern kann" -- damit gehoeren Lesen und
Umstellen zusammen, samt der Tabelle dahinter und der Frage, wer was
darf. Ein Leseweg hier und ein Schreibweg dort waeren zwei Orte fuer
dieselbe Sache. */
workspaceRouter.post("/workspace/api/anmelden", (req, res) => {
const ip = echteIp(req);
@@ -3958,12 +3959,21 @@ workspaceRouter.get("/workspace/api/ich", (req, res) => {
sehen. Genau daran war beim Sicht-Umschalter schon einmal zu
erkennen, dass er nicht greift ("ich seh immer noch die Seite
genau wie meine"). */
bereiche: bereicheFuer(angesehen || person),
bereiche: nurOffeneKacheln(bereicheFuer(angesehen || person), angesehen || person),
rolle_text: rollentextFuer(angesehen || person),
marke: markeFuer(angesehen || person, req.get("host")),
/* Kacheln, die zur eigenen Liste DAZUkommen -- im Gegensatz zu
`bereiche`, das sie ersetzt. Leer fuer alle, die keine haben. */
bereiche_zusatz: zusatzBereicheFuer(angesehen || person),
bereiche_zusatz: nurOffeneKacheln(zusatzBereicheFuer(angesehen || person),
angesehen || person),
/* DIE EIGENEN SEITEN -- damit die Oberflaeche ihre EIGENE
Kachelliste (assets/js/bereiche.js) ebenfalls filtern kann.
Geliefert wird, was die Person DARF, nicht was sie nicht darf:
Eine Liste der verbotenen Seiten waere eine Aufzaehlung dessen,
was es sonst noch gibt -- und damit genau die Auskunft, die
niemand bekommen soll. */
seiten: seitenFuer((angesehen || person).rolle),
});
});
+4 -4
View File
@@ -13,25 +13,25 @@
"theme_color": "#06090f",
"icons": [
{
"src": "/assets/img/app-symbole/workspace-192.png?v=202609112217",
"src": "/assets/img/app-symbole/workspace-192.png?v=202609112230",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/assets/img/app-symbole/workspace-512.png?v=202609112217",
"src": "/assets/img/app-symbole/workspace-512.png?v=202609112230",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609112217",
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609112230",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609112217",
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609112230",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
+77
View File
@@ -294,3 +294,80 @@
.treff-fall__zeile { display: block; }
.treff-fall__schild { display: block; padding: 0 0 1px; }
}
/* =====================================================================
DIE TAFEL IST SEIT DEM 11.09.2026 BEDIENBAR
Aus jedem Feld wird ein Schalter. Zwei Dinge müssen dabei sichtbar
sein, und beide sind leicht zu übersehen:
1. DASS man klicken kann. Ein Punkt in einer Tabelle sieht nach
Anzeige aus, nicht nach Bedienelement — deshalb bekommt jedes
Feld eine Fläche, die auf Mauszeiger und Tastaturfokus
antwortet.
2. WAS von Hand gesetzt ist. Der Code ist der Grundstand; was davon
abweicht, hat jemand entschieden. Ohne Markierung wüsste nach
vier Wochen niemand mehr, was gedacht war.
UND DIE FLÄCHE IST GROSS GENUG ZUM TIPPEN. 34 mal 30 Pixel — unter
der üblichen Empfehlung von 44, aber das ist eine Tabelle mit acht
Spalten auf einem Handy, und ein größeres Feld hieße weniger Spalten.
Der Ausgleich ist der Abstand: zwischen zwei Feldern liegen die
Zellenränder, sodass man nicht auf den Nachbarn kommt.
===================================================================== */
.rechte-leiste {
display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
margin: 0 0 20px; padding: 12px 16px;
background: color-mix(in srgb, var(--tinte) 94%, #ffffff 6%);
border: 1px solid var(--rand);
}
.rechte-leiste__satz {
flex: 1 1 240px; font-size: .85rem; line-height: 1.55; color: var(--text-leise);
}
.rechte-feld {
display: inline-flex; align-items: center; justify-content: center;
width: 34px; height: 30px; padding: 0; cursor: pointer;
font: inherit;
background: transparent;
border: 1px solid transparent; border-radius: 6px;
transition: background .14s ease, border-color .14s ease;
}
.rechte-feld:hover:not(:disabled) {
background: rgba(255, 255, 255, .07);
border-color: var(--rand-hell);
}
.rechte-feld:focus-visible {
outline: 2px solid var(--akzent); outline-offset: 1px;
}
.rechte-feld:disabled { opacity: .45; cursor: default; }
/* FEST heißt: kein Schalter, sondern eine Tatsache. Er sieht deshalb
nicht aus wie etwas, das man drücken könnte — der Server würde es
ohnehin ablehnen, und ein Knopf, der immer eine Absage bringt, wird
trotzdem gedrückt. */
.rechte-feld[data-fest="ja"] {
cursor: default;
border-style: dashed;
border-color: var(--rand);
}
.rechte-feld[data-fest="ja"]:hover { background: transparent; }
/* VON HAND GESETZT. Ein Rahmen, keine Farbe: Die Zelle sagt damit
„hier hat jemand entschieden", ohne zu sagen, ob das gut war. */
.rechte-tafel td[data-anders="ja"] {
background: color-mix(in srgb, var(--akzent) 9%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--akzent) 34%, transparent);
}
.rechte-zahlen__anders { color: var(--text-still); }
/* Die Meldung nach dem Speichern ist kein Fehler — sie darf nicht
aussehen wie einer. */
.fehler[data-art="gut"] { color: var(--text-leise); }
@media (prefers-reduced-motion: reduce) {
.rechte-feld { transition: none; }
}
+207 -92
View File
@@ -1,14 +1,33 @@
/* =====================================================================
„Wer sieht was" — die Rechtetafel, gezeichnet (11.09.2026)
„Wer sieht was" — ansehen und umstellen (11.09.2026)
Dieses Skript ENTSCHEIDET nichts. Es holt die Tafel vom Server und
zeichnet sie. Jede Zahl und jeder Punkt darin kommt aus rechte.js —
derselben Datei, aus der auch die Schranke ihre Antwort holt.
Filipe: *„so dass ich die da auch manuell wechseln und speichern
kann."*
WARUM DAS WICHTIG IST: Eine Übersicht, die im Browser gebaut wird
(„Rolle X darf wohl Y, weil …"), wäre eine zweite Meinung über die
Rechte — und ausgerechnet die, der man glaubt, weil sie wie eine
Auskunft aussieht. Sie würde altern, ohne dass man es ihr ansieht.
---------------------------------------------------------------------
GESPEICHERT WIRD SOFORT, MIT JEDEM KLICK
Kein „Speichern"-Knopf am Ende. Bei einer Tafel mit zweihundert
Feldern ist der die Stelle, an der man eine halbe Änderung verliert —
und eine halbe Änderung an Rechten ist die gefährlichste Lage von
allen. Zurückgenommen wird mit demselben Klick.
---------------------------------------------------------------------
WAS HIER NICHT ENTSCHIEDEN WIRD
Ob jemand etwas darf. Der Browser fragt und zeichnet; jede Absage
kommt vom Server. Die rechte Hand sieht dieselbe Tafel, bekommt aber
keine Knöpfe — nicht weil dieses Skript sie versteckt, sondern weil
der Server `darf_aendern: false` schickt. Ein Knopf weniger ist
Bequemlichkeit, kein Riegel.
---------------------------------------------------------------------
UND EIN UNTERSCHIED, DEN MAN SEHEN MUSS
Der Code in rechte.js ist der GRUNDSTAND. Was davon abweicht, ist von
Hand gesetzt — und das steht an der Zelle. Ohne diese Markierung wüsste
nach vier Wochen niemand mehr, was gedacht war und was jemand
daraus gemacht hat.
===================================================================== */
(() => {
const $ = (id) => document.getElementById(id);
@@ -18,104 +37,200 @@
if (text !== undefined) n.textContent = text;
return n;
};
const melde = (t) => {
const melde = (t, art) => {
const f = $('fehler');
if (f) { f.textContent = t; f.hidden = !t; }
if (!f) return;
f.textContent = t;
f.hidden = !t;
f.dataset.art = art || 'fehler';
};
(async () => {
let r;
let daten = null;
async function hole(pfad, optionen = {}) {
const a = await fetch(pfad, { credentials: 'same-origin', ...optionen });
if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); }
if (a.status === 404) { location.assign('/workspace/start.html'); throw new Error('unbekannt'); }
return a;
}
/* ---- Ein Feld ------------------------------------------------------- */
function feld(seite, rolle) {
const darf = seite.rollen.includes(rolle.schluessel);
const anders = darf !== seite.grund.includes(rolle.schluessel);
const fest = seite.fest.includes(rolle.schluessel);
const td = document.createElement('td');
if (anders) td.dataset.anders = 'ja';
/* OHNE RECHT ZUM UMSTELLEN BLEIBT ES EIN ZEICHEN, KEIN KNOPF.
Ein Knopf, der nichts tut, ist schlimmer als keiner — man drückt
ihn immer wieder. */
if (!daten.darf_aendern) {
td.append(zeichen(darf));
if (anders) td.title = 'Von Hand gesetzt – weicht vom Grundstand ab.';
return td;
}
const b = el('button', 'rechte-feld');
b.type = 'button';
b.setAttribute('aria-pressed', String(darf));
b.setAttribute('aria-label',
`${rolle.name} · ${seite.datei}: ${darf ? 'darf' : 'darf nicht'}`);
if (fest) b.dataset.fest = 'ja';
b.title = fest
? 'Diese Seite kannst du dir nicht selbst wegnehmen – sonst kämst du '
+ 'nicht mehr an die Rechte, die Zugänge oder die Startseite.'
: (anders ? 'Von Hand gesetzt. Klicken stellt es um.' : 'Klicken stellt es um.');
b.append(zeichen(darf));
b.addEventListener('click', () => umstellen(seite, rolle, !darf, b));
td.append(b);
return td;
}
function zeichen(darf) {
if (darf) {
const punkt = el('span', 'rechte-ja');
punkt.setAttribute('role', 'img');
punkt.setAttribute('aria-label', 'darf');
return punkt;
}
/* Ein Zeichen und nicht ein leeres Feld: Ein Bildschirmleser liest
eine leere Zelle als nichts vor — man hört dann nicht, dass die
Antwort „nein" ist, sondern gar nichts. */
const nein = el('span', 'rechte-nein', '–');
nein.setAttribute('aria-label', 'darf nicht');
return nein;
}
async function umstellen(seite, rolle, erlaubt, knopf) {
/* EINE RÜCKFRAGE NUR BEIM ÖFFNEN, nicht beim Schließen.
Etwas wegzunehmen kann man in einer Sekunde zurückdrehen und es
sieht sofort jemand. Etwas aufzumachen merkt unter Umständen
lange niemand — und genau das ist der Klick, bei dem eine
Sekunde Nachdenken etwas wert ist. */
if (erlaubt && !confirm(
`„${rolle.name}" darf danach ${seite.datei} öffnen.\n\n`
+ 'Das gilt sofort, für alle mit dieser Rolle. Sicher?')) return;
knopf.disabled = true;
try {
const a = await fetch('/workspace/api/rechte/tafel', { credentials: 'same-origin' });
if (a.status === 401) { location.assign('/workspace/'); return; }
/* 404 heißt hier: diese Seite gibt es für dich nicht. Die
Schranke hätte schon vorher umgeleitet; kommt es trotzdem
hierher, ist die Startseite die richtige Antwort — und nicht
eine Fehlermeldung, die verrät, dass es die Auskunft gibt. */
if (a.status === 404) { location.assign('/workspace/start.html'); return; }
if (!a.ok) { melde('Die Tafel konnte nicht geladen werden.'); return; }
r = await a.json();
} catch {
melde('Die Tafel konnte nicht geladen werden.');
return;
const a = await hole('/workspace/api/rechte/feld', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ pfad: seite.pfad, rolle: rolle.schluessel, erlaubt }),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
return;
}
melde(`Gespeichert: „${rolle.name}" darf ${seite.datei} `
+ `${erlaubt ? 'jetzt' : 'nicht mehr'} öffnen.`, 'gut');
await laden();
} finally {
knopf.disabled = false;
}
}
const gesamt = r.seiten.length;
/* ---- Zeichnen --------------------------------------------------------- */
function zeichne() {
const gesamt = daten.seiten.length;
/* ---- Die Zahlen je Rolle ---------------------------------------
Sortiert nach Anzahl, absteigend. Eine alphabetische Reihenfolge
hätte dieselben Zahlen gezeigt und die Frage nicht beantwortet:
Man sieht hier auf einen Blick, wer viel sieht und wer wenig —
und ob irgendwo eine Rolle steht, die mehr sieht, als sie sollte. */
/* Die Zahlen je Rolle. Sortiert nach Anzahl — man sieht auf einen
Blick, wer viel sieht und wer wenig, und ob irgendwo eine Rolle
steht, die mehr sieht als sie sollte. */
const zahlen = $('zahlen');
if (zahlen) {
zahlen.textContent = '';
for (const rolle of [...r.rollen].sort((a, b) => b.seiten - a.seiten)) {
const li = document.createElement('li');
li.append(rolle.name + ' ');
const b = document.createElement('b');
b.textContent = `${rolle.seiten} von ${gesamt}`;
li.append(b);
zahlen.append(li);
zahlen.textContent = '';
for (const rolle of [...daten.rollen].sort((a, b) => b.seiten - a.seiten)) {
const li = document.createElement('li');
li.append(rolle.name + ' ');
const b = document.createElement('b');
b.textContent = `${rolle.seiten} von ${gesamt}`;
li.append(b);
if (rolle.abweichend) {
const m = el('span', 'rechte-zahlen__anders', ` · ${rolle.abweichend} von Hand`);
m.title = 'So viele Felder weichen bei dieser Rolle vom Grundstand ab.';
li.append(m);
}
zahlen.append(li);
}
/* ---- Die Tafel --------------------------------------------------- */
/* Die Leiste: was ist von Hand gesetzt, und wie kommt man zurück. */
const leiste = $('leiste');
leiste.textContent = '';
if (daten.abweichungen) {
leiste.hidden = false;
leiste.append(el('span', 'rechte-leiste__satz',
`${daten.abweichungen} ${daten.abweichungen === 1 ? 'Feld ist' : 'Felder sind'} `
+ 'von Hand gesetzt und weicht vom Grundstand ab.'));
if (daten.darf_aendern) {
const b = el('button', 'schritt', 'Alles zurück auf Grundstand');
b.type = 'button';
b.addEventListener('click', async () => {
if (!confirm(`${daten.abweichungen} Änderungen zurücknehmen?\n\n`
+ 'Danach gilt wieder genau das, was im Code steht.')) return;
b.disabled = true;
const a = await hole('/workspace/api/rechte/abweichungen', { method: 'DELETE' });
b.disabled = false;
if (!a.ok) { melde('Ging nicht.'); return; }
melde('Alles zurück auf Grundstand.', 'gut');
await laden();
});
leiste.append(b);
}
} else {
leiste.hidden = false;
leiste.append(el('span', 'rechte-leiste__satz',
daten.darf_aendern
? 'Alles steht so, wie es im Code steht. Klick auf ein Feld, um es umzustellen – '
+ 'das wird sofort gespeichert.'
: 'Alles steht so, wie es im Code steht. Umstellen kann das nur DogFather.'));
}
/* Die Tafel. */
const ziel = $('tafel');
if (ziel) {
ziel.textContent = '';
const tab = document.createElement('table');
ziel.textContent = '';
const tab = document.createElement('table');
const kopf = document.createElement('thead');
const kz = document.createElement('tr');
kz.append(el('th', null, 'Seite'));
for (const rolle of r.rollen) {
const th = el('th', null, rolle.name);
/* Der Schlüssel als Titel: Im Alltag liest man „Community",
im Code steht „gast". Wer die beiden nicht zusammenbringen
kann, sucht im Quelltext an der falschen Stelle. */
th.title = rolle.schluessel;
kz.append(th);
}
kopf.append(kz);
tab.append(kopf);
const koerper = document.createElement('tbody');
for (const seite of r.seiten) {
const tr = document.createElement('tr');
tr.append(el('td', null, seite.datei));
for (const rolle of r.rollen) {
const td = document.createElement('td');
if (seite.rollen.includes(rolle.schluessel)) {
const punkt = el('span', 'rechte-ja');
punkt.setAttribute('role', 'img');
punkt.setAttribute('aria-label', 'darf');
td.append(punkt);
} else {
/* Ein Zeichen und nicht ein leeres Feld: Ein Bildschirmleser
liest eine leere Zelle als nichts vor — man hört dann
nicht, dass die Antwort „nein" ist, sondern gar nichts. */
const nein = el('span', 'rechte-nein', '–');
nein.setAttribute('aria-label', 'darf nicht');
td.append(nein);
}
tr.append(td);
}
koerper.append(tr);
}
tab.append(koerper);
ziel.append(tab);
ziel.setAttribute('aria-busy', 'false');
const kopf = document.createElement('thead');
const kz = document.createElement('tr');
kz.append(el('th', null, 'Seite'));
for (const rolle of daten.rollen) {
const th = el('th', null, rolle.name);
/* Der Schlüssel als Titel: Im Alltag liest man „Community", im
Code steht „gast". Wer die beiden nicht zusammenbringen kann,
sucht im Quelltext an der falschen Stelle. */
th.title = rolle.schluessel;
kz.append(th);
}
kopf.append(kz);
tab.append(kopf);
const koerper = document.createElement('tbody');
for (const seite of daten.seiten) {
const tr = document.createElement('tr');
tr.append(el('td', null, seite.datei));
for (const rolle of daten.rollen) tr.append(feld(seite, rolle));
koerper.append(tr);
}
tab.append(koerper);
ziel.append(tab);
ziel.setAttribute('aria-busy', 'false');
/* ---- Ohne Anmeldung --------------------------------------------- */
const offen = $('offen');
if (offen) {
offen.textContent = '';
for (const pfad of r.ohne_anmeldung) {
offen.append(el('li', null, pfad));
}
}
})();
offen.textContent = '';
for (const pfad of daten.ohne_anmeldung) offen.append(el('li', null, pfad));
}
async function laden() {
let a;
try {
a = await hole('/workspace/api/rechte/tafel');
} catch { return; }
if (!a.ok) { melde('Die Tafel konnte nicht geladen werden.'); return; }
daten = await a.json();
zeichne();
}
(async () => { try { await laden(); } catch { /* umgeleitet */ } })();
})();
+26 -2
View File
@@ -155,7 +155,17 @@
"keine Kacheln". Die beiden auseinanderzuhalten ist kein
Feinschliff: Verwechselte man sie, haetten die fuenf bekannten
Rollen ab sofort eine leere Startseite. */
function eigeneGruppen(wer) {
/* Ist die Seite hinter diesem Ziel fuer mich offen?
Ohne Auskunft vom Server: ja -- siehe oben. */
function seiteOffen(ziel) {
const offen = ich?.seiten;
if (!Array.isArray(offen) || !offen.length) return true;
const z2 = String(ziel || "");
if (!z2) return true;
return offen.includes('/workspace/' + z2.split('?')[0]);
}
function eigeneGruppen(wer) {
const vomServer = ich && ich.bereiche;
if (Array.isArray(vomServer)) {
/* Nach Gruppen buendeln, in der Reihenfolge, in der sie kommen
@@ -186,7 +196,21 @@
kaputtgeht. */
const zusatz = Array.isArray(ich?.bereiche_zusatz) ? ich.bereiche_zusatz : [];
const gruppen = GRUPPEN
.map((g) => ({ ...g, bereiche: g.bereiche.filter((b) => b.rollen.includes(wer.rolle)) }))
/* UND GEGEN DIE RECHTETAFEL (11.09.2026).
`rollen` an der Kachel ist die Liste in dieser Datei -- sie
wird von Hand gepflegt und kennt nichts davon, dass DogFather
eine Seite umgestellt hat. `ich.seiten` kommt vom Server und
ist dieselbe Auskunft, aus der auch die Schranke ihre
Entscheidung holt.
BEIDES UND NICHT NUR EINES: Die Liste hier entscheidet, was
ueberhaupt angeboten wird; die Tafel, ob es noch offen ist.
Faellt `ich.seiten` aus (alte Fassung im Zwischenspeicher),
bleibt es beim alten Verhalten -- eine Kachel zu viel ist
aergerlich, eine fehlende sieht aus wie ein Fehler. */
.map((g) => ({ ...g, bereiche: g.bereiche.filter((b) => b.rollen.includes(wer.rolle)
&& seiteOffen(b.ziel)) }))
.filter((g) => g.bereiche.length);
for (const b of zusatz) {
const g = gruppen.find((x) => x.name === b.gruppe);
+12 -12
View File
@@ -12,19 +12,19 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -252,10 +252,10 @@
</form>
</dialog>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/aufgaben.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/aufgaben.js?v=202609112230" defer></script>
</body>
</html>
+13 -13
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/automation.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/automation.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -147,10 +147,10 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/automation.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/automation.js?v=202609112230" defer></script>
</body>
</html>
+17 -17
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -181,14 +181,14 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/checkliste.js?v=202609112217" defer></script>
<script src="assets/js/ampel.js?v=202609112217" defer></script>
<script src="assets/js/schulung.js?v=202609112217" defer></script>
<script src="assets/js/vorlagen.js?v=202609112217" defer></script>
<script src="assets/js/bereich.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/checkliste.js?v=202609112230" defer></script>
<script src="assets/js/ampel.js?v=202609112230" defer></script>
<script src="assets/js/schulung.js?v=202609112230" defer></script>
<script src="assets/js/vorlagen.js?v=202609112230" defer></script>
<script src="assets/js/bereich.js?v=202609112230" defer></script>
</body>
</html>
+14 -14
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/calls.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/calls.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -89,11 +89,11 @@
</main>
<script src="assets/js/ki.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/calls.js?v=202609112217" defer></script>
<script src="assets/js/ki.js?v=202609112230" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/calls.js?v=202609112230" defer></script>
</body>
</html>
+13 -13
View File
@@ -12,26 +12,26 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
Dialog zu sehen bekam. -->
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/chat.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/chat.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -341,10 +341,10 @@
</form>
</dialog>
<script src="assets/js/bereiche.js?v=202609112217"></script>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217"></script>
<script src="assets/js/glocke.js?v=202609112217"></script>
<script src="assets/js/chat.js?v=202609112217"></script>
<script src="assets/js/bereiche.js?v=202609112230"></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230"></script>
<script src="assets/js/glocke.js?v=202609112230"></script>
<script src="assets/js/chat.js?v=202609112230"></script>
</body>
</html>
+15 -15
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/content.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/content.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -179,12 +179,12 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/checkliste.js?v=202609112217" defer></script>
<script src="assets/js/vorlagen.js?v=202609112217" defer></script>
<script src="assets/js/content.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/checkliste.js?v=202609112230" defer></script>
<script src="assets/js/vorlagen.js?v=202609112230" defer></script>
<script src="assets/js/content.js?v=202609112230" defer></script>
</body>
</html>
+5 -5
View File
@@ -36,15 +36,15 @@
<link rel="preload" as="image" href="assets/img/buehne/crew-gate-hoch-580.webp"
type="image/webp" media="(max-width: 899px)" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<!-- Diese Wand steht NUR auf der Adresse von Team Dogi (ueberall sonst
404, siehe crewWeiche). Sie darf das Haus deshalb direkt nennen
statt ueber den Platzhalter haus.css zu gehen -- und sie MUSS es:
Eine Zugangswand, die sich erst nach dem Umbiegen faerbt, waere
die eine Seite, auf der man das Umfaerben sieht. -->
<link rel="stylesheet" href="assets/css/crew-haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/crew-haus.css?v=202609112230" />
</head>
<body class="gate gate--crew">
@@ -319,6 +319,6 @@
</main>
</div>
<script src="assets/js/gate.js?v=202609112217" defer></script>
<script src="assets/js/gate.js?v=202609112230" defer></script>
</body>
</html>
+4 -4
View File
@@ -13,25 +13,25 @@
"theme_color": "#06090f",
"icons": [
{
"src": "/assets/img/app-symbole/crew-192.png?v=202609112217",
"src": "/assets/img/app-symbole/crew-192.png?v=202609112230",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/assets/img/app-symbole/crew-512.png?v=202609112217",
"src": "/assets/img/app-symbole/crew-512.png?v=202609112230",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609112217",
"src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609112230",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609112217",
"src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609112230",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
+13 -13
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/dateien.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/dateien.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -108,10 +108,10 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/dateien.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/dateien.js?v=202609112230" defer></script>
</body>
</html>
+12 -12
View File
@@ -7,14 +7,14 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -79,9 +79,9 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/entwicklung.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/entwicklung.js?v=202609112230" defer></script>
</body>
</html>
+4 -4
View File
@@ -30,9 +30,9 @@
<link rel="preload" as="image" href="assets/img/buehne/gate-hoch-580.webp"
type="image/webp" media="(max-width: 899px)" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
</head>
<body class="gate">
@@ -401,6 +401,6 @@
</main>
</div>
<script src="assets/js/gate.js?v=202609112217" defer></script>
<script src="assets/js/gate.js?v=202609112230" defer></script>
</body>
</html>
+14 -14
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/kalender.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/kalender.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -377,11 +377,11 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/kalender.js?v=202609112217" defer></script>
<script src="assets/js/kalender-abo.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/kalender.js?v=202609112230" defer></script>
<script src="assets/js/kalender-abo.js?v=202609112230" defer></script>
</body>
</html>
+13 -13
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/leistung.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/leistung.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -382,10 +382,10 @@
</form>
</dialog>
<script src="assets/js/wahl.js?v=202609112217"></script>
<script src="assets/js/bereiche.js?v=202609112217"></script>
<script src="assets/js/kopf.js?v=202609112217"></script>
<script src="assets/js/glocke.js?v=202609112217"></script>
<script src="assets/js/leistung.js?v=202609112217"></script>
<script src="assets/js/wahl.js?v=202609112230"></script>
<script src="assets/js/bereiche.js?v=202609112230"></script>
<script src="assets/js/kopf.js?v=202609112230"></script>
<script src="assets/js/glocke.js?v=202609112230"></script>
<script src="assets/js/leistung.js?v=202609112230"></script>
</body>
</html>
+13 -13
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/personen.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/personen.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -423,10 +423,10 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/personen.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/personen.js?v=202609112230" defer></script>
</body>
</html>
+14 -14
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/profil.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/profil.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -214,11 +214,11 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/steckbrief.js?v=202609112217" defer></script>
<script src="assets/js/profil.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/steckbrief.js?v=202609112230" defer></script>
<script src="assets/js/profil.js?v=202609112230" defer></script>
</body>
</html>
+31 -14
View File
@@ -7,14 +7,14 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -43,12 +43,18 @@
<h1 class="titel">Wer sieht was</h1>
<p class="unterzeile">
Nicht aufgeschrieben, sondern ausgerechnet – aus derselben Tafel,
aus der die Schranke ihre Entscheidung holt.
aus der die Schranke ihre Entscheidung holt. Und umstellbar.
</p>
</div>
</section>
<p class="fehler" id="fehler" role="alert" aria-live="polite"></p>
<p class="fehler" id="fehler" role="alert" aria-live="polite" hidden></p>
<!-- Die Leiste sagt, ob etwas von Hand gesetzt ist, und ist der Weg
zurück. Sie steht OBEN und nicht unter der Tabelle: Wer etwas
verstellt hat und es zurückdrehen will, sucht nicht erst
zweihundert Zeilen lang. -->
<div class="rechte-leiste" id="leiste" hidden></div>
<!-- ==================================================================
WARUM DIESE SEITE ÜBERHAUPT EXISTIERT
@@ -87,6 +93,17 @@
nein – und zwar auch dann, wenn niemand je darüber nachgedacht hat.
Das ist der Unterschied zu vorher.
</p>
<p>
<strong>Ein Klick stellt um</strong>, und er wird sofort gespeichert –
kein Knopf am Ende, an dem eine halbe Änderung verlorengeht. Ein
heller Rahmen heißt: <em>dieses Feld ist von Hand gesetzt</em> und
weicht vom Grundstand im Code ab.
</p>
<p class="leise">
Drei Felder sind fest: Du kannst dir selbst die Rechte-, die Personen-
und die Startseite nicht wegnehmen – sonst käme niemand mehr an die
Rechte, an die Zugänge oder auf die Startseite.
</p>
</div>
<div class="rechte-tafel" id="tafel" aria-busy="true">
<p class="leise">wird geladen …</p>
@@ -117,9 +134,9 @@
Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen
Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen,
die seit dem 07.09. jede Seite danach absucht. -->
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/rechte.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/rechte.js?v=202609112230" defer></script>
</body>
</html>
+14 -14
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/report.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/report.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -106,11 +106,11 @@
</main>
<script src="assets/js/ki.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/report.js?v=202609112217" defer></script>
<script src="assets/js/ki.js?v=202609112230" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/report.js?v=202609112230" defer></script>
</body>
</html>
+13 -13
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/scouting.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/scouting.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -173,10 +173,10 @@
</div>
</div>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/scouting.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/scouting.js?v=202609112230" defer></script>
</body>
</html>
+12 -12
View File
@@ -12,21 +12,21 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe
den Hinweis in start.css -- dort stand es bis zum 07.09.2026. -->
<link rel="stylesheet" href="assets/css/heim.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/heim.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -674,10 +674,10 @@
</p>
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/start.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/start.js?v=202609112230" defer></script>
</body>
</html>
+14 -14
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -85,11 +85,11 @@
</main>
<script src="assets/js/ki.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/startcheck.js?v=202609112217" defer></script>
<script src="assets/js/ki.js?v=202609112230" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/startcheck.js?v=202609112230" defer></script>
</body>
</html>
+13 -13
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/profil.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/profil.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -126,10 +126,10 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/steckbrief.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/steckbrief.js?v=202609112230" defer></script>
</body>
</html>
+12 -12
View File
@@ -7,14 +7,14 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -72,9 +72,9 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/talente.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/talente.js?v=202609112230" defer></script>
</body>
</html>
+13 -13
View File
@@ -7,20 +7,20 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/team.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/team.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -110,10 +110,10 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/team.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/team.js?v=202609112230" defer></script>
</body>
</html>
+12 -12
View File
@@ -7,20 +7,20 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/team.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/team.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<!-- `data-buehne` steht hier fest und nicht im Skript: Die Kachel zu
@@ -84,9 +84,9 @@
Kaputt ist dabei nichts -- er funktioniert. Genau deshalb faellt
es niemandem auf, und deshalb prueft pruef-css-klassen die
ABHAENGIGKEIT statt der Optik. Hier hat sie angeschlagen. -->
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/teamlage.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/teamlage.js?v=202609112230" defer></script>
</body>
</html>
+12 -12
View File
@@ -7,14 +7,14 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -110,9 +110,9 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/treff-moderation.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/treff-moderation.js?v=202609112230" defer></script>
</body>
</html>
+12 -12
View File
@@ -7,18 +7,18 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein. Auf der Adresse des
Treffs biegt der Server genau diesen Dateinamen auf crew-haus.css
um, und die Seite kommt schon beim ERSTEN Abruf in Lila und
Babyblau an -- ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -190,9 +190,9 @@
Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen
Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen,
die seit dem 07.09. jede Seite danach absucht. -->
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/treff-regeln.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/treff-regeln.js?v=202609112230" defer></script>
</body>
</html>
+13 -13
View File
@@ -12,22 +12,22 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
Dialog "Creator anlegen". -->
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -149,11 +149,11 @@
</main>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/uebersicht.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/uebersicht.js?v=202609112230" defer></script>
</body>
</html>
+13 -13
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112217" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112217" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/wissen.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112217" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609112230" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609112230" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/start.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/wissen.css?v=202609112230" />
<link rel="stylesheet" href="assets/css/module.css?v=202609112230" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609112217" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609112230" />
</head>
<body class="start">
@@ -190,10 +190,10 @@
</main>
<script src="assets/js/wahl.js?v=202609112217" defer></script>
<script src="assets/js/bereiche.js?v=202609112217" defer></script>
<script src="assets/js/kopf.js?v=202609112217" defer></script>
<script src="assets/js/glocke.js?v=202609112217" defer></script>
<script src="assets/js/wissen.js?v=202609112217" defer></script>
<script src="assets/js/wahl.js?v=202609112230" defer></script>
<script src="assets/js/bereiche.js?v=202609112230" defer></script>
<script src="assets/js/kopf.js?v=202609112230" defer></script>
<script src="assets/js/glocke.js?v=202609112230" defer></script>
<script src="assets/js/wissen.js?v=202609112230" defer></script>
</body>
</html>