Teilen gibt einen fertigen Discord-Beitrag -- und die zwei Seiten bekommen ein Gesicht

Filipe: "beim teilen wird auch nur der link geteilt, und ich will dass
wenn ich es teile im discord der agentur, dass ein banner und ein
kompletter fertiger text fuer die creator entsteht und nicht einfach
der link kopiert wird damit die leute drauf gehen. das ist kake."
Und: "perfektionier die zwei events kategorien. ich will es noch viel
krasser und spezieller."

EIN LINK IN EINEM DISCORD-KANAL IST EINE AUFFORDERUNG. Wer ihn nicht
anklickt -- und das sind die meisten -- weiss nicht, dass es die
Kampagne gibt. Was in Discord steht, muss die Kampagne SEIN. Der
Teilen-Knopf legt deshalb einen fertigen Beitrag in die Zwischenablage:
Titel als Ueberschrift, Zeitraum, Einleitung, die Aufgaben als Liste,
die Geschenke mit Coinpreis, "Gut zu wissen" aus Rangliste, Zeitplan
und Teilnahme. Er steht zusaetzlich sichtbar unter der Kachel, mit
Zeichenzaehler und zweitem Kopierknopf -- was man nicht gesehen hat,
fuegt man blind ein.

ZWEI DINGE SIND DABEI NICHT BELIEBIG:
  - Der Link steht GANZ AM ENDE. Discord setzt die Bannervorschau
    UNTER die Nachricht; steht er oben, schiebt das Banner den Text
    aus dem Blick.
  - Der Link steht NACKT da. Spitze Klammern unterdruecken die
    Vorschau -- und damit genau das Banner, um das es geht.

2000 ZEICHEN, UND NICHT EINS MEHR. Gekuerzt wird auf dem Server, in
fester Reihenfolge und an Abschnittsgrenzen: erst "Gut zu wissen",
dann die Einleitung auf zwei Saetze, dann die Geschenke. Titel,
Zeitraum, Aufgaben und Link bleiben IMMER. Gemessen gegen sieben
Laengen und einen 1500-Zeichen-Titel als Haertefall.

Gebaut wird der Beitrag aus `teilenDaten()` -- derselben Funktion, aus
der auch die offene Seite entsteht. Zwei Aufbauten fuer dasselbe Event
wuerden irgendwann Verschiedenes sagen, und niemand koennte entscheiden,
welcher recht hat.

DIE ZWEI SEITEN: ein gezeichnetes Wappen in der Farbe der Spalte, ein
Kopf mit Lichtkante und Verlauf, der beim Scrollen oben stehen bleibt,
und eine Lagezeile ("2 laufen - 1 kommt - 3 vorbei"). Gezaehlt mit
derselben Regel, die die Gruppen darunter bildet; was null ist,
erscheint nicht. Augenschonend bleibt es ueber Form und Tiefe, nicht
ueber Leuchtkraft.

NEBENBEFUND, BEHOBEN: Der Einlesekasten war durch Seitenwahl und
Bildweg auf 521 px gewachsen -- auf 412 px mehr als ein halber
Telefonbildschirm, bevor man die erste Kachel sieht. Er klappt jetzt zu
(82 px) und merkt sich die Wahl; ein Druck, und er bleibt offen.

BERICHTIGT, WEIL GEMESSEN:
  - Der Bindestrich wurde entwertet: "4\-woechige", "Ahorn\-Welt". In
    Discord ist "-" nur am Zeilenanfang eine Aufzaehlung.
  - Das Klappschild hatte nur eine Handy-Regel und sonst kein
    Aussehen -- auf 1400 px ein nackter Knopf mit 27 px statt 48.
  - bild-kampagne mass elementFromPoint ausserhalb des
    Sichtfensters und meldete "etwas liegt auf dem Knopf", wo nichts
    lag; und r(null) liess den ganzen Lauf abstuerzen, statt die
    Zusicherung rot zu machen.
  - EINE GEGENPROBE WAR BLIND: DISCORD_MAX von 2000 auf 99999 zu
    setzen liess die Pruefung gruen -- der Testbeitrag ist 1230
    Zeichen lang, die Grenze wurde also nie geprueft. Jetzt mit einem
    Beitrag, der ohne Kuerzung sicher zu lang waere.

GEPRUEFT: 190 (Route und Teilen, +8), 183 (Kachel, +13), 69 Messungen
(Bild, +2), 67 (Agentur), 43 (Bildleser), struktur, css-klassen,
zeichen, deutsche-texte, tippziele. Sechs Gegenproben: Link nur noch
Link, Link oben statt unten, Grenze weg, Lagezeile weg, Kopf klebt
nicht, Kasten wieder offen -- alle sechs werden bemerkt, alle Dateien
byte-gleich wiederhergestellt.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-10-08 11:20:51 +02:00
co-authored by Claude Opus 5
parent 807d7d4a1c
commit fdadd5d573
54 changed files with 1752 additions and 729 deletions
+257
View File
@@ -4295,3 +4295,260 @@ body[data-ton] .kopf-zeile::before {
@media (prefers-reduced-motion: reduce) {
.kampagneholen__waehlen { transition: none; }
}
/* =====================================================================
DAS SCHILD DES EINLESEKASTENS (08.10.2026)
Zuerst stand hier NUR eine Handy-Regel (`min-height: 44px` unter
560 px) und sonst nichts -- ein Knopf ohne Grundaussehen. Gemessen
kam auf 1400 px eine Höhe von 27 px heraus: ein nackter `button`,
mit Systemschrift, ohne Fläche, ohne Tippziel. pruef-eventkarte hat
es gefunden, bevor Filipe es gesehen hat.
Die Lehre ist dieselbe wie bei den Direktkind-Selektoren: Eine
Regel, die nur in einem `@media`-Block steht, gilt auch nur dort.
Was überall gelten soll, steht draußen.
ES SIEHT AUS WIE EIN SCHILD, NICHT WIE EIN KNOPF. Der Kasten
darunter ist die Handlung; dies hier ist seine Beschriftung. Ein
zweiter kräftiger Knopf neben „Kampagne holen“ wäre eine zweite
Haupthandlung, und dann ist keine mehr die Haupthandlung.
===================================================================== */
.kampagneholen__schild {
display: flex; align-items: center; flex-wrap: wrap;
gap: 2px 12px;
width: 100%; min-height: 48px;
margin: 0; padding: 11px 14px;
font: inherit;
color: var(--text);
text-align: left; cursor: pointer;
background: linear-gradient(180deg,
color-mix(in srgb, var(--akzent, #3fbdf5) 8%, transparent), transparent 70%);
border: 1px solid color-mix(in srgb, var(--akzent, #3fbdf5) 22%, var(--rand));
border-radius: var(--radius-klein);
}
.kampagneholen__schild:hover,
.kampagneholen__schild:focus-visible {
border-color: color-mix(in srgb, var(--akzent, #3fbdf5) 50%, transparent);
}
.kampagneholen__schildwort {
flex: 0 0 auto;
font-size: .95rem; font-weight: 700;
}
/* Was dahinter steckt -- damit man nicht erst aufklappen muss, um es
zu erfahren. Ein zugeklappter Kasten ohne Auskunft sieht aus, als
sei er leer. */
.kampagneholen__schildwie {
flex: 1 1 auto; min-width: 0;
font-size: .8rem; color: var(--text-still);
}
.kampagneholen__schild .klapp-pfeil { flex: none; }
/* Aufgeklappt braucht das Formular Luft zum Schild. */
.kampagneholen__schild[data-zu="nein"] { margin-bottom: 12px; }
/* =====================================================================
DER FERTIGE DISCORD-BEITRAG (08.10.2026)
Filipe: „beim teilen wird auch nur der link geteilt ... ich will dass
ein banner und ein kompletter fertiger text für die creator entsteht
... das ist kake.“
Der Beitrag liegt nach dem Druck in der Zwischenablage. Er steht
trotzdem sichtbar da: Was man nicht gesehen hat, fügt man blind ein.
===================================================================== */
.ev-beitrag {
margin: 14px 0 0;
padding: 13px 15px 15px;
border: 1px solid color-mix(in srgb, var(--akzent, #3fbdf5) 28%, var(--rand));
border-radius: var(--radius-klein);
background:
linear-gradient(180deg,
color-mix(in srgb, var(--akzent, #3fbdf5) 7%, transparent),
transparent 60%),
rgba(255, 255, 255, .022);
}
.ev-beitrag__kopf {
display: flex; align-items: baseline; flex-wrap: wrap;
gap: 4px 10px; margin-bottom: 9px;
}
.ev-beitrag__schild {
margin: 0;
font-size: .75rem; font-weight: 700;
letter-spacing: .09em; text-transform: uppercase;
color: var(--text-leise);
}
.ev-beitrag__lage { font-size: .78rem; color: var(--text-still); }
.ev-beitrag__lage::before { content: "·"; margin-right: 7px; opacity: .7; }
/* DAS FELD IST LESBAR, NICHT NUR VORHANDEN. Eine feste Schrifthöhe
und `pre-wrap`, damit der Beitrag genau so aussieht wie später in
Discord -- Zeilenumbrüche inbegriffen. */
.ev-beitrag__text {
display: block; width: 100%;
min-height: 140px; resize: vertical;
padding: 10px 12px;
border: 1px solid var(--rand);
border-radius: var(--radius-klein);
background: rgba(0, 0, 0, .22);
color: var(--text);
font: .83rem/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
white-space: pre-wrap; overflow-wrap: anywhere;
}
.ev-beitrag__text:focus-visible {
outline: 2px solid var(--akzent, #3fbdf5); outline-offset: 1px;
}
.ev-beitrag__reihe {
display: flex; flex-wrap: wrap; align-items: center;
gap: 8px 12px; margin-top: 10px;
}
.ev-beitrag__zahl {
font-size: .76rem; color: var(--text-still);
font-variant-numeric: tabular-nums;
}
.ev-beitrag__hinweis {
margin: 9px 0 0;
font-size: .78rem; line-height: 1.5; color: var(--text-still);
}
.ev-beitrag__hinweis a { color: var(--akzent, #3fbdf5); }
/* =====================================================================
DIE ZWEI SEITEN -- KRASSER UND SPEZIELLER (08.10.2026)
Filipe: „perfektionier die zwei events kategorien. ich will es noch
viel krasser und spezieller.“
AUGENSCHONEND BLEIBT ES TROTZDEM (CLAUDE.md, 12.08.2026). Was den
Kopf besonders macht, sind Form und Tiefe, nicht Leuchtkraft: ein
gezeichnetes Wappen, ein Lichtstreifen entlang der Oberkante, ein
gedeckter Verlauf. Keine zweite Signalfarbe, kein Blinken.
===================================================================== */
/* DAS WAPPEN. Ein Ring mit einem vollen Kern -- derselbe Gedanke wie
beim Farbpunkt des Herkunftsschildes, nur größer und mit einem
zweiten Ring außen. Gezeichnet, nicht gebildet: Es färbt sich mit
`--kv-ton` mit und bleibt bei starkem Kontrast sichtbar. */
.kv-seite__wappen {
flex: none;
position: relative;
width: 30px; height: 30px;
margin-top: 1px;
border-radius: 50%;
border: 2px solid color-mix(in srgb, var(--kv-ton) 55%, transparent);
background:
radial-gradient(circle at 50% 50%,
color-mix(in srgb, var(--kv-ton) 85%, transparent) 0 22%,
transparent 23%),
radial-gradient(circle at 50% 50%,
color-mix(in srgb, var(--kv-ton) 22%, transparent) 0 70%,
transparent 71%);
}
/* Der äußere Hauch — er macht aus dem Ring ein Abzeichen. */
.kv-seite__wappen::after {
content: "";
position: absolute; inset: -5px;
border-radius: 50%;
background: radial-gradient(circle,
color-mix(in srgb, var(--kv-ton) 16%, transparent) 0, transparent 68%);
pointer-events: none;
}
/* DER KOPF BEKOMMT EINE EIGENE FLÄCHE und bleibt beim Scrollen oben
stehen. Bei fünf Gruppen und zwölf Kacheln je Spalte ist „auf
welcher Seite bin ich gerade“ sonst eine echte Frage.
`top: 0` reicht: Die Kopfleiste der Seite scrollt mit weg, es gibt
darüber nichts, was im Weg stünde. */
.kv-seite__kopf {
position: sticky; top: 0; z-index: 3;
align-items: center;
gap: 12px;
margin: 0 0 2px;
padding: 11px 13px 11px 11px;
border: 1px solid color-mix(in srgb, var(--kv-ton) 24%, var(--rand));
border-bottom: 2px solid color-mix(in srgb, var(--kv-ton) 55%, transparent);
border-radius: var(--radius-klein) var(--radius-klein) 0 0;
background:
linear-gradient(180deg,
color-mix(in srgb, var(--kv-ton) 13%, transparent),
color-mix(in srgb, var(--kv-ton) 3%, transparent) 70%),
color-mix(in srgb, var(--tinte) 92%, #000000 8%);
/* Der Lichtstreifen entlang der Oberkante -- eine Kante, kein
Leuchten. */
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--kv-ton) 40%, transparent);
backdrop-filter: blur(6px);
}
/* DIE LAGE DER SEITE: „2 laufen · 1 kommt · 3 vorbei“.
Eine Zahl sagt, wie viel da ist; das hier sagt, was los ist. */
.kv-seite__lage {
display: flex; flex-wrap: wrap; align-items: center;
gap: 5px 7px;
margin: 0 0 10px;
padding: 0 2px;
}
.kv-seite__lageteil {
display: inline-flex; align-items: center; gap: 6px;
padding: 2px 9px 2px 8px;
border-radius: 999px;
border: 1px solid color-mix(in srgb, var(--rand) 75%, transparent);
font-size: .74rem; font-weight: 600;
font-variant-numeric: tabular-nums;
color: var(--text-still);
}
.kv-seite__lageteil::before {
content: "";
width: 7px; height: 7px; border-radius: 50%;
background: currentColor;
}
/* DIESELBEN DREI TÖNE WIE AN DEN KACHELN (laeuft/kommt/vorbei). Ein
vierter Satz Farben für dieselbe Aussage wäre eine zweite Sprache
auf einer Seite. */
.kv-seite__lageteil[data-stand="laeuft"] {
color: color-mix(in srgb, #cace02 70%, var(--text));
border-color: color-mix(in srgb, #cace02 35%, transparent);
background: color-mix(in srgb, #cace02 10%, transparent);
}
.kv-seite__lageteil[data-stand="kommt"] {
color: color-mix(in srgb, #5b8fd4 78%, var(--text));
border-color: color-mix(in srgb, #5b8fd4 35%, transparent);
}
.kv-seite__lageteil[data-stand="vorbei"] { opacity: .72; }
@media (max-width: 560px) {
/* Auf dem Handy stehen die Spalten untereinander — ein klebender
Kopf je Spalte würde sich beim Scrollen gegenseitig überlagern.
Also nur am Rechner. */
.kv-seite__kopf { position: static; }
.kv-seite__wappen { width: 26px; height: 26px; }
/* ---- DER EINLESEKASTEN BLEIBT HANDLICH (08.10.2026) -------
Gemessen auf 412 px: Mit Seitenwahl und Bildweg war er auf
539 px gewachsen — mehr als ein halber Telefonbildschirm,
bevor man die erste Kachel sieht. Er ist ein Werkzeug, nicht
der Inhalt der Seite.
GEKÜRZT WIRD, WO NICHTS VERLOREN GEHT: Das Textfeld bleibt
mehrzeilig, nur eben zwei statt drei Zeilen (einen Link sieht
man auch so ganz), und die Abstände werden enger. Es
verschwindet nichts — man muss nichts aufklappen, um etwas
zu finden. */
.kampagneholen__form > textarea { min-height: 62px; }
.kampagneholen__schild { min-height: 44px; }
.kampagneholen__wahl { margin-top: 9px; gap: 6px 10px; }
.kampagneholen__wahl > .feld-schild { margin-bottom: 5px; }
.kampagneholen__bild { margin-top: 9px; gap: 6px 10px; }
.kampagneholen__zeile { margin-top: 9px; }
}
@media (prefers-reduced-motion: reduce) {
.kv-seite__kopf { backdrop-filter: none; }
}
@media (forced-colors: active) {
.kv-seite__wappen { border: 2px solid CanvasText; background: none; }
.kv-seite__kopf { border: 1px solid CanvasText; background: Canvas; }
.kv-seite__lageteil { border: 1px solid CanvasText; }
.ev-beitrag { border: 1px solid CanvasText; }
.ev-beitrag__text { border: 1px solid CanvasText; }
}
+165 -3
View File
@@ -1197,6 +1197,86 @@
return block;
}
/* =====================================================================
DER FERTIGE DISCORD-BEITRAG, SICHTBAR (08.10.2026)
Er liegt nach dem Druck in der Zwischenablage. Das allein
reicht nicht: Wer ihn nicht gesehen hat, fügt ihn blind ein.
Deshalb steht er zusätzlich unter der Kachel -- mit einem
zweiten Kopierknopf, falls die Zwischenablage nicht ging
(ältere Browser, kein https) oder er inzwischen etwas anderes
kopiert hat.
NUR EINER JE KACHEL. Beim zweiten Druck wird der alte
ersetzt, nicht ein zweiter angehängt -- sonst stapeln sie
sich.
`readOnly` STATT `disabled`: Ein abgeschaltetes Textfeld
lässt sich nicht markieren, und genau das will man hier
notfalls tun. */
function beitragZeigen(karteEl, text, kopiert, weg) {
if (!karteEl) return;
karteEl.querySelector('.ev-beitrag')?.remove();
const kasten = el('section', 'ev-beitrag');
const kopf = el('div', 'ev-beitrag__kopf');
kopf.append(el('h4', 'ev-beitrag__schild', 'Beitrag für Discord'));
kopf.append(el('span', 'ev-beitrag__lage',
kopiert ? 'liegt in der Zwischenablage' : 'bitte von Hand kopieren'));
kasten.append(kopf);
const feld = el('textarea', 'ev-beitrag__text');
feld.value = text;
feld.readOnly = true;
feld.rows = 10;
feld.spellcheck = false;
kasten.append(feld);
const reihe = el('div', 'ev-beitrag__reihe');
const nochmal = el('button', 'knopf knopf--klein');
nochmal.type = 'button';
nochmal.textContent = 'Beitrag kopieren';
nochmal.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(text);
nochmal.textContent = 'Kopiert ✓';
setTimeout(() => { nochmal.textContent = 'Beitrag kopieren'; }, 2000);
} catch (f) {
void f;
/* Geht die Zwischenablage nicht, wird markiert -- dann
reicht Strg+C. Besser als eine Meldung, die sagt, dass
es nicht geht. */
feld.select();
}
});
reihe.append(nochmal);
const zu = el('button', 'knopf knopf--klein');
zu.type = 'button';
zu.textContent = 'schließen';
zu.addEventListener('click', () => kasten.remove());
reihe.append(zu);
/* DIE ZEICHENZAHL STEHT DABEI. Discord nimmt 2000 je
Nachricht; der Server hält das ein, aber man soll es sehen
können, statt es glauben zu müssen. */
reihe.append(el('span', 'ev-beitrag__zahl',
`${text.length} von 2000 Zeichen`));
kasten.append(reihe);
const hinweis = el('p', 'ev-beitrag__hinweis');
hinweis.append(document.createTextNode(
'Der Link am Ende bleibt stehen — daraus baut Discord das Banner. '));
const a = el('a', '', 'Seite ansehen');
a.href = weg;
a.target = '_blank';
a.rel = 'noopener noreferrer';
hinweis.append(a);
kasten.append(hinweis);
karteEl.append(kasten);
feld.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
}
function karte(e) {
const k = el('article', 'eintrag-karte');
k.dataset.dringlich = e.dringlichkeit;
@@ -2245,12 +2325,32 @@
`Der Server hat abgelehnt (${a.status}), ohne den Grund mitzuschicken.`));
return;
}
/* DER FERTIGE BEITRAG, NICHT DER LINK (08.10.2026).
Filipe: „ich will dass wenn ich es teile im discord der
agentur, dass ein banner und ein kompletter fertiger
text für die creator entsteht und nicht einfach der
link kopiert wird damit die leute drauf gehen.“
Der Beitrag kommt vom Server (`r.text`) und endet mit
dem Link -- daraus baut Discord die Vorschau mit dem
Banner. Bleibt der Text aus (alte Serverfassung,
Baufehler), wird der Link kopiert wie bisher: Ein
Teilen, das gar nicht geht, wäre schlimmer als eines
ohne Text. */
const inhalt = r.text || r.weg;
let kopiert = false;
try { await navigator.clipboard.writeText(r.weg); kopiert = true; }
try { await navigator.clipboard.writeText(inhalt); kopiert = true; }
catch (f) { void f; }
/* UND ER WIRD GEZEIGT. Ein Text in der Zwischenablage,
den man nicht gesehen hat, fügt man blind ein -- und
merkt erst in Discord, wenn etwas fehlt. */
beitragZeigen(k, inhalt, kopiert, r.weg);
melde(kopiert
? `Link kopiert — in Discord einfügen: ${r.weg}`
: `Link zum Kopieren: ${r.weg}`);
? (r.text
? 'Fertiger Beitrag kopiert — in Discord einfügen.'
: `Link kopiert — in Discord einfügen: ${r.weg}`)
: 'Der Beitrag steht unten zum Kopieren.');
await laden();
}, 'schritt--teilen'));
/* ---- UND WOHIN GEHÖRT ES? (07.10.2026) --------------------
@@ -2855,6 +2955,23 @@
ablehnt, laesst den Menschen davor sich selbst fuer den Fehler
halten. */
ziel.hidden = !(bereich === 'agentur' && ich && ich.darf_kampagne_einlesen === true);
/* ZUGEKLAPPT, BIS MAN IHN BRAUCHT (08.10.2026). Begründung im
HTML. Derselbe Mechanismus wie überall; er merkt sich die
Wahl, also ist „zu“ nur die Vorgabe beim ersten Mal.
ERST WENN DER KASTEN SICHTBAR IST: Bei einem Creator steht
`hidden` am ganzen Abschnitt, und ein Klapper an etwas
Unsichtbarem merkt sich einen Zustand, den niemand
gewählt hat. */
if (!ziel.hidden) {
const schild = $('kampagne-schild');
const form = $('kampagne-form');
if (schild && form && window.klappPfeil && !schild.querySelector('.klapp-pfeil')) {
schild.append(window.klappPfeil());
}
window.abschnittKlappbar?.(schild, form, 'agentur:kampagnekasten', true);
}
}
function kampagneFeldBedienen() {
@@ -3850,11 +3967,56 @@
const spalte = el('section', 'kv-seite');
spalte.dataset.seite = seite;
const kopf = el('header', 'kv-seite__kopf');
/* ---- DAS WAPPEN DER SEITE (08.10.2026) --------------
Filipe: „perfektionier die zwei events kategorien. ich
will es noch viel krasser und spezieller.“
Ein gezeichnetes Zeichen, kein Bild: Es faerbt sich mit
`--kv-ton` mit, braucht keinen zweiten Abruf und bleibt
bei starkem Kontrast sichtbar. Die Form entsteht im CSS
(`.kv-seite__wappen`), hier steht nur, DASS es eines
gibt -- so wie ueberall im Haus. */
kopf.append(el('span', 'kv-seite__wappen'));
const wort = el('div', 'kv-seite__titel', titel);
wort.append(el('span', 'kv-seite__unter', unter));
kopf.append(wort);
kopf.append(el('span', 'kv-seite__zahl', String(drin.length)));
spalte.append(kopf);
/* ---- WIE ES UM DIE SEITE STEHT ----------------------
Eine Zahl allein („3“) sagt, wie viel da ist. Sie sagt
nicht, ob davon heute etwas laeuft -- und genau das ist
die Frage, mit der man auf dieses Brett schaut.
GEZAEHLT WIRD MIT DERSELBEN REGEL, die die Gruppen
darunter bildet (`evGruppeVon`). Eine zweite Zaehlung
koennte etwas anderes sagen als die Ueberschriften
zwei Zentimeter weiter unten.
WAS NULL IST, ERSCHEINT NICHT. „0 laufen“ ist keine
Auskunft, sondern Fuellmaterial -- und drei Nullen
nebeneinander lassen eine leere Spalte voll aussehen. */
const lage = el('div', 'kv-seite__lage');
for (const [schluessel, wortform] of [
['laeuft', 'laufen'], ['kommt', 'kommen'], ['vorbei', 'vorbei'],
]) {
const n = drin.filter((x) => (schluessel === 'vorbei'
? evGruppeVon(x).startsWith('vorbei')
: evGruppeVon(x) === schluessel)).length;
if (!n) continue;
const teil = el('span', 'kv-seite__lageteil', `${n} ${wortform}`);
teil.dataset.stand = schluessel;
lage.append(teil);
}
/* Eine leere Spalte sagt es mit Worten, nicht mit nichts. */
if (!lage.childElementCount) {
lage.append(el('span', 'kv-seite__lageteil', 'noch nichts'));
}
spalte.append(lage);
if (!drin.length) {
spalte.append(el('p', 'kv-seite__leer', seite === 'tiktok'
? 'Noch keine Einladung eingelesen.'