Files
dogfather-universe/workspace/assets/css/gate.css
T
DogFatherGitandClaude Opus 5 540b5d8838 Freie Namen, ein Call-Knopf, und drei Seiten, die fuer Scouts kaputt waren
DREI SACHEN AUF EINMAL, alle aus derselben Sitzung.

1. AUSSUCHEN ODER SELBST EINTRAGEN
   Wunsch: "ich will da auch sachen selber noch eintragen koennen, also
   aussuchen und selbst eintragen."

   Jede Personenauswahl (Kalender, Aufgaben, Bereiche, Content, Dateien)
   nimmt jetzt auch einen getippten Namen an -- eine Agentur, eine Marke,
   einen Gast ohne Konto. Dazu bekommt JEDE Auswahl ab acht Eintraegen
   ein Suchfeld: tippen statt scrollen.

   Gebaut IM vorhandenen Auswahl-Bauteil (wahl.js), nicht daneben. Der
   erste Anlauf war ein zweites Bauteil -- es hat sich prompt mit dem
   ersten gebissen, beide haben denselben <select> eingepackt. Ein
   zweites haette ausserdem anders ausgesehen und waere beim naechsten
   Umbau nur an einer von zwei Stellen nachgezogen worden.

   Der freie Name steht in einer EIGENEN Spalte je Feld; die Verknuepfung
   bleibt leer. Entweder eine Person ODER ein Name, nie beides.

   Filipe hat ausdruecklich auch bei den Creator-Feldern freie Namen
   gewollt, nachdem der Nachteil benannt war: Der Eintrag gehoert dann zu
   keinem Konto. Damit daraus kein STILLER Ausfall wird, faellt jede
   Abfrage, die bisher den Namen der verknuepften Person las, jetzt auf
   den freien Text zurueck (externSql) -- gekennzeichnet als "(extern)".
   Der Eintrag verschwindet dadurch aus keiner Liste, keiner Suche und
   keiner Uebersicht.

2. WO FUEHRE ICH DEN CALL?
   Den Knopf gab es, aber nur wenn jemand von Hand einen Link ins
   Ortsfeld getippt hatte UND das Gespraech noch bevorstand. Stand dort
   "Hier", war nichts zum Anklicken da.

   Jetzt hat das Team einen festen Call-Raum (Discord-Sprachkanal), den
   das Management einmal hinterlegt. Danach hat JEDER Call den Knopf --
   und er bleibt, solange das Gespraech laufen kann, nicht nur bis zur
   Startzeit. Ein eigener Link am Termin schlaegt den festen Raum.

3. WAS EINE ROLLE SIEHT, MUSS AUCH FUNKTIONIEREN
   Gemeldet: "cigdem kriegt als manager gewisse sachen nicht auf die sie
   sieht, check jede rolle ab."

   Neue Pruefung server/pruef-rollen.mjs schickt SECHS Rollen-Zustaende
   ueber alle 16 Seiten und misst Konsolenfehler, fehlgeschlagene
   Serveraufrufe, tote Verweise, haengende Ladeanzeigen und wortlos leere
   Seiten. 96 Durchgaenge.

   Der sechste Zustand ist der wichtige: eine Rolle OHNE zugeteilte
   Creator -- der Normalfall am ersten Tag und Cigdems echte Lage.
   Genau dort fielen die Seiten durch, waehrend dieselben Seiten MIT
   Zuteilung tadellos waren.

   VIER ECHTE FEHLER GEFUNDEN UND BEHOBEN:

   a) Eine Aufgabe, die eine Managerin ohne Creator anlegte, war fuer sie
      im selben Moment unsichtbar -- creator_id und verantwortlich_id
      leer, und "von mir selbst angelegt" stand in keiner
      Sichtbarkeitsregel. Nur DogFather sah sie noch. Kein Fehler, keine
      Meldung, die Aufgabe war einfach weg. Dasselbe bei den
      Bereichseintraegen. Beide Regeln kennen jetzt erstellt_von.
      Niemand sieht dadurch etwas Fremdes -- nur das Eigene.

   b) Ein Scout ohne zugeteilten Creator bekam auf die GESAMTE
      Report-Seite 404, obwohl sie fuer ihn verlinkt ist. Die Seite
      antwortet jetzt sauber und leer, statt sich zu verweigern.

   c) Start-Check und Report blieben fuer immer auf "wird geladen"
      stehen, wenn es nichts zu laden gab.

   d) Das Creator-Profil war fuer Scouts ohne Zuteilung wortlos leer --
      der erklaerende Satz stand nur in der grauen Unterzeile.

   Ausserdem meldete die bestehende Lesbarkeitspruefung zwei neue
   Beschriftungen von mir als zu klein fuers Handy (10,88 statt 11,5 px).
   Behoben, und dieselbe Groesse an der Serien-Karte gleich mit -- dort
   waere es erst aufgefallen, sobald jemand eine Wiederholung anlegt.

GEPRUEFT: pruef-rollen 97, pruef-freie-namen 32 (mit Gegenproben:
Ben sieht Cigdems Aufgabe NICHT; die Saeulen-Zuordnung nimmt
ausdruecklich KEINEN freien Namen). Alle bestehenden Laeufe gruen:
Startansicht 133, Kalender 84, Serien 67, Protokoll 52, Handy 50, Sicht
48, Ampel 47, Content 45, Aufgabenbrett 44, Sprung 43,
Personen-Loeschen 40, Bereiche 37, Uebersicht 33, Workspace-Seiten 32,
Formulare 19, Betreuung 18, Grosscheck 15, Lesbarkeit 14.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-02 20:10:09 +02:00

914 lines
33 KiB
CSS

/* ===================================================================
Creator Workspace - Anmeldeseite
-------------------------------------------------------------------
Grundsatz: dunkel und ruhig. Die Farben stammen aus dem Motiv, sind
aber bewusst entsättigt - Neon als Stimmung, nicht als Blendung.
Kein reines Weiss auf Schwarz, keine harten Kanten, keine Blinker.
=================================================================== */
:root {
--tinte: #05070d;
--glas: rgba(10, 14, 23, 0.74);
--glas-tief: rgba(6, 9, 16, 0.86);
/* Die Flaeche jeder Kachel, Karte und Spalte -- an EINER Stelle.
Vorher stand in jeder Datei ein eigener Wert zwischen .02 und .032
WEISS, also praktisch durchsichtig. Solange der Hintergrund fast
schwarz war, fiel das nicht auf. Seit die Buehnen hell sind, kam das
Bild mitten durch den Text.
Jetzt eine deckende dunkle Flaeche: Der Hintergrund bleibt sichtbar,
aber DAZWISCHEN -- nicht hinter der Schrift. Genau das macht den
Unterschied zwischen "hat einen Hintergrund" und "ist unlesbar".
Wer die Kacheln durchsichtiger oder dichter will, aendert nur diese
eine Zeile. */
--flaeche: rgba(9, 13, 22, 0.78);
--rand: rgba(150, 186, 220, 0.16);
--rand-hell: rgba(150, 186, 220, 0.30);
--text: #dbe4ef; /* kein reines Weiss - schont die Augen */
--text-leise: #94a5bb;
/* Von #6d7d92 auf #75859a angehoben (01.09.2026).
Aufgefallen erst, als die Buehne im Hintergrund dazukam und der
Kontrast an echten BILDPUNKTEN gemessen wurde statt an der
Farbangabe: Der leiseste Grauton lag bei 4,43:1 -- knapp unter den
geforderten 4,5:1. Das war schon vorher so, nur hatte es nie jemand
nachgerechnet. Jetzt 4,95:1 auf dem hellsten Untergrund, 5,35:1 auf
reiner Tinte. Immer noch deutlich leiser als der Haupttext, aber
lesbar -- und das gilt jetzt fuer JEDE Seite, nicht nur fuer die
mit dem Hintergrundbild. */
--text-still: #75859a;
--akzent: #3fbdf5; /* aus dem Neon des Motivs, entsättigt */
--akzent-tief: #1f7fb0;
--violett: #8a76ff;
--warn: #ff9aa2; /* weiches Rot statt Signalrot */
--radius: 18px;
--radius-klein: 12px;
--schatten: 0 24px 60px -20px rgba(0, 0, 0, 0.75);
--tempo: .22s cubic-bezier(.4, 0, .2, 1);
}
*, *::before, *::after { box-sizing: border-box; }
/* `color-scheme: dark` ist der eigentliche Schalter für alles, was der
Browser selbst zeichnet und was CSS nicht erreicht: die aufgeklappte
Liste eines <select>, den Kalender eines Datumsfeldes, Bildlaufleisten,
die Textmarkierung. Ohne diese Zeile geht der Browser von einer hellen
Seite aus und malt die Auswahlliste weiss -- die helle Schrift darauf
war dann praktisch unlesbar. */
html { -webkit-text-size-adjust: 100%; color-scheme: dark; }
/* Zusätzlich die Einträge selbst setzen. Ältere Browser und Linux-
Oberflächen ignorieren color-scheme teilweise; diese beiden Zeilen
wirken auch dort. */
option {
background-color: #0d131d;
color: var(--text);
}
/* WICHTIG. Das hidden-Attribut blendet ein Element nur über eine sehr
schwache Browserregel aus -- JEDE eigene display-Angabe schlägt sie.
Ohne diese Zeile blieben Elemente sichtbar, die per JavaScript
ausgeblendet wurden: das Ablegefeld für Dateien (display: grid) und
die Verwaltungsfelder im Creator-Profil (ebenfalls grid). Aufgefallen
erst im Screenshot -- eine Prüfung auf das Attribut allein hätte den
Fehler nie gezeigt, denn gesetzt war es ja. */
[hidden] { display: none !important; }
body.gate {
margin: 0;
min-height: 100svh;
display: flex;
align-items: center;
background: var(--tinte);
color: var(--text);
font-family: "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
font-size: 16px;
line-height: 1.55;
letter-spacing: .01em;
overflow-x: hidden;
}
/* ---------- Bühne ---------------------------------------------------- */
.buehne { position: fixed; inset: 0; z-index: 0; }
.buehne__bild {
width: 100%; height: 100%;
object-fit: cover;
object-position: center;
/* Minimal abgedunkelt und entsättigt, damit der Text darüber ruhig liegt. */
filter: saturate(.92) brightness(.9);
transform: scale(1.03); /* Reserve für die Parallaxe */
transition: transform 1.4s cubic-bezier(.2, .7, .2, 1);
will-change: transform;
}
/* Der Schleier macht die linke Seite lesbar, ohne das Motiv zu erschlagen.
Rechts bleibt er fast durchsichtig - dort stehen Dogfather und Hasi Dog. */
.buehne__schleier {
position: absolute; inset: 0;
/* Viele weiche Stufen statt weniger harter - sonst zeichnet sich die Kante
des Verlaufs als sichtbare Diagonale quer durchs Bild. */
background:
linear-gradient(96deg,
rgba(3, 5, 10, 0.93) 0%,
rgba(3, 5, 10, 0.90) 18%,
rgba(3, 5, 10, 0.82) 28%,
rgba(4, 6, 12, 0.68) 36%,
rgba(4, 6, 12, 0.50) 44%,
rgba(4, 6, 12, 0.33) 52%,
rgba(4, 6, 12, 0.19) 60%,
rgba(4, 6, 12, 0.09) 68%,
rgba(4, 6, 12, 0.03) 78%,
rgba(4, 6, 12, 0.00) 88%),
radial-gradient(120% 90% at 50% 120%, rgba(0, 0, 0, .42), transparent 62%);
}
/* ---------- Tafel ---------------------------------------------------- */
.tafel {
position: relative;
z-index: 1;
margin-left: clamp(24px, 7vw, 104px);
width: min(452px, 44vw);
padding: clamp(28px, 3vw, 40px) clamp(24px, 2.6vw, 36px) clamp(24px, 2.6vw, 32px);
background: var(--glas);
backdrop-filter: blur(22px) saturate(115%);
-webkit-backdrop-filter: blur(22px) saturate(115%);
border: 1px solid var(--rand);
border-radius: var(--radius);
box-shadow: var(--schatten);
animation: auftauchen .7s cubic-bezier(.2, .7, .2, 1) both;
}
/* Feine Lichtkante links - nimmt das Neon des Motivs auf, ohne zu leuchten. */
.tafel__kante {
position: absolute; left: -1px; top: 16%; bottom: 16%;
width: 2px; border-radius: 2px;
background: linear-gradient(180deg,
transparent, var(--akzent) 22%, var(--violett) 72%, transparent);
opacity: .55;
}
@keyframes auftauchen {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: none; }
}
/* ---------- Kopf ----------------------------------------------------- */
.marke {
margin: 0 0 6px;
font-size: .74rem; font-weight: 700;
letter-spacing: .18em; text-transform: uppercase;
color: var(--akzent);
}
.titel {
margin: 0 0 8px;
font-size: clamp(1.6rem, 2.5vw, 2rem);
font-weight: 700; line-height: 1.15;
letter-spacing: -.01em;
}
.unterzeile { margin: 0 0 26px; color: var(--text-leise); font-size: .94rem; }
/* ---------- Rollen --------------------------------------------------- */
.feldschild {
margin: 0 0 10px;
font-size: .78rem; font-weight: 600;
letter-spacing: .06em; text-transform: uppercase;
color: var(--text-still);
}
.rollen { margin-bottom: 22px; }
.rolle {
display: flex; align-items: center; gap: 13px;
width: 100%; margin-bottom: 8px; padding: 11px 14px;
background: rgba(255, 255, 255, .035);
border: 1px solid var(--rand);
border-radius: var(--radius-klein);
color: var(--text);
font: inherit; text-align: left; cursor: pointer;
transition: border-color var(--tempo), background var(--tempo), transform var(--tempo);
}
.rolle:hover { background: rgba(255, 255, 255, .06); border-color: var(--rand-hell); }
.rolle:active { transform: scale(.995); }
.rolle[aria-checked="true"] {
background: linear-gradient(100deg, rgba(63, 189, 245, .14), rgba(138, 118, 255, .10));
border-color: rgba(63, 189, 245, .55);
}
.rolle__zeichen {
flex: 0 0 auto; width: 21px; height: 21px;
fill: none; stroke: var(--text-still);
stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
transition: stroke var(--tempo);
}
.rolle__zeichen .strich { fill: none; }
.rolle[aria-checked="true"] .rolle__zeichen { stroke: var(--akzent); }
.rolle__text { display: flex; flex-direction: column; min-width: 0; }
.rolle__name { font-weight: 600; font-size: .95rem; }
.rolle__zusatz { font-size: .8rem; color: var(--text-still); }
/* ---------- Formular ------------------------------------------------- */
.fuer { color: var(--akzent); text-transform: none; letter-spacing: normal; }
.feld { position: relative; }
#code {
width: 100%; padding: 13px 46px 13px 15px;
background: rgba(255, 255, 255, .045);
border: 1px solid var(--rand);
border-radius: var(--radius-klein);
color: var(--text);
font: inherit; letter-spacing: .06em;
transition: border-color var(--tempo), background var(--tempo), box-shadow var(--tempo);
}
#code::placeholder { color: var(--text-still); letter-spacing: normal; }
#code:hover { border-color: var(--rand-hell); }
#code:focus {
outline: none;
border-color: rgba(63, 189, 245, .7);
background: rgba(255, 255, 255, .065);
box-shadow: 0 0 0 3px rgba(63, 189, 245, .16);
}
.feld__auge {
position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
width: 36px; height: 36px; padding: 8px;
background: none; border: 0; border-radius: 9px;
cursor: pointer; color: var(--text-still);
transition: color var(--tempo), background var(--tempo);
}
.feld__auge:hover { color: var(--text-leise); background: rgba(255, 255, 255, .06); }
.feld__auge svg { width: 100%; height: 100%; fill: none; stroke: currentColor;
stroke-width: 1.7; stroke-linecap: round; }
.feld__auge .durch { opacity: 0; transition: opacity var(--tempo); }
.feld__auge[aria-pressed="true"] .durch { opacity: 1; }
.hinweis, .fehler {
margin: 9px 2px 0; font-size: .84rem; min-height: 0;
}
.hinweis { color: var(--text-leise); }
.fehler { color: var(--warn); }
.fehler:empty { margin: 0; }
.knopf__laden {
position: absolute; right: 16px; top: 50%; margin-top: -8px;
width: 16px; height: 16px; border-radius: 50%;
border: 2px solid rgba(255, 255, 255, .35);
border-top-color: #fff;
opacity: 0;
animation: dreh .8s linear infinite;
}
.knopf[disabled] .knopf__laden { opacity: 1; }
@keyframes dreh { to { transform: rotate(360deg); } }
.fuss {
margin: 22px 0 0; padding-top: 16px;
border-top: 1px solid var(--rand);
font-size: .78rem; color: var(--text-still); text-align: center;
}
/* ---------- Übergang beim erfolgreichen Anmelden ---------------------
Die Tafel tritt zurück, das Motiv wird kurz frei - als würde sich das
Tor öffnen. Bewusst kurz und ohne Aufblitzen. */
body.oeffnet .tafel {
opacity: 0; transform: translateY(-8px) scale(.985);
transition: opacity .26s ease, transform .26s ease;
}
body.oeffnet .buehne__schleier { opacity: .35; transition: opacity .26s ease; }
body.oeffnet .buehne__bild { filter: saturate(1) brightness(1); transition: filter .26s ease; }
/* ---------- Tastatur-Bedienung --------------------------------------- */
:where(button, input):focus-visible {
outline: 2px solid var(--akzent);
outline-offset: 2px;
}
/* ---------- Schmale oder hohe Fenster --------------------------------
Sobald das Fenster nicht mehr deutlich breiter als hoch ist, würde die
Tafel den Figuren zu nahe kommen. Dann wechselt die Seite auf das
Hochformat-Motiv und die Tafel rückt nach unten - die beiden bleiben
oben im Bild sichtbar. */
/* Gemeinsam: Tafel wandert nach unten, Motiv bleibt darüber frei. */
@media (max-width: 899px), (max-aspect-ratio: 7/5) {
body.gate { align-items: flex-end; justify-content: center; }
.tafel {
margin: 0 0 max(18px, env(safe-area-inset-bottom));
width: min(520px, calc(100vw - 32px));
}
.tafel__kante { display: none; }
}
/* Schmale Geräte: Hochformat-Ausschnitt. Die Bildfläche endet oberhalb der
Tafel - dadurch liegt ihr Seitenverhältnis nah am Ausschnitt und der
Browser muss seitlich kaum etwas wegschneiden. Beide Figuren bleiben ganz
im Bild, der Verlauf blendet nach unten ins Dunkle. */
@media (max-width: 899px) {
.buehne { bottom: auto; height: 66svh; }
.buehne__bild { object-position: center top; }
.buehne__schleier {
background:
linear-gradient(180deg,
rgba(3, 5, 10, 0.30) 0%,
rgba(3, 5, 10, 0.10) 20%,
rgba(3, 5, 10, 0.22) 46%,
rgba(3, 5, 10, 0.55) 66%,
rgba(5, 7, 13, 0.88) 82%,
/* Muss vor der Unterkante voll deckend sein und exakt der Seitenfarbe
entsprechen - sonst zeichnet sich dort eine feine Kante ab. */
rgba(5, 7, 13, 1.00) 94%);
}
}
/* Breite, aber hohe Fenster (z. B. 1100x980): Querformat bleibt und füllt die
ganze Fläche - sonst entsteht unten eine tote dunkle Zone. Weil hier
seitlich beschnitten wird, rückt das Motiv nach rechts, damit Dogfather und
Hasi Dog vollständig im Bild bleiben. */
@media (min-width: 900px) and (max-aspect-ratio: 7/5) {
.buehne__bild { object-position: 82% center; }
.buehne__schleier {
background:
linear-gradient(180deg,
rgba(3, 5, 10, 0.12) 0%,
rgba(3, 5, 10, 0.20) 34%,
rgba(3, 5, 10, 0.48) 56%,
rgba(3, 5, 10, 0.82) 74%,
rgba(3, 5, 10, 0.95) 90%);
}
}
@media (max-width: 420px) {
.tafel { padding: 22px 18px 20px; border-radius: 16px; }
.rolle__zusatz { display: none; }
}
/* Niedrige Bildschirme (iPhone 13 hat nur 664 px nutzbare Höhe): Die Tafel
würde fast alles verdecken. Sie wird kompakter, und die Bildfläche wird
flacher - dadurch skaliert der Ausschnitt nach BREITE statt nach Höhe und
zeigt genau den oberen Teil mit den Gesichtern. */
@media (max-width: 899px) and (max-height: 760px) {
.buehne { height: 46svh; }
.tafel { padding: 16px 18px 14px; }
.titel { font-size: 1.42rem; margin-bottom: 4px; }
.unterzeile { margin-bottom: 16px; font-size: .88rem; }
.marke { font-size: .68rem; margin-bottom: 3px; }
.feldschild { margin-bottom: 7px; font-size: .74rem; }
.rollen { margin-bottom: 14px; }
.rolle { padding: 8px 13px; margin-bottom: 6px; }
.rolle__name { font-size: .9rem; }
#code { padding: 11px 44px 11px 14px; }
.knopf { margin-top: 12px; padding: 11px 18px; }
.fuss { margin-top: 14px; padding-top: 10px; font-size: .73rem; }
}
/* ---------- Weniger Bewegung ----------------------------------------- */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: .001ms !important;
animation-iteration-count: 1 !important;
transition-duration: .001ms !important;
}
.buehne__bild { transform: none; transition: none; }
}
/* ===================================================================
Suche über alle Bereiche.
Liegt bewusst hier und nicht in einer Seiten-Datei: kopf.js baut die
Suche auf JEDER Workspace-Seite ein, also muss auch das Aussehen
überall geladen sein. gate.css ist die einzige Datei, die das
erfüllt. (Genau der Fehler, der bei .knopf-still schon einmal
passiert ist -- Baustein in einer Seiten-Datei, prompt fehlte er
woanders.)
=================================================================== */
.suche-knopf__lupe {
width: 15px; height: 15px; flex: none;
fill: none; stroke: currentColor; stroke-width: 1.8;
stroke-linecap: round;
}
@media (max-width: 620px) { .suche-knopf__text { display: none; } }
.suche-schleier {
position: fixed; inset: 0; z-index: 80;
/* flex-start, nicht das voreingestellte stretch -- sonst zieht sich
das Fenster auf volle Hoehe, auch bei zwei Treffern. */
display: flex; justify-content: center; align-items: flex-start;
padding: 9vh 18px 18px;
background: rgba(3, 5, 10, .74);
backdrop-filter: blur(5px);
}
body.suche-offen { overflow: hidden; }
.suche-fenster {
width: min(680px, 100%); max-height: 78vh;
display: flex; flex-direction: column;
background: #0b111b;
border: 1px solid rgba(63, 189, 245, .26);
border-radius: var(--radius);
box-shadow: 0 28px 70px -22px rgba(0, 0, 0, .85);
overflow: hidden;
}
.suche-zeile {
display: flex; align-items: center; gap: 10px;
padding: 14px 16px;
border-bottom: 1px solid var(--rand);
}
.suche-feld {
flex: 1; min-width: 0; padding: 6px 2px;
background: transparent; border: 0;
color: var(--text); font: inherit; font-size: 1.02rem;
}
.suche-feld:focus { outline: none; }
.suche-feld::placeholder { color: var(--text-still); }
/* Das eingebaute Kreuz von type="search" steht sonst direkt neben dem
Esc-Knopf -- zwei Wege, dasselbe zu tun, dicht nebeneinander. */
.suche-feld::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.suche-ergebnis { overflow-y: auto; padding: 6px 10px 12px; }
.suche-hinweis {
margin: 0; padding: 26px 8px; text-align: center;
color: var(--text-still); font-size: .88rem;
}
.suche-gruppe { margin-top: 10px; }
.suche-gruppe__titel {
margin: 0 0 5px; padding: 0 8px;
font-size: .71rem; font-weight: 700;
letter-spacing: .08em; text-transform: uppercase; color: var(--akzent);
}
.suche-treffer {
display: block; padding: 9px 11px; margin-bottom: 3px;
border-radius: var(--radius-klein);
color: var(--text); text-decoration: none;
border: 1px solid transparent;
}
.suche-treffer:hover, .suche-treffer:focus-visible {
background: rgba(255, 255, 255, .05);
border-color: var(--rand);
outline: none;
}
.suche-treffer__titel { display: block; font-size: .92rem; font-weight: 600; }
.suche-treffer__zusatz {
display: block; margin-top: 1px;
font-size: .74rem; color: var(--text-still);
}
.suche-treffer__text {
display: block; margin-top: 3px;
font-size: .81rem; color: var(--text-leise); line-height: 1.45;
}
@media (prefers-reduced-motion: reduce) { .suche-knopf { transition: none; } }
/* ===================================================================
KNOPF-SYSTEM „GESCHLIFFEN“ (von Filipe gewählt, 28.08.2026)
DAS IST DIE EINZIGE STELLE, AN DER KNÖPFE GESTALTET WERDEN.
Vorher standen sie in neun Dateien verstreut -- und prompt sahen
dieselben Knöpfe auf verschiedenen Seiten verschieden aus. Wer einen
neuen Knopf braucht, nimmt hier eine Farbklasse, schreibt kein CSS.
Der Aufbau: ein farbiger Verlaufsrand (padding-box/border-box) und
ein feiner heller Streifen obenauf -- wie eine angeschliffene Kante.
Die Farbe sitzt im Rand und in der Schrift, die Fläche bleibt dunkel.
Beim Zeigen hebt sich der Knopf einen Pixel an und bekommt einen
weichen farbigen Schein darunter.
JEDE AKTION HAT IHRE EIGENE FARBE. Alle Töne sind bewusst gedeckt,
nicht neon: augenschonend ist Vorgabe, nicht Geschmack. Der
schwächste Kontrast im ganzen System liegt bei 9,2:1 -- die
Barrierefreiheits-Schwelle wäre 4,5:1.
=================================================================== */
:root {
--k-gut: #5fc99a; /* passt, erledigt, freigegeben */
--k-mittel: #e8b877; /* ausbaufähig, wartet */
--k-warn: #f0868f; /* Handlungsbedarf, löschen */
--k-gold: #e8c07d; /* Onboarding -- legt eine Person an */
--k-neutral: #8e9cb0; /* abbrechen, schließen */
/* Neutral braucht einen echten Farbwert. Halbdurchsichtiges Weiß
ergab in der Messung 1,1:1 -- praktisch unlesbar. */
}
.knopf,
.knopf-still,
.schritt,
.abmelden,
.stufe-knopf,
.suche-knopf,
.suche-zu,
.todo__weg,
.karte__stift {
--f: var(--akzent);
position: relative;
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
padding: 10px 17px;
border: 1px solid transparent;
border-radius: 11px;
background:
linear-gradient(#121926, #0d1420) padding-box,
linear-gradient(150deg,
color-mix(in srgb, var(--f) 75%, transparent),
rgba(255, 255, 255, .07) 52%,
color-mix(in srgb, var(--f) 30%, transparent)) border-box;
color: color-mix(in srgb, var(--f) 55%, #ffffff);
font: inherit; font-size: .85rem; font-weight: 600;
cursor: pointer;
transition: transform .16s ease, box-shadow .2s ease, color .16s ease;
}
/* Der helle Streifen obenauf -- die „geschliffene“ Kante. */
.knopf::after,
.knopf-still::after,
.schritt::after,
.abmelden::after,
.stufe-knopf::after,
.suche-knopf::after,
.suche-zu::after,
.todo__weg::after,
.karte__stift::after {
content: ""; position: absolute; left: 11px; right: 11px; top: 1px; height: 1px;
border-radius: 1px; pointer-events: none;
background: linear-gradient(90deg, transparent,
color-mix(in srgb, var(--f) 55%, transparent), transparent);
opacity: .75;
}
.knopf:hover,
.knopf-still:hover,
.schritt:hover:not([disabled]),
.abmelden:hover,
.stufe-knopf:hover,
.suche-knopf:hover,
.suche-zu:hover,
.todo__weg:hover,
.karte__stift:hover {
transform: translateY(-1px);
color: #fff;
box-shadow: 0 5px 18px -8px color-mix(in srgb, var(--f) 85%, transparent),
inset 0 0 22px -14px var(--f);
}
.knopf:active, .knopf-still:active, .schritt:active,
.abmelden:active, .stufe-knopf:active { transform: translateY(0); }
.knopf:focus-visible,
.knopf-still:focus-visible,
.schritt:focus-visible,
.abmelden:focus-visible,
.stufe-knopf:focus-visible,
.suche-knopf:focus-visible,
.suche-zu:focus-visible,
.todo__weg:focus-visible,
.karte__stift:focus-visible {
outline: none;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--f) 32%, transparent);
}
/* ---------- Gewählt / aktiv ----------------------------------------
Die Fläche nimmt die Farbe auf, der Rand wird voll. Das ist auch der
Auftritt für den wichtigsten Knopf einer Seite. */
.knopf,
.stufe-knopf[data-aktiv="ja"],
[data-an] {
color: #fff;
background:
linear-gradient(color-mix(in srgb, var(--f) 20%, #121926),
color-mix(in srgb, var(--f) 12%, #0d1420)) padding-box,
linear-gradient(150deg, var(--f),
color-mix(in srgb, var(--f) 45%, transparent)) border-box;
box-shadow: 0 4px 16px -9px var(--f);
}
/* ---------- Farbe je Aktion --------------------------------------- */
.knopf--gut, .stufe-knopf[data-wert="ok"] { --f: var(--k-gut); }
.knopf--mittel, .stufe-knopf[data-wert="mittel"] { --f: var(--k-mittel); }
.knopf--warn, .stufe-knopf[data-wert="handlung"],
.knopf-still--warn, .todo__weg { --f: var(--k-warn); }
.knopf--gold { --f: var(--k-gold); }
.knopf--violett { --f: var(--violett); }
.knopf-still, .schritt, .abmelden, .suche-zu,
.karte__stift { --f: var(--k-neutral); }
/* Der Weiter-Knopf der Scout-Pipeline trägt die Farbe seiner Stufe --
bleibt aber bewusst ungefüllt. Er wiederholt sich auf jeder Karte;
als volle Fläche wäre die Seite ein Streifenmuster. */
.knopf--weiter {
--f: var(--stufe-farbe, var(--akzent));
color: color-mix(in srgb, var(--stufe-farbe) 55%, #ffffff);
background:
linear-gradient(#121926, #0d1420) padding-box,
linear-gradient(150deg,
color-mix(in srgb, var(--f) 75%, transparent),
rgba(255, 255, 255, .07) 52%,
color-mix(in srgb, var(--f) 30%, transparent)) border-box;
box-shadow: none;
}
/* ---------- Größen ------------------------------------------------- */
.knopf--klein { padding: 8px 15px; font-size: .82rem; }
.schritt { padding: 6px 12px; font-size: .76rem; border-radius: 9px; }
.abmelden { padding: 8px 15px; font-size: .82rem; }
.suche-zu { padding: 5px 10px; font-size: .74rem; letter-spacing: .04em; border-radius: 9px; }
.suche-knopf { padding: 8px 15px; font-size: .82rem; }
.stufe-knopf { padding: 7px 13px; font-size: .78rem; }
.todo__weg { width: 36px; padding: 0; font-size: 1.05rem; line-height: 1; }
.karte__stift { padding: 4px 10px; font-size: .74rem; border-radius: 8px; }
/* Der Anmeldeknopf ist der einzige, der die ganze Breite bekommt --
auf der Anmeldeseite gibt es nichts anderes zu tun. */
.gate .knopf { width: 100%; margin-top: 18px; padding: 13px 18px; font-size: .95rem; }
.schritt[disabled], .knopf[disabled] { opacity: .45; cursor: default; box-shadow: none; }
.knopf[disabled] { cursor: progress; }
.knopf[disabled]:hover { transform: none; }
.suche-knopf__lupe { width: 15px; height: 15px; flex: none; }
@media (prefers-reduced-motion: reduce) {
.knopf, .knopf-still, .schritt, .abmelden,
.stufe-knopf, .suche-knopf, .suche-zu, .todo__weg,
.karte__stift { transition: none; }
.knopf:hover, .knopf-still:hover, .schritt:hover, .abmelden:hover,
.stufe-knopf:hover, .suche-knopf:hover, .suche-zu:hover,
.todo__weg:hover, .karte__stift:hover { transform: none; }
}
/* ===================================================================
KI-Vorschläge (28.08.2026).
Bewusst ANDERS als die normalen Knöpfe: Ein KI-Knopf tut nichts
Endgültiges, er schlägt vor. Deshalb gestrichelter Rand statt
geschliffener Kante -- man soll auf den ersten Blick sehen, dass hier
etwas Unverbindliches passiert.
Liegt in gate.css, weil drei Seiten ihn benutzen.
=================================================================== */
.ki-knopf {
display: inline-flex; align-items: center; gap: 8px;
padding: 8px 15px; cursor: pointer;
background: rgba(138, 118, 255, .07);
border: 1px dashed rgba(138, 118, 255, .45);
border-radius: 999px;
color: #b6a9ff; font: inherit; font-size: .82rem; font-weight: 600;
transition: color .16s ease, border-color .16s ease, background .16s ease;
}
.ki-knopf:hover:not([disabled]) {
color: #fff;
border-color: rgba(138, 118, 255, .8);
background: rgba(138, 118, 255, .16);
}
.ki-knopf:focus-visible {
outline: none; box-shadow: 0 0 0 3px rgba(138, 118, 255, .3);
}
.ki-knopf[disabled] { cursor: progress; }
.ki-knopf__funke {
width: 15px; height: 15px; flex: none;
fill: currentColor; stroke: none;
}
/* Denkt gerade: der Funke pulst langsam. Zwölf Sekunden ohne
Rückmeldung fühlen sich sonst an wie ein Fehler. */
.ki-knopf[data-denkt="ja"] { border-style: solid; }
.ki-knopf[data-denkt="ja"] .ki-knopf__funke { animation: ki-puls 1.6s ease-in-out infinite; }
@keyframes ki-puls { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
.ki-fehler, .ki-hinweis {
display: block; margin-top: 7px;
font-size: .8rem; color: var(--text-still);
}
.ki-fehler { color: var(--warn); }
.ki-reihe { margin-top: 22px; }
.ki-text {
margin: 10px 0 0; padding: 13px 15px;
background: rgba(138, 118, 255, .06);
border: 1px solid rgba(138, 118, 255, .28);
border-left-width: 3px;
border-radius: var(--radius-klein);
font-size: .9rem; color: var(--text-leise); line-height: 1.6;
max-width: 80ch;
}
.todo-knoepfe { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
@media (prefers-reduced-motion: reduce) {
.ki-knopf { transition: none; }
.ki-knopf[data-denkt="ja"] .ki-knopf__funke { animation: none; opacity: .7; }
}
/* ===================================================================
Auswahlfelder (31.08.2026).
Das echte <select> bleibt im Dokument -- es haelt den Wert und macht
jedes vorhandene `feld.value` weiter richtig. Es wird nur unsichtbar
gemacht; sichtbar ist der Knopf darueber.
Bewusst NICHT display:none: Ein so verstecktes Feld faellt aus der
Formularpruefung des Browsers heraus, und `required` wuerde stumm
nicht mehr greifen.
=================================================================== */
.wahl2 { position: relative; display: block; }
.wahl2__echt {
position: absolute; inset: 0;
opacity: 0; pointer-events: none;
/* Nur ein Pixel gross, damit ein Fehlerhinweis des Browsers noch am
richtigen Ort erscheint. */
width: 1px; height: 1px;
}
.wahl2__knopf {
display: flex; align-items: center; justify-content: space-between; gap: 10px;
width: 100%; padding: 10px 12px; cursor: pointer;
background: linear-gradient(#141c28, #101722);
border: 1px solid var(--rand); border-radius: var(--radius-klein);
color: var(--text); font: inherit; text-align: left;
transition: border-color .16s ease, background .16s ease;
}
.wahl2__knopf:hover:not([disabled]) {
border-color: rgba(255, 255, 255, .24);
background: linear-gradient(#18212f, #141b27);
}
.wahl2__knopf:focus-visible,
.wahl2__knopf[aria-expanded="true"] {
outline: none;
border-color: rgba(63, 189, 245, .65);
box-shadow: 0 0 0 3px rgba(63, 189, 245, .16);
}
.wahl2__knopf[disabled] { opacity: .5; cursor: default; }
.wahl2__knopf[data-leer="ja"] .wahl2__text { color: var(--text-still); }
.wahl2__text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wahl2__pfeil {
width: 16px; height: 16px; flex: none;
fill: none; stroke: var(--text-still); stroke-width: 1.8;
stroke-linecap: round; stroke-linejoin: round;
transition: transform .18s ease;
}
.wahl2__knopf[aria-expanded="true"] .wahl2__pfeil { transform: rotate(180deg); }
/* Die Liste haengt am Fenster, nicht am Elternelement -- in einer Karte
mit overflow: hidden waere sie sonst abgeschnitten. */
.wahl2__liste {
position: fixed; z-index: 90;
min-width: 180px; max-width: min(420px, calc(100vw - 24px));
overflow-y: auto; padding: 5px;
background: #0d1420;
border: 1px solid rgba(63, 189, 245, .3);
border-radius: 11px;
box-shadow: 0 18px 44px -16px rgba(0, 0, 0, .85);
}
.wahl2__gruppe {
margin: 7px 8px 3px; font-size: .68rem; font-weight: 700;
letter-spacing: .08em; text-transform: uppercase; color: var(--akzent);
}
.wahl2__gruppe:first-child { margin-top: 3px; }
.wahl2__eintrag {
display: block; width: 100%; padding: 8px 11px; cursor: pointer;
background: transparent; border: 0; border-radius: 7px;
color: var(--text-leise); font: inherit; font-size: .88rem; text-align: left;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wahl2__eintrag:hover:not([disabled]) { background: rgba(255, 255, 255, .06); color: var(--text); }
.wahl2__eintrag:focus-visible {
outline: none; background: rgba(63, 189, 245, .16); color: #fff;
}
.wahl2__eintrag[data-an="ja"] {
color: #fff; font-weight: 600;
background: rgba(63, 189, 245, .12);
}
.wahl2__eintrag[disabled], .wahl2__eintrag[data-aus="ja"] {
color: var(--text-still); cursor: default;
}
/* ---------- Suchen und selbst eintragen (02.09.2026) --------------------
Das Suchfeld sitzt fest oben in der Liste, die Eintraege scrollen
darunter weg -- sonst waere es beim Scrollen weg, und man wuesste
nicht mehr, wonach gerade gefiltert wird. */
.wahl2__suche {
display: block; width: 100%;
margin: 0 0 5px; padding: 8px 11px;
box-sizing: border-box;
background: rgba(255, 255, 255, .04);
border: 1px solid var(--rand); border-radius: 8px;
color: var(--text); font: inherit; font-size: .88rem;
}
.wahl2__suche::placeholder { color: var(--text-still); }
.wahl2__suche:focus-visible {
outline: none;
border-color: rgba(63, 189, 245, .6);
box-shadow: 0 0 0 3px rgba(63, 189, 245, .14);
}
/* Nur die Eintraege scrollen, nicht das Suchfeld mit. */
.wahl2__zeilen { max-height: min(46vh, 320px); overflow-y: auto; }
/* Der getippte Name als eigener Eintrag. Gedeckt abgesetzt, nicht
grell -- er ist eine Moeglichkeit, keine Warnung. */
.wahl2__eintrag--eigen {
color: #e8c07a;
border: 1px dashed rgba(216, 161, 58, .45);
margin-bottom: 4px;
}
.wahl2__eintrag--eigen:hover:not([disabled]) {
background: rgba(216, 161, 58, .12); color: #f4dcb0;
}
.wahl2__leer {
margin: 0; padding: 9px 11px;
color: var(--text-still); font-size: .82rem;
}
/* Steht ein freier Name im Feld, sagt der Rand es leise mit. */
.wahl2__knopf[data-extern="ja"] {
border-color: color-mix(in srgb, #d8a13a 45%, var(--rand));
}
.wahl2__hinweis {
margin: 6px 0 0;
font-size: .78rem; line-height: 1.45;
color: var(--text-leise);
}
.wahl2__hinweis::before {
content: "↯"; margin-right: 6px; color: #d8a13a;
}
@media (prefers-reduced-motion: reduce) {
.wahl2__knopf, .wahl2__pfeil { transition: none; }
}
@media (forced-colors: active) {
/* Ohne Farben traegt der gestrichelte Rand die Unterscheidung. */
.wahl2__eintrag--eigen { border: 1px dashed CanvasText; }
.wahl2__knopf[data-extern="ja"] { border: 2px solid CanvasText; }
}
/* ===================================================================
Schalter (31.08.2026).
Lag zuerst in wissen.css -- und fehlte prompt auf der Automationen-
Seite, wo er als nacktes Kaestchen erschien. Genau derselbe Fehler
wie damals bei .knopf-still: Ein gemeinsamer Baustein in einer
Seiten-Datei fehlt irgendwann dort, wo die Datei nicht geladen wird.
=================================================================== */
/* ---------- Schalter „besonders wichtig“ ---------------------------------
Ein Kästchen von 17 Pixeln ist auf dem Handy kaum zu treffen und sagt
nicht, was passiert. Der Schalter ist groß, zeigt seinen Zustand und
erklärt sich in einer Zeile. */
.schalter {
display: flex; align-items: flex-start; gap: 14px;
margin-top: 18px; padding: 14px 16px; cursor: pointer;
border: 1px solid var(--rand); border-radius: var(--radius);
background: var(--flaeche);
transition: border-color .18s ease, background .18s ease;
}
.schalter:hover { border-color: rgba(255, 255, 255, .2); }
.schalter input { position: absolute; opacity: 0; width: 0; height: 0; }
.schalter__gleis {
flex: none; position: relative; width: 42px; height: 24px; margin-top: 1px;
border-radius: 999px;
background: rgba(255, 255, 255, .1);
border: 1px solid var(--rand);
transition: background .2s ease, border-color .2s ease;
}
.schalter__punkt {
position: absolute; left: 3px; top: 3px; width: 16px; height: 16px;
border-radius: 50%; background: var(--text-still);
transition: transform .2s ease, background .2s ease;
}
.schalter input:checked + .schalter__gleis {
background: rgba(232, 192, 125, .3);
border-color: rgba(232, 192, 125, .6);
}
.schalter input:checked + .schalter__gleis .schalter__punkt {
transform: translateX(18px); background: #e8c07d;
}
.schalter input:focus-visible + .schalter__gleis {
box-shadow: 0 0 0 3px rgba(232, 192, 125, .28);
}
.schalter:has(input:checked) {
border-color: rgba(232, 192, 125, .35);
background: rgba(232, 192, 125, .05);
}
.schalter__text { display: grid; gap: 2px; font-size: .88rem; }
.schalter__text .leise { font-size: .79rem; }