Wunsch vom 28.08.2026: "die dateien sollen nur die manager oder scout
hochladen koennen aber nicht die creator."
Serverseitig ueber DARF_HOCHLADEN. Die Pruefung steht bewusst VOR
express.raw -- wer nicht darf, wird abgewiesen, bevor auch nur ein Byte
entgegengenommen wird. Sonst wanderten bis zu 25 MB durch die Leitung,
nur um danach verworfen zu werden.
Ein Creator sieht und oeffnet weiterhin die Dateien seines Bereichs.
Statt des Ablegefeldes steht dort jetzt, warum es fehlt -- ein einfach
verschwundenes Feld wirkt wie ein Fehler.
Geprueft: Management 201, Scout 201, Creator 403 mit klarer Meldung;
Creator sieht und laedt weiterhin.
--------------------------------------------------------------------
Dabei ein Fehler aufgefallen, der mehrere Seiten betraf:
Das hidden-Attribut blendet nur ueber eine sehr schwache Browserregel
aus -- JEDE eigene display-Angabe schlaegt sie. Betroffen waren:
* das Ablegefeld fuer Dateien (display: grid) -> ein Creator sah es
trotz hidden weiterhin
* die Verwaltungsfelder "Plan-Start" und "Naechster Review" im
Creator-Profil (ebenfalls grid) -> ein Creator sah sie ebenfalls
* der Zurueck-Knopf (inline-flex) -> waere vor dem Laden des Skripts
kurz sichtbar gewesen
Behoben mit einer Regel in gate.css: [hidden] { display: none !important }
Beim Profil waren nie Daten betroffen -- der Server schickt die Felder
an einen Creator gar nicht erst mit, sie waren leer. Sichtbar sein
sollten sie trotzdem nicht.
Aufgefallen ist es erst im Screenshot. Der vorige Test hatte auf das
hidden-ATTRIBUT geprueft, und das war ja gesetzt. Die Pruefung schaut
jetzt auf die tatsaechliche Sichtbarkeit (isVisible).
Ausserdem: In der Regex zum Saeubern von Dateinamen standen die
Steuerzeichen als rohe Bytes statt als Escape-Sequenz. Die Funktion
arbeitete korrekt, aber die Datei galt dadurch als binaer -- grep
verweigerte sie, und Editoren haetten sie zerstoeren koennen. Jetzt
steht dort \x00-\x1f als Text.
382 lines
14 KiB
CSS
382 lines
14 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);
|
|
--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;
|
|
--text-still: #6d7d92;
|
|
|
|
--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 {
|
|
position: relative;
|
|
width: 100%; margin-top: 18px; padding: 13px 18px;
|
|
background: linear-gradient(100deg, var(--akzent-tief), #2f6fa8 55%, var(--violett));
|
|
border: 0; border-radius: var(--radius-klein);
|
|
color: #f2f7fc; font: inherit; font-weight: 650; letter-spacing: .02em;
|
|
cursor: pointer;
|
|
transition: filter var(--tempo), transform var(--tempo);
|
|
}
|
|
.knopf:hover { filter: brightness(1.1); }
|
|
.knopf:active { transform: scale(.99); }
|
|
.knopf[disabled] { cursor: progress; filter: saturate(.6) brightness(.85); }
|
|
|
|
.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; }
|
|
}
|