Aus jedem Fenster kommt man heraus, die Kachel dreht sich, der Eingang sieht aus wie das Haus

VIER DINGE, und das erste ist eine Meldung aus dem Support.

1. MISS KAM AUS "AUFGABE BEARBEITEN" NICHT HERAUS.
   „Ich konnte da wieder nicht zurück gehen, musste die App schließen
   damit ich wieder auf die Hauptseite kam."
   Gemessen (mess-dialog-ausgang.mjs), vier Größen:
     412x915 App      782 px Inhalt in 784 px  -- knapp ja
     412x780 Browser  782 px Inhalt in 742 px  -- SACKGASSE
     360x640 klein    794 px Inhalt in 602 px  -- SACKGASSE
     412x430 Tastatur 794 px Inhalt in 392 px  -- SACKGASSE
   `.dialog` hatte `overflow: hidden`, eine Scroll-Höhe gab es NUR für
   `.dialog--breit`. Alles unterhalb des Rands wurde abgeschnitten --
   samt "Abbrechen". Jetzt rollt JEDES Fenster, und Kopf wie Knopfzeile
   bleiben stehen (`position: sticky`), damit man den Ausgang SIEHT,
   ohne erst durch acht Felder zu scrollen. Alle vier Größen: ja.

2. DIE VORLAGENKACHEL DREHT SICH.
   „wenn ich drauf drücke dreht sich die kachel und dan seh ich wer es
   gemacht hat, und immer noch die option es nochmal zu verteilen falls
   neue leute ins team zustoßen."
   Vorne bleibt die Kurzfassung ("liegt bei 3 von 4"), hinten stehen
   die Namen mit ihrem Stand und zwei Knöpfe: "Nachholen – 1 fehlt"
   (oder "Nochmal an alle", wenn wirklich alle sie haben) und "Zurück".
   Nach dem Verteilen dreht sie sich von selbst; wer nur nachsehen
   will, drückt "Wer hat sie?".

3. DER EINGANG SIEHT AUS WIE DAS HAUS.
   Fase und Leuchtschiene statt flachem Kasten, die Schiene in der
   Farbe des Stands. Die drei Zahlen werden drei Felder -- und die
   "0 neu" leuchtet nicht mehr rot: Eine Warnung, die immer kommt, ist
   keine Warnung. Ab 760 px steht das Bild neben dem Text statt
   darunter; die Karte war dadurch dreimal so hoch wie nötig.

4. DER CREATOR-KATALOG IST AUF DER TEAM-SEITE WEG.
   „es gibt keine creator auf dieser seite" -- dort stand "Wähle oben
   einen Creator", eine Aufforderung zu etwas Unmöglichem. Gefragt wird
   jetzt nach den Daten (gibt es jemanden, dem ich das geben kann?),
   nicht nach der Adresse.

DAZU FERTIG GEMACHT, WAS VON GESTERN OFFEN WAR:
  * Die zwei Serien ohne Haus ("Community-Call", "Schulung-Agentur").
    Ursache war meine eigene Abschrift: Bei den Terminen frage ich die
    Teilnehmerliste, bei den Serien hatte ich sie vergessen. Auf einer
    Kopie der echten Datenbank: 0 offene Zeilen.
  * Sieben Schreibwege setzen jetzt `haus` (Aufgaben, Einträge,
    Dateien, Material, Wissen, Video-Titelbild). Dabei gefunden:
    `material` verwaltet seine Spalten SELBST -- meine Spalte stand in
    der falschen Liste und fehlte auf einer frischen Datenbank
    (78 Fehlschläge in pruef-material, jetzt 159/0).
  * unterstuetzen.html lud meldung.js gar nicht -- dort stand das
    Maschinenwort des Servers statt eines Satzes (pruef-meldungen 8/0).

DREI VERALTETE PRÜFUNGEN NACHGEZOGEN, jede STRENGER als vorher:
  * "der Modi legt eine Aufgabe an (201)" -- seit dem 22.09. ist das
    403 und gewollt. Geprüft wird jetzt auch das WORT.
  * "calls.html ist verboten" -- Filipe hat die Kachel selbst verlangt
    ("jeder der einen kalender hat"). Mit Gegenprobe ersetzt.
  * "Review" heißt seit dem 20.09. "Zur Freigabe". Der Name wird jetzt
    aus STATUS_NAME GELESEN statt abgeschrieben.

GEPRÜFT: modi-katalog 150/0 (war 144), modi-verborgen 85/0 (war 80/2),
haus-trennung 97/0, material 159/0, meldungen 8/0, abbrechen-optik 0
Fehler. Dazu grün: an-alle, vorlagen, support, css-klassen,
aufgabenbrett, aufgaben-vorlagen, unterstuetzung, formulare, loeschen,
nachfrage, kalender, chat, leerzustand.

OFFEN UND NICHT ANGEFASST: pruef-breiten meldet auf report.html ein
Berührziel von 27x18 px. Der Link (`class="zurueck"`) ist auf 30
Seiten derselbe und hat gar keinen eigenen Stil; beanstandet wird nur
diese eine Seite, weil dort hinter ihm nur "· Review" steht und die
Prüfung Fließtext-Links erst ab 12 Zeichen Umgebung ausnimmt. Eine
Klasse auf 30 Seiten ohne Prüflauf zu ändern wäre geraten.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-25 00:50:15 +02:00
co-authored by Claude Opus 5
parent 14d6f5000c
commit e5cd016b60
60 changed files with 1890 additions and 693 deletions
+93 -2
View File
@@ -925,6 +925,94 @@
background: rgba(4, 7, 12, .72);
border: 1px solid rgba(160, 190, 225, .14);
}
/* =====================================================================
DIE KACHEL DREHT SICH (25.09.2026)
Filipe: „wenn ich drauf drücke dreht sich die kachel und dan seh ich
wer es gemacht hat, und immer noch die option es nochmal zu
verteilen falls neue leute ins team zustoßen."
WIE ES GEBAUT IST: Die Karte selbst dreht sich (`rotateY`), und die
beiden Seiten verstecken ihre Rueckseite (`backface-visibility`).
Das ist der eine Weg, bei dem die Drehung EINE Bewegung ist und
nicht zwei -- ein Ein- und Ausblenden saehe aus wie ein Austausch,
nicht wie ein Umdrehen.
DIE RUECKSEITE LIEGT ABSOLUT UEBER DER VORDERSEITE, und das hat
einen Grund, der nicht nach aussen sichtbar ist: Das Brett ist ein
Raster. Zwei Seiten im Fluss wuerden die Karte doppelt so hoch
machen, und das Raster zoege alle Nachbarkarten mit. So bestimmt
weiterhin die Vorderseite die Hoehe.
`inset: 0` MEINT DEN INNENRAND, nicht den Rahmen -- absolute
Kinder richten sich am Innenrand ihres Ankers aus. Die Rueckseite
sitzt damit genau dort, wo auch der Text der Vorderseite beginnt,
ohne dass ein Wert doppelt gepflegt werden muss.
UND WER NICHT UMDREHEN WILL, MUSS NICHT: Die Kurzfassung („liegt bei
3 von 4") steht weiterhin vorne im Fuss. Die Rueckseite ist die
Vertiefung, nicht die Grundauskunft.
===================================================================== */
.vorlagenbrett__karte[data-dreh="ja"] {
position: relative;
transform-style: preserve-3d;
transition: transform .34s cubic-bezier(.2, .7, .3, 1);
}
.vorlagenbrett__karte[data-dreh="ja"][data-gedreht="ja"] { transform: rotateY(180deg); }
.vorlagenbrett__seite {
display: flex; flex-direction: column; gap: 8px;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
.vorlagenbrett__seite--vorn { flex: 1; }
.vorlagenbrett__seite--hinten {
position: absolute; inset: 0;
transform: rotateY(180deg);
/* DIE POLSTERUNG MUSS HIER NOCH EINMAL STEHEN. `inset: 0` richtet
sich am INNENRAND des Rahmens aus -- die 16/18 px der Karte liegen
damit INNERHALB dieser Flaeche und nicht davor. Ohne diese Zeile
klebte die Ueberschrift am Rahmen; im Bild war die erste Ziffer
angeschnitten. Dieselben Werte wie an `.vorlagenbrett__karte`. */
padding: 16px 18px;
/* Passt die Rueckseite einmal nicht (viele Namen, schmales Geraet),
rollt SIE -- die Karte bleibt so hoch wie vorne. Ein Abschneiden
waere dieselbe Sackgasse wie im Bearbeiten-Fenster. */
overflow: auto;
overscroll-behavior: contain;
}
/* BEWEGUNG IST EIN ANGEBOT, KEINE PFLICHT. Wer sie abbestellt hat,
bekommt dieselbe Rueckseite -- nur sofort. `transition: none`
genuegt dafuer: Der Endzustand ist derselbe. */
@media (prefers-reduced-motion: reduce) {
.vorlagenbrett__karte[data-dreh="ja"] { transition: none; }
}
.vorlagenbrett__wer-schild {
margin: 0; font-size: .78rem; font-weight: 700;
letter-spacing: .04em; text-transform: uppercase;
color: var(--text-still);
}
/* Der Weg zurueck ist ruhiger als der Weg vorwaerts: „Nochmal
verteilen" ist die Handlung, „Zurueck" nur das Zumachen. */
.vorlagenbrett__zurueck {
background: none;
border-color: rgba(160, 190, 225, .22);
color: var(--text-still);
}
.vorlagenbrett__zurueck:hover { color: var(--text); border-color: var(--akzent); }
/* Der Umdreh-Knopf steht rechts im Fuss, hinter Frist und Vermerk --
er ist die Nebenhandlung, nicht die Hauptsache. */
.vorlagenbrett__drehknopf {
background: none;
border-color: rgba(160, 190, 225, .22);
color: var(--text-still);
}
.vorlagenbrett__drehknopf:hover { color: var(--text); border-color: var(--akzent); }
.vorlagenbrett__titel {
margin: 0; font-size: 1rem; font-weight: 700; line-height: 1.3;
color: var(--text);
@@ -1045,8 +1133,11 @@
===================================================================== */
.vorlagenbrett__wer {
display: flex; flex-wrap: wrap; gap: 6px;
margin: 10px 0 0; padding-top: 9px;
border-top: 1px solid var(--rand);
/* KEIN TRENNSTRICH MEHR (25.09.2026): Der gehoerte dazu, solange die
Reihe UNTER der Karte stand und sich vom Text darueber absetzen
musste. Auf der Rueckseite ist sie der Inhalt -- ein Strich ueber
dem einzigen Inhalt trennt nichts. */
margin: 8px 0 0;
}
.vorlagenbrett__wer-p {
display: inline-flex; align-items: baseline; gap: 6px;
+92 -2
View File
@@ -714,7 +714,95 @@
border-bottom: 1px solid rgba(150, 186, 220, .12);
}
.dialog__knoepfe { display: flex; gap: 10px; align-items: center; margin-top: 16px; flex-wrap: wrap; }
/* =====================================================================
AUS EINEM FENSTER MUSS MAN HERAUSKOMMEN (25.09.2026)
=====================================================================
Miss, ueber den Support-Weg: „Ich konnte da wieder nicht zurueck
gehen, musste die App schliessen damit ich wieder auf die Hauptseite
kam." Dazu ein Bild von „Aufgabe bearbeiten" auf dem Handy.
GEMESSEN (mess-dialog-ausgang.mjs), vier Groessen:
412 x 915 installierte App 782 px Inhalt in 784 px -- knapp ja
412 x 780 Browser mit Leiste 782 px Inhalt in 742 px -- SACKGASSE
360 x 640 aelteres Geraet 794 px Inhalt in 602 px -- SACKGASSE
412 x 430 Tastatur offen 794 px Inhalt in 392 px -- SACKGASSE
`.dialog` hatte `overflow: hidden`, und eine Scroll-Hoehe gab es NUR
fuer `.dialog--breit .neu`. Bei allen anderen wurde alles unterhalb
des Rands schlicht abgeschnitten -- samt der Knopfzeile mit
„Abbrechen". Kein Rollbalken, kein Hinweis, kein Weg zurueck. Auf
dem Rechner faellt das nie auf: Dort ist der Bildschirm hoch genug.
---------------------------------------------------------------------
WARUM EINE REGEL UND NICHT EIN ZWEITES `--breit`
Ein zweiter Sonderfall waere beim dritten Fenster wieder faellig, und
dann wieder. Deshalb rollt ab jetzt JEDES Fenster, und `--breit`
unterscheidet sich nur noch in der Breite -- was sein Name sagt.
---------------------------------------------------------------------
UND WARUM KOPF UND FUSS STEHENBLEIBEN
Rollen allein haette gereicht, um herauszukommen -- aber man muesste
erst durch acht Felder scrollen, um den Ausgang zu SEHEN. Wer nicht
weiss, dass unten etwas ist, sucht ihn nicht. `position: sticky`
haelt die Ueberschrift oben und die Knoepfe unten, ohne dass ein
einziges Zeichen am HTML geaendert werden muss: Damit gilt es auch
fuer jedes Fenster, das es noch nicht gibt.
DER HINTERGRUND IST PFLICHT, nicht Zierde: Ohne ihn liefe der Text
der Felder unter den Knoepfen durch und beide waeren unlesbar. Er
ist derselbe dunkle Ton wie der Fensterboden, leicht durchscheinend
-- gedeckt, nicht als Balken erkennbar.
===================================================================== */
.dialog > form,
.dialog > .neu {
/* 82vh und nicht 100: Darueber und darunter bleibt der abgedunkelte
Hintergrund sichtbar. Ein Fenster, das den Bildschirm randlos
fuellt, sieht aus wie eine neue Seite -- und dann sucht man den
Zurueck-Knopf des Browsers statt den Ausgang im Fenster. */
max-height: min(82vh, 900px);
overflow-y: auto;
/* Wer im Fenster am Ende angekommen ist, soll nicht versehentlich die
Seite DAHINTER weiterschieben. */
overscroll-behavior: contain;
}
.dialog > form > .dialog__titel,
.dialog > .neu > .dialog__titel {
position: sticky; top: 0; z-index: 3;
margin: 0; padding: 14px 0 12px;
/* EIN AUSLAUFENDER RAND STATT EINER KANTE. Ein flaechiger Kasten
sah aus wie ein zweites Fenster ueber dem ersten -- im Bild klar
zu sehen. Der Verlauf endet durchsichtig, damit der Text darunter
hineinlaeuft, statt an einer Linie abzubrechen. Der Ton ist der
obere des Fensters selbst (#141c29), nicht ein dunklerer. */
background: linear-gradient(180deg,
rgba(20, 28, 41, .98) 0%,
rgba(20, 28, 41, .96) 62%,
rgba(20, 28, 41, 0) 100%);
backdrop-filter: blur(7px);
}
.dialog__knoepfe {
display: flex; gap: 10px; align-items: center; margin-top: 16px; flex-wrap: wrap;
position: sticky; bottom: 0; z-index: 3;
padding: 16px 0 14px;
/* Spiegelbildlich zum Kopf: oben durchsichtig, unten deckend. Der
Ton ist der untere des Fensterverlaufs (#070a11). */
background: linear-gradient(180deg,
rgba(7, 10, 17, 0) 0%,
rgba(7, 10, 17, .92) 32%,
rgba(7, 10, 17, .98) 100%);
backdrop-filter: blur(7px);
}
/* Wo kein Fenster ist, gilt nichts davon -- die Knopfzeile wird auch
ausserhalb von Dialogen benutzt und soll dort nicht kleben. */
:not(.dialog) > form > .dialog__knoepfe,
body > .dialog__knoepfe { position: static; background: none; backdrop-filter: none; }
/* ---------- Der Dialog --------------------------------------------------- */
@@ -733,7 +821,9 @@
/* Ein Fenster, das laenger ist als der Bildschirm, versteckt seine
eigenen Knoepfe. Gerollt wird INNEN, damit "Uebernehmen" immer
erreichbar bleibt. */
.dialog--breit .neu { max-height: min(86vh, 900px); overflow-y: auto; }
/* `--breit` sagt jetzt nur noch, was sein Name sagt: breiter. Die
Scroll-Hoehe stand hier als EINZIGE im ganzen Haus und war damit der
Grund, warum die anderen Fenster keine hatten (25.09.2026). */
/* ---- Die zwei Zeilen, die confirm() nicht kann ---------------------
`nachfrage.js` baut den Dialog; diese beiden Teile gibt es nur dort.
+159 -14
View File
@@ -95,13 +95,39 @@
/* Das Zahlenband: je Stand eine Zahl gross, das Wort klein darunter.
Dieselbe Bauart wie in den Personenkacheln -- man vergleicht mit dem
Auge, statt eine Zeile zu lesen. */
.s-bilanz { display: flex; gap: 18px; }
.s-bilanz__feld { display: flex; flex-direction: column; gap: 1px; }
.s-bilanz__zahl {
font-size: 1.3rem; font-weight: 700; line-height: 1.1; color: var(--text);
/* Das Zahlenband: je Stand eine Zahl gross, das Wort klein darunter --
und seit dem 25.09.2026 jede in einem eigenen kleinen Feld. Drei
nackte Zahlen nebeneinander lasen sich als EINE Angabe ("0 1 0");
mit Rand und Fase sind es drei Auskuenfte. */
.s-bilanz { display: flex; gap: 10px; flex-wrap: wrap; }
.s-bilanz__feld {
display: flex; flex-direction: column; gap: 1px;
min-width: 68px; padding: 8px 13px 7px;
border: 1px solid var(--rand);
clip-path: polygon(10px 0, 100% 0, 100% 100%, 0 100%, 0 10px);
background: linear-gradient(163deg,
rgba(255, 255, 255, .035) 0%, rgba(255, 255, 255, .012) 100%);
}
.s-bilanz__zahl {
font-size: 1.42rem; font-weight: 700; line-height: 1.1; color: var(--text);
font-variant-numeric: tabular-nums;
}
.s-bilanz__wort {
font-size: .72rem; color: var(--text-still);
letter-spacing: .04em; text-transform: uppercase;
}
/* EINE NULL IST KEINE WARNUNG. Vorher war die Zahl bei "neu" IMMER
rot -- auch die 0, und damit stand dauerhaft ein roter Punkt auf der
Seite, an dem nichts war. Eine Warnung, die immer kommt, ist keine
Warnung mehr; dieselbe Erkenntnis steht im Projektgedaechtnis vom
03.09.2026. `data-leer` setzt support.js. */
.s-bilanz__feld[data-stand="neu"]:not([data-leer="ja"]) .s-bilanz__zahl { color: var(--warn); }
.s-bilanz__feld[data-stand="neu"]:not([data-leer="ja"]) {
border-color: color-mix(in srgb, var(--warn) 38%, var(--rand));
background: linear-gradient(163deg,
color-mix(in srgb, var(--warn) 9%, transparent) 0%,
rgba(255, 255, 255, .012) 100%);
}
.s-bilanz__wort { font-size: .75rem; color: var(--text-still); }
.s-bilanz__feld[data-stand="neu"] .s-bilanz__zahl { color: var(--warn); }
.s-filter { display: flex; gap: 8px; flex-wrap: wrap; margin: 14px 0 16px; }
.s-filter__knopf {
@@ -124,16 +150,74 @@
/* ---------- Eine Meldung ------------------------------------------- */
/* =====================================================================
DIE MELDEKARTE IM HAUS-STIL (25.09.2026)
Filipe zum Bildschirmfoto des Eingangs: "lass das bitte auch geiler
aussehen bitte."
WAS VORHER DA WAR: ein flacher Kasten mit runden Ecken und einem
3 px breiten Strich links. Funktional richtig -- aber die einzige
Flaeche im Haus, die weder die Fase noch die Leuchtschiene traegt.
Neben einer Kachel oder einem Dialog sah sie aus wie aus einer
anderen Anwendung.
UEBERNOMMEN WIRD, WAS ES SCHON GIBT, nicht etwas Neues erfunden:
dieselbe `--fase` wie ueberall, dieselbe Bauart der Leuchtschiene
wie an `.dialog` und an jeder Kachel, derselbe dunkle Verlauf. Eine
zweite Formensprache waere ein zweites Haus.
DIE SCHIENE TRAEGT DIE STANDFARBE (`--s`) und nicht den Akzent: An
ihr liest man ab, ob etwas neu ist, bevor man ein Wort liest. Sie
ist gedeckt beigemischt, nicht als Leuchtbalken -- augenschonend
ist Vorgabe, nicht Geschmack.
===================================================================== */
.s-karte {
background: var(--flaeche);
border: 1px solid var(--rand);
border-radius: var(--radius-klein);
padding: 15px 16px 14px;
--s: var(--text-still);
position: relative;
padding: 16px 18px 15px 22px;
margin-bottom: 12px;
border: 1px solid var(--rand);
border-radius: 0;
/* Dieselbe Fase wie an Kachel und Dialog. Wer sie spaeter aendert,
aendert sie an EINER Stelle -- `--fase` steht im Grundblatt. */
clip-path: polygon(var(--fase, 18px) 0, 100% 0, 100% 100%, 0 100%, 0 var(--fase, 18px));
background:
linear-gradient(90deg,
color-mix(in srgb, var(--s) 13%, transparent) 0%,
color-mix(in srgb, var(--s) 4%, transparent) 16%,
transparent 44%),
linear-gradient(163deg, #131b27 0%, #0b1017 62%, #080c13 100%);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, .07),
inset 0 -1px 0 rgba(0, 0, 0, .45),
0 14px 30px -20px rgba(0, 0, 0, .9);
overflow: hidden;
}
/* Die Leuchtschiene -- dieselbe Bauart wie an `.dialog`, nur in der
Farbe des Stands. Sie ersetzt den alten flachen Strich links. */
.s-karte::before {
content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
background: linear-gradient(180deg,
color-mix(in srgb, var(--s) 52%, #fff) 0%,
var(--s) 24%,
color-mix(in srgb, var(--s) 42%, transparent) 100%);
box-shadow:
0 0 8px 0 color-mix(in srgb, var(--s) 45%, transparent),
3px 0 18px -4px color-mix(in srgb, var(--s) 34%, transparent);
z-index: 1;
}
/* Beim Daruebergehen tritt die Karte einen Schritt vor -- ohne
Bewegung, ohne Aufblitzen. Nur Rand und Schatten. */
.s-karte:hover {
border-color: color-mix(in srgb, var(--s) 32%, var(--rand));
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, .09),
inset 0 -1px 0 rgba(0, 0, 0, .45),
0 18px 38px -20px rgba(0, 0, 0, .95);
}
/* Ein schmaler Streifen links sagt den Stand, bevor man liest --
gedeckt beigemischt, nicht als Leuchtbalken. */
.s-karte { --s: var(--text-still); border-left: 3px solid color-mix(in srgb, var(--s) 55%, transparent); }
.s-karte[data-stand="neu"] { --s: var(--warn); }
.s-karte[data-stand="in_arbeit"] { --s: var(--akzent); }
/* --k-gut ist die Hausfarbe fuer "passt, erledigt, freigegeben"
@@ -175,8 +259,69 @@
.s-karte__bild img {
display: block; max-width: 100%; max-height: 300px;
width: auto; height: auto;
border-radius: var(--radius-klein);
border: 1px solid var(--rand);
border-radius: 0;
/* Kleine Fase, damit das Bild zur Karte gehoert und nicht wie ein
aufgeklebtes Rechteck darauf liegt. */
clip-path: polygon(10px 0, 100% 0, 100% 100%, 0 100%, 0 10px);
box-shadow: 0 10px 24px -16px rgba(0, 0, 0, .95);
transition: border-color var(--tempo, .18s);
}
/* Dass man es anklicken kann, sagt bisher nur der Mauszeiger-Text.
Ein heller werdender Rand sagt es beim Daruebergehen -- ohne
Bewegung, ohne Vergroessern: `prefers-reduced-motion` braucht hier
nichts abzuschalten, weil sich nichts bewegt. */
.s-karte__bild a:hover img,
.s-karte__bild a:focus-visible img {
border-color: color-mix(in srgb, var(--akzent) 55%, var(--rand));
}
.s-karte__bild a:focus-visible { outline: 2px solid var(--akzent); outline-offset: 3px; }
/* =====================================================================
DAS BILD NEBEN DEN TEXT, NICHT DARUNTER (25.09.2026)
Im Bildschirmfoto stand ein hochkantes Handybild unter zwei Zeilen
Text -- und rechts daneben eine handbreite leere Flaeche ueber die
ganze Kartenhoehe. Eine Meldung mit Bild war damit dreimal so hoch
wie noetig, und im Eingang sah man weniger davon auf einmal.
ERST AB 760 px, und das ist der Punkt: Auf dem Handy gibt es keine
zweite Spalte, dort bleibt alles untereinander. Gefragt wird nach
dem PLATZ, nicht nach dem Geraet.
`auto` UND NICHT EINE FESTE BREITE fuer die Bildspalte: Gibt es kein
Bild, greift die ganze Regel gar nicht erst (`:has`). Eine feste
Breite waere eine Luecke bei jeder Meldung ohne Bild -- und die sind
die Mehrzahl.
===================================================================== */
@media (min-width: 760px) {
.s-karte:has(.s-karte__bild) {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
grid-template-areas:
"kopf kopf"
"text bild"
"wo bild"
"antwort bild"
"tun bild";
column-gap: 20px;
align-items: start;
}
.s-karte:has(.s-karte__bild) > .s-karte__kopf { grid-area: kopf; }
.s-karte:has(.s-karte__bild) > .s-karte__text { grid-area: text; }
.s-karte:has(.s-karte__bild) > .s-karte__wo { grid-area: wo; }
.s-karte:has(.s-karte__bild) > .s-antwort { grid-area: antwort; }
.s-karte:has(.s-karte__bild) > .s-karte__tun { grid-area: tun; }
.s-karte:has(.s-karte__bild) > .s-karte__bild {
grid-area: bild; margin-top: 0; align-self: start;
}
/* 180 UND NICHT 260 (gemessen 25.09.2026). Bei 260 war die Karte
330 px hoch, obwohl der Text zwei Zeilen hat -- die Bildspalte gab
die Hoehe vor, und links stand eine leere Flaeche. Das Bild ist
anklickbar ("In voller Groesse ansehen"), eine Vorschau reicht
also. Ein Handy-Bildschirmfoto ist bei 180 px noch als solches zu
erkennen; darum geht es an dieser Stelle. */
.s-karte:has(.s-karte__bild) > .s-karte__bild img { max-height: 180px; }
}
.s-karte__bild a:focus-visible img { outline: 2px solid var(--akzent); outline-offset: 2px; }
+40 -3
View File
@@ -1755,8 +1755,10 @@
/* ERST DIE AUFGABEN, DANN DER KATALOG. Er markiert daran, was
schon uebernommen wurde -- in umgekehrter Reihenfolge waere die
Markierung beim ersten Zeichnen leer. */
vorlagenbrettAnschliessen();
await window.Vorlagenbrett?.zeigen();
/* NUR ZEICHNEN, WENN AUCH EINGERICHTET WURDE. `zeigen()` ohne
`einrichten()` liest aus einer Schnittstelle, die es nicht gibt
-- ein Fehler in der Konsole und ein halb gezeichneter Kasten. */
if (vorlagenbrettAnschliessen()) await window.Vorlagenbrett?.zeigen();
})();
/* =====================================================================
@@ -1779,7 +1781,41 @@
ZWEIG 'creator': Der Team-Katalog wird hier NICHT gezeigt, auch
wenn der Server ihn mitschickt. Er hat seine Seite. */
function vorlagenbrettAnschliessen() {
if (!window.Vorlagenbrett) return;
if (!window.Vorlagenbrett) return false;
/* ==== OHNE CREATOR KEIN CREATOR-KATALOG (25.09.2026) ============
Filipe, zu einem Bildschirmfoto dieser Seite: „es gibt keine
creator auf dieser seite. die app ist nur fuer modis und
community also steht das falsch da."
Er hatte den Satz „Waehle oben einen Creator, dann lassen sich
diese Aufgaben uebernehmen" vor sich -- unter einem Katalog mit
vier Bereichen und vier Stufen, der fuer Creator gemacht ist.
Auf der Team-Adresse gibt es keine. Der Satz war also nicht nur
falsch, sondern eine Aufforderung zu etwas Unmoeglichem: Man
sucht oben nach einer Auswahl, die dort nie erscheinen wird.
GEFRAGT WIRD NACH DEN DATEN, NICHT NACH DER ADRESSE. „Gibt es
jemanden, dem ich das geben kann?" ist dieselbe Frage, egal auf
welcher Seite man steht -- und sie beantwortet sich aus der
Personenliste, die die Seite ohnehin schon geholt hat. Eine
Abfrage auf das Haus waere eine zweite Regel neben der
Personenliste, und die beiden koennten auseinanderlaufen.
Ein Creator selbst behaelt den Katalog immer: Er nimmt Aufgaben
fuer sich, dafuer braucht er niemanden zum Auswaehlen. */
const gibtCreator = ich?.rolle === 'creator'
|| (personenListe || []).some((p) => p.rolle === 'creator');
if (!gibtCreator) {
/* Der ganze Kasten geht weg, nicht nur der Satz. Eine
Ueberschrift ueber einem Katalog, den niemand uebernehmen
kann, ist dieselbe Sackgasse in leiser. */
const kasten = document.getElementById('vorlagenbrett');
if (kasten) kasten.hidden = true;
return false;
}
window.Vorlagenbrett.einrichten({
zweig: 'creator',
hole,
@@ -1811,6 +1847,7 @@
zielWaehlen: () => {},
nachAenderung: () => laden(),
});
return true;
}
})();
+7 -1
View File
@@ -325,7 +325,13 @@
]) {
const kasten = el('div', 's-bilanz__feld');
kasten.dataset.stand = stand;
kasten.append(el('span', 's-bilanz__zahl', String(zahl(stand))));
/* OB DAS FELD LEER IST, steht als Merkmal daran -- das Stilblatt
faerbt die "neu"-Null damit nicht mehr rot. Gerechnet wird es
hier und nicht im CSS: Dort gibt es keine Zahlen, nur Text,
und ":empty" traefe eine "0" nicht. */
const n = zahl(stand);
if (!n) kasten.dataset.leer = 'ja';
kasten.append(el('span', 's-bilanz__zahl', String(n)));
kasten.append(el('span', 's-bilanz__wort', wort));
ziel.append(kasten);
}
+21 -2
View File
@@ -29,6 +29,19 @@
===================================================================== */
(() => {
const $ = (id) => document.getElementById(id);
/* DER SATZ ZU EINER SERVER-KENNUNG -- mit Rueckfall (25.09.2026).
Ueber `window` und nicht als blanker Aufruf: Wuerde diese Datei
einmal ohne meldung.js geladen, waere `sagWas(...)` ein
ReferenceError -- und der traete ausgerechnet dann auf, wenn
ohnehin schon etwas schiefgegangen ist. Eine Fehlerbehandlung,
die selbst abstuerzt, verschluckt den eigentlichen Fehler.
Dieselbe Bauart steht in vorlagenbrett.js und ist dort aus
demselben Grund entstanden. */
const sagWas = (fehler, ersatz) => (typeof window.sagWas === 'function'
? window.sagWas(fehler, ersatz)
: (ersatz || 'Das hat nicht geklappt.'));
const el = (t, k, text) => {
const n = document.createElement(t);
if (k) n.className = k;
@@ -301,7 +314,13 @@
});
if (!a.ok) {
const r = await a.json().catch(() => ({}));
melde(r.fehler || 'Das ließ sich nicht speichern.');
/* SATZ STATT MASCHINENWORT (25.09.2026). Hier stand
`r.fehler || …` -- also wurde die Kennung des Servers
ungefiltert angezeigt. Wer "nicht_gefunden" liest, weiss
weder, was los ist, noch was er tun soll. `sagWas` macht
daraus den Satz aus meldung.js und faellt auf den Text
dahinter zurueck, wenn es die Kennung dort nicht gibt. */
melde(sagWas(r.fehler, 'Das ließ sich nicht speichern.'));
return;
}
melde('Gespeichert.', true);
@@ -339,7 +358,7 @@
});
if (!a.ok) {
const r = await a.json().catch(() => ({}));
melde(r.fehler || 'Das ließ sich nicht anlegen.');
melde(sagWas(r.fehler, 'Das ließ sich nicht anlegen.'));
return;
}
await laden();
+114 -5
View File
@@ -61,6 +61,23 @@
? window.sagWas(fehler, ersatz)
: (ersatz || 'Das hat nicht geklappt.'));
/* ==== WELCHE KARTE SICH NACH DEM VERTEILEN ZEIGEN SOLL ==========
Filipe: „wenn ich drauf drücke dreht sich die kachel und dan seh
ich wer es gemacht hat, und immer noch die option es nochmal zu
verteilen falls neue leute ins team zustoßen."
WARUM EIN MERKZETTEL UND KEIN DIREKTES UMSCHALTEN: Nach dem
Verteilen wird das ganze Brett NEU gezeichnet (die Zahlen an den
Namen stimmen sonst nicht mehr). Eine Karte, die man vorher
umdreht, ist danach weg -- samt ihrer Drehung. Der Schluessel
ueberlebt das Neuzeichnen; die Karte findet sich selbst wieder.
EINER UND NICHT EINE LISTE: Man verteilt eine Karte und sieht sie
an. Zwei gleichzeitig gedrehte Karten waeren ein Zustand, den
niemand hergestellt hat. */
let zuletztVerteilt = null;
/* DIE PFLICHTANGABEN als Liste und nicht als Reihe von if-Abfragen:
Eine Liste laesst sich nicht halb vergessen. */
const NOETIG = ['zweig', 'hole', 'melde', 'ich', 'aufgaben', 'ziel',
@@ -959,10 +976,16 @@
ist -- nicht, dass er die Sperre vergessen soll. Bei "alle"
fuellt er sonst nur die Luecken; steht hier "Nochmal", haben
wirklich alle sie, und dann ist der Druck eine Entscheidung. */
k.addEventListener('click', () => uebernehmen(k, {
art: 'katalog', nr: v.nr, gruppe: kGruppe,
person_id: katalogZiel(), nochmal: wirklichNochmal,
}));
k.addEventListener('click', () => {
/* VOR dem Verteilen merken, nicht danach: `uebernehmen`
zeichnet am Ende neu, und dann gibt es diese Karte als
Objekt nicht mehr. */
if (anAlle) zuletztVerteilt = v.schluessel;
uebernehmen(k, {
art: 'katalog', nr: v.nr, gruppe: kGruppe,
person_id: katalogZiel(), nochmal: wirklichNochmal,
});
});
fuss.append(k);
} else if (meine) {
/* SCHON BEWORBEN. Der Satz sagt, WER antwortet -- sonst wartet
@@ -1025,7 +1048,93 @@
: ((U.statusName[a2.status] || a2.status) + (spaet ? ' · überfällig' : ''))));
reihe.append(marke);
}
karte.append(reihe);
/* ==== DIE KARTE BEKOMMT ZWEI SEITEN (25.09.2026) =============
Bis heute stand die Namensreihe UNTER der Karte -- immer
sichtbar, bei zwoelf Karten also zwoelfmal. Das war Filipes
erste Entscheidung und richtig, solange es nur um "wie
weit" ging. Jetzt will er das Einzelne auf Druck sehen:
„wenn ich drauf drücke dreht sich die kachel".
VORNE BLEIBT DIE KURZFASSUNG. „liegt bei 3 von 4" steht
weiterhin im Fuss -- man soll den Stand ueberfliegen
koennen, ohne zwoelf Karten umzudrehen. Die Namen sind die
Vertiefung, nicht die Grundauskunft.
UMGEBAUT STATT NEU GEBAUT: Alles, was schon an der Karte
haengt, wandert geschlossen auf die Vorderseite. So kann
die Rueckseite nichts vergessen, was vorne steht -- und ein
kuenftiger Zusatz landet von selbst richtig. */
const vorn = el('div', 'vorlagenbrett__seite vorlagenbrett__seite--vorn');
while (karte.firstChild) vorn.append(karte.firstChild);
const hinten = el('div', 'vorlagenbrett__seite vorlagenbrett__seite--hinten');
hinten.append(el('p', 'vorlagenbrett__wer-schild', alleHabenSie
? `Alle ${zielbar.length} haben sie`
: `${werHat.size} von ${zielbar.length} haben sie`));
hinten.append(reihe);
const fussH = el('div', 'vorlagenbrett__fuss');
/* ==== „NOCHMAL VERTEILEN" GEHOERT AUF DIE RUECKSEITE =========
Filipe ausdruecklich: „und immer noch die option es nochmal
zu verteilen falls neue leute ins team zustoßen."
DER KNOPF SAGT, WAS ER TUT, und das ist bei den zwei Faellen
NICHT dasselbe: Fehlt noch jemand, holt er die Fehlenden
nach und laesst die anderen in Ruhe. Haben alle sie, ist der
naechste Druck eine bewusste Wiederholung -- dann heisst er
so und sagt es dem Server auch (`nochmal`). */
/* DIE ZAHL IN KLAMMERN UND NICHT IM SATZ (25.09.2026).
Der erste Entwurf setzte sie hinein: „Die 1 Fehlenden
nachholen" -- bei genau einer Person falsches Deutsch, und
„Die 1 Fehlende" waere je nach Mensch das falsche
Geschlecht. So stimmt der Satz bei jeder Zahl, ohne
Sonderfall und ohne zu raten. */
const fehlt = zielbar.length - werHat.size;
const nachholen = el('button', 'knopf knopf--klein', alleHabenSie
? 'Nochmal an alle'
: `Nachholen – ${fehlt === 1 ? '1 fehlt' : fehlt + ' fehlen'}`);
nachholen.type = 'button';
nachholen.title = alleHabenSie
? 'Alle bekommen sie noch einmal – auch die, die sie schon haben.'
: 'Nur die, die sie noch nicht haben. Niemand bekommt sie doppelt.';
nachholen.addEventListener('click', () => {
zuletztVerteilt = v.schluessel;
uebernehmen(nachholen, {
art: 'katalog', nr: v.nr, gruppe: kGruppe,
person_id: katalogZiel(), nochmal: alleHabenSie,
});
});
fussH.append(nachholen);
const zurueck = el('button', 'knopf knopf--klein vorlagenbrett__zurueck', 'Zurück');
zurueck.type = 'button';
zurueck.addEventListener('click', () => { karte.dataset.gedreht = 'nein'; });
fussH.append(zurueck);
hinten.append(fussH);
karte.append(vorn);
karte.append(hinten);
karte.dataset.dreh = 'ja';
/* WER SIE SCHON HAT, KANN MAN AUCH OHNE VERTEILEN NACHSEHEN --
sonst waere die Rueckseite nur als Nebenwirkung einer
Handlung erreichbar, und wer nur nachsehen will, muesste
erst etwas verteilen. */
const umdrehen = el('button', 'knopf knopf--klein vorlagenbrett__drehknopf',
'Wer hat sie?');
umdrehen.type = 'button';
umdrehen.addEventListener('click', () => { karte.dataset.gedreht = 'ja'; });
vorn.querySelector('.vorlagenbrett__fuss')?.append(umdrehen);
/* Und wenn gerade DIESE Karte verteilt wurde: gedreht zeigen. */
if (zuletztVerteilt === v.schluessel) {
karte.dataset.gedreht = 'ja';
zuletztVerteilt = null;
} else {
karte.dataset.gedreht = 'nein';
}
}
/* ==== DIE BEWERBUNGEN ANDERER -- nur für die, die entscheiden ==