Files
dogfather-universe/workspace/assets/css/start.css
T
DogFatherGitandClaude Opus 5 e74254263f Buehne aus den echten Marken-Bildern, Kopfleiste und Begruessung neu
Filipe hatte recht, und zwar zweifach: Der Husky war der FALSCHE (ein
fremder Neon-Husky aus dem alten Bilderordner statt DogFather), und zwei
in die Ecken geklebte Bilder ergeben noch kein Bild.

DIE BUEHNE, zweiter Anlauf. Aus den zehn geschickten Bildern das Studio
gewaehlt, in dem HasiDog und DogFather stehen -- nicht aus Geschmack,
sondern wegen der ANORDNUNG: Die beiden stehen weit aussen, die Mitte ist
dunkel. Genau dort steht der Inhalt. Bei den Bildern mit den Figuren in
der Mitte waeren sie hinter dem Text gelandet.

Ueber die ganze Flaeche, fest an der Scheibe, 138 % breit: Bei 100 %
staenden die beiden bei 20 % und 78 % der Breite -- mitten unter der
Textspalte. Vergroessert man das Bild ueber die Scheibe hinaus, wandern
sie nach aussen (rund 9 % und 88 %) und der leere Hallenboden liegt dort,
wo gelesen wird. Ein Bild groesser zu machen, damit WENIGER davon im Weg
ist, klingt verkehrt und ist genau richtig.

Fuer schmale Schirme ein eigener Hochkant-Ausschnitt derselben Szene --
ein auf 9:16 gequetschtes Breitbild zeigt nur noch Wand.
1,9 MB PNG -> 28 KB WebP.

DIE VIER STELLEN aus den Bildschirmfotos:

* Kopfleiste: der DogFather-Kopf davor. Als MASKE aus dem Logo gerechnet
  (Dunkelheit wird Deckkraft), nicht als Bild -- so laesst er sich im
  Stil einfaerben; ein fertig eingefaerbtes Bild muesste man fuer jede
  Farbe neu bauen. 114 KB -> 3 KB.

* "Dogfather · DogFather" war der peinlichste Punkt: Name und Rolle lasen
  sich gleich, es sah nach einem Fehler aus. Jetzt drei unterscheidbare
  Dinge -- Zeichen mit dem Anfangsbuchstaben, Name, Rolle als Marke in
  ihrer Farbe. Gebaut in kopf.js, EINMAL statt vierzehnmal: Jede
  Seitendatei setzte diese Zeile bisher selbst zusammen.

* Begruessung: leuchtender Strich, groesserer Gruss, auslaufende
  Trennlinie. Dieselben Striche vor "WAS IST DRAN" und "DEINE AUFGABEN" --
  so gehoert sichtbar zusammen, was zusammengehoert.

ZWEI EIGENE FEHLER, die die Pruefungen gefunden haben:

1. Ich hatte Verlaeufe IM TEXT gebaut (background-clip mit durchsichtiger
   Schrift). Sah gut aus, und der Kontrasttest meldete 1,05:1 -- zu Recht.
   Durchsichtige Schrift haengt an einer einzigen Technik; faellt sie aus
   (Kontrastmodus, Druck, aeltere Browser), ist der Text UNSICHTBAR statt
   nur anders gefaerbt. Das ist kein Schoenheitsfehler, das ist ein
   Ausfall. Jetzt feste Farbe mit einem Hauch Leuchten.

2. Auf dem Handy stand der Abmelden-Knopf 9 px aus dem Bild. Die Suche
   nach der Ursache ging zuerst in die Irre -- der Test nannte das
   Wasserzeichen, das aber von seiner Kachel sauber abgeschnitten wird
   und nur seine Masse meldet. Gemessen war es die Marke: 209 px + 139 px
   rechte Gruppe passten nicht in 390 px. Auf dem Handy bleibt jetzt nur
   der Kopf stehen; der Markenzug steht zwei Zeilen tiefer ohnehin als
   Ueberschrift. Fuer Vorleseprogramme bleibt er im Dokument.

Und einmal habe ich mir beim Ersetzen eines CSS-Blocks das halbe
Stylesheet geloescht (die Kachel-Regeln lagen zwischen den beiden
Suchmarken). Aufgefallen sofort am Bildschirmfoto, zurueckgeholt aus Git,
danach zeilengenau ersetzt.

118 Pruefungen, alle gruen. Der Kontrast wird weiterhin an echten
Bildpunkten gemessen, jetzt an bis zu 35 Stellen je Seite.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 05:45:59 +02:00

852 lines
34 KiB
CSS

/* ===================================================================
Startseite nach dem Anmelden. Baut auf den Farben und Bausteinen von
gate.css auf, damit beides wie eine Anwendung wirkt und nicht wie zwei
verschiedene Seiten.
=================================================================== */
/* Grundschrift und Grundfarbe stehen in gate.css auf `body.gate` und
greifen hier deshalb NICHT. Ohne diese Wiederholung fällt die Seite auf
die Browservorgaben zurück: schwarze Serifenschrift auf dunklem Grund,
praktisch unlesbar. */
body.start {
display: block;
min-height: 100svh;
margin: 0;
padding: 0;
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;
background:
radial-gradient(120% 80% at 12% -10%, rgba(63, 189, 245, .10), transparent 58%),
radial-gradient(100% 70% at 92% 8%, rgba(138, 118, 255, .10), transparent 60%),
var(--tinte);
}
/* ---------- Die Bühne ----------------------------------------------------
HasiDog und DogFather im Studio -- das echte Marken-Bild, nicht
irgendein Husky aus dem alten Bilderordner. Über die ganze Fläche und
FEST an der Scheibe: Es scrollt nicht mit, sondern steht da wie ein
Bühnenbild, vor dem gearbeitet wird.
Warum genau dieses von den fünf: Die beiden stehen weit AUSSEN, die
Mitte ist dunkel -- und dort steht der Inhalt. Ein Bild mit Figuren in
der Mitte wäre hinter dem Text gelandet.
Aufbereitet in tools/buehne-bauen.mjs: abgedunkelt, Mitte zusätzlich
verdunkelt, weiche Kante oben. 1,9 MB PNG -> 28 KB WebP.
138 % breit und nach unten ausgerichtet: Bei 100 % ständen die beiden
bei 20 % und 78 % der Breite -- also mitten unter der Textspalte.
Vergrößert man das Bild über die Scheibe hinaus, wandern sie nach
außen (auf rund 9 % und 88 %) und der leere Hallenboden liegt dort,
wo gelesen wird. Ein Bild größer zu machen, damit WENIGER davon im
Weg ist, klingt verkehrt und ist genau richtig. */
body.start::before {
content: "";
position: fixed; inset: 0; z-index: 0;
pointer-events: none;
background-image: url("../img/buehne-breit.webp");
background-repeat: no-repeat;
background-position: center bottom;
background-size: 138% auto;
}
/* Der Schleier. Er ist der Grund, warum Text über einem Foto unverändert
lesbar bleibt -- oben dicht für die Kopfleiste, in der Mitte dicht für
den Inhalt, an den Rändern offen, damit die beiden sichtbar bleiben.
Gemessen statt geschätzt: server/pruef-buehne.mjs rechnet den Kontrast
an echten Bildpunkten nach, an bis zu 34 Stellen je Seite. */
body.start > .schleier {
position: fixed; inset: 0; z-index: 0; pointer-events: none;
background:
linear-gradient(180deg, rgba(5, 7, 13, .95) 0%, rgba(5, 7, 13, .72) 13%,
rgba(5, 7, 13, .6) 48%, rgba(5, 7, 13, .8) 100%),
radial-gradient(56% 68% at 50% 46%, rgba(5, 7, 13, .8), transparent 84%);
}
/* Alles Bedienbare liegt über der Bühne. Ohne das läge der Inhalt zwar
sichtbar darüber, aber Klicks landeten teils am falschen Ort. */
body.start > .kopfleiste,
body.start > .inhalt,
body.start > main { position: relative; z-index: 1; }
/* Auf schmalen Schirmen ein hochkant-Ausschnitt derselben Szene -- ein
auf 9:16 gequetschtes Breitbild zeigt nur noch Wand. */
@media (max-width: 760px) {
body.start::before {
background-image: url("../img/buehne-schmal.webp");
background-size: cover;
}
body.start > .schleier {
background:
linear-gradient(180deg, rgba(5, 7, 13, .97) 0%, rgba(5, 7, 13, .85) 15%,
rgba(5, 7, 13, .82) 60%, rgba(5, 7, 13, .92) 100%);
}
}
/* ---------- Kopfleiste ------------------------------------------------ */
.kopfleiste {
position: sticky; top: 0; z-index: 5;
display: flex; align-items: center; justify-content: space-between; gap: 16px;
padding: 14px clamp(16px, 4vw, 40px);
background: rgba(6, 9, 16, .82);
backdrop-filter: blur(18px);
-webkit-backdrop-filter: blur(18px);
border-bottom: 1px solid var(--rand);
}
/* Damit in einer Flex-Zeile ueberhaupt etwas schrumpfen KANN, braucht es
min-width: 0. Ohne diese eine Zeile behielt jedes Teil seine volle
Wunschbreite -- Zurueck (118) + Marke (141) + rechte Gruppe (297)
ergaben 556 px auf einem 390 px breiten Handy. Die Kopfleiste schob
214 px aus dem Bild und der Abmelden-Knopf war dort nicht mehr zu
erreichen. Auf JEDER Seite, seit es die Kopfleiste gibt; aufgefallen
erst beim Messen, weil man am Rechner nie danebengreift. */
.kopfleiste > * { min-width: 0; }
.kopfleiste .marke {
margin: 0; font-size: .72rem;
/* Die Marke gibt als Erstes nach: Ihr Inhalt steht direkt darunter
noch einmal als Ueberschrift, der Abmelden-Knopf dagegen nirgends. */
flex: 1 1 auto;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Zurück-Knopf. Steht ganz links, weil dort jeder ihn erwartet -- gleiche
Stelle wie im Browser. Auf der Startseite gibt es ihn gar nicht: Dort
ist kein "davor", und ein Knopf, der nichts tut, ist schlimmer als
keiner. Auf den übrigen Seiten entscheidet kopf.js, ob er erscheint. */
/* Entwurf "Neon-Kante": Der senkrechte Lichtstrich links ist dasselbe
Motiv wie die Kante der Anmelde-Tafel (.tafel__kante in gate.css).
Dadurch wirkt der Workspace wie ein Stück und nicht wie zusammen-
gesetzte Teile. */
.zurueck-knopf {
position: relative;
flex: 0 0 auto;
display: inline-flex; align-items: center; gap: 10px;
padding: 9px 18px 9px 17px;
border: 0;
/* Links flach, rechts rund: Die gerade Kante gehört dem Lichtstrich,
nach rechts läuft der Knopf weich aus. */
border-radius: 4px 999px 999px 4px;
background: linear-gradient(100deg,
rgba(63, 189, 245, .10), rgba(138, 118, 255, .05) 60%, transparent);
color: var(--text-leise);
font: inherit; font-size: .84rem; font-weight: 600;
cursor: pointer; white-space: nowrap;
transition: color var(--tempo), background var(--tempo);
}
/* Der Lichtstrich. Ruht etwas kürzer als der Knopf und wächst beim
Überfahren auf volle Höhe -- die Bewegung ersetzt jedes Aufblitzen. */
.zurueck-knopf::before {
content: "";
position: absolute; left: 0; top: 6px; bottom: 6px; width: 2px;
border-radius: 2px;
background: linear-gradient(180deg, var(--akzent), var(--violett));
box-shadow: 0 0 8px -1px rgba(63, 189, 245, .55);
transition: top var(--tempo), bottom var(--tempo), box-shadow var(--tempo);
}
.zurueck-knopf__pfeil {
width: 15px; height: 15px; flex: 0 0 auto;
fill: none; stroke: currentColor;
stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
transition: transform var(--tempo);
}
.zurueck-knopf:hover {
color: #fff;
background: linear-gradient(100deg,
rgba(63, 189, 245, .18), rgba(138, 118, 255, .09) 60%, transparent);
}
.zurueck-knopf:hover::before {
top: 0; bottom: 0;
box-shadow: 0 0 14px 0 rgba(63, 189, 245, .75);
}
/* Der Pfeil rückt nach links -- sagt die Richtung ohne zusätzlichen Text. */
.zurueck-knopf:hover .zurueck-knopf__pfeil { transform: translateX(-3px); }
.zurueck-knopf:active { transform: translateY(1px); }
.zurueck-knopf:focus-visible {
outline: 2px solid var(--akzent);
outline-offset: 3px;
border-radius: 4px 999px 999px 4px;
}
@media (prefers-reduced-motion: reduce) {
.zurueck-knopf, .zurueck-knopf::before, .zurueck-knopf__pfeil { transition: none; }
.zurueck-knopf:hover .zurueck-knopf__pfeil { transform: none; }
}
.kopfleiste__rechts { display: flex; align-items: center; gap: 12px; flex: 0 1 auto; min-width: 0; }
.kopfleiste__rechts > * { flex: 0 0 auto; }
.wer {
font-size: .86rem; color: var(--text-leise);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 46vw;
/* Der Name darf kuerzer werden -- der Knopf daneben nicht. */
flex: 0 1 auto; min-width: 0;
}
/* ---------- Inhalt ---------------------------------------------------- */
.inhalt {
max-width: 880px;
margin: 0 auto;
padding: clamp(28px, 5vw, 56px) clamp(16px, 4vw, 40px) 64px;
}
/* Diese vier standen bis zum 31.08.2026 in aufgaben.css -- also in einer
Datei, die eine neue Seite gar nicht laden muss. Genau dadurch war das
Dashboard beim ersten Aufbau schmal und ohne Kopfzeilen-Layout: die
Klassen im HTML gab es, die Regeln dazu nicht. Derselbe Fehler ist
vorher schon zweimal passiert (.knopf-still, .schalter). Was auf
mehreren Seiten benutzt wird, gehoert hierher, wo jede Seite es hat. */
.inhalt--breit { max-width: 1240px; }
.zurueck { color: inherit; text-decoration: none; border-bottom: 1px solid transparent; }
.zurueck:hover { border-bottom-color: currentColor; }
.kopf-zeile {
display: flex; align-items: flex-end; justify-content: space-between;
gap: 20px; flex-wrap: wrap; margin-bottom: 24px;
}
.kopf-zeile .titel { margin: 0; font-size: clamp(1.4rem, 2.2vw, 1.85rem); }
.leise { color: var(--text-still); font-weight: 400; }
/* ---------- Auf- und Zuklappen ------------------------------------------
Lange Listen (Protokoll, Verlauf) stehen zugeklappt da und zeigen nur
die neuesten Zeilen. Der Pfeil dreht sich beim Aufklappen -- er zeigt
die Richtung, der Text daneben sagt, was der Knopf TUT.
Steht hier und nicht in einer Seiten-Datei: Er wird an mehreren
Stellen gebraucht, und genau so sind schon dreimal Regeln dort
gelandet, wo eine Seite sie gar nicht laedt. */
.klapp-kopf {
display: flex; align-items: center; justify-content: space-between;
gap: 12px; flex-wrap: wrap; margin-bottom: 8px;
}
.klapp-kopf > .feldschild, .klapp-kopf > .block__titel { margin: 0; }
.klapp-pfeil {
width: 14px; height: 14px; flex: 0 0 auto;
fill: none; stroke: currentColor;
stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
transition: transform var(--tempo);
}
[aria-expanded="true"] > .klapp-pfeil { transform: rotate(180deg); }
/* Zugeklappt bleiben die vier neuesten Zeilen stehen. Bewusst ueber CSS
statt ueber das Entfernen von Zeilen: Das Aufklappen braucht dann
weder einen neuen Aufbau noch eine zweite Abfrage. */
[data-klapp="zu"] > *:nth-child(n+5) { display: none; }
.willkommen { margin-bottom: 38px; }
.willkommen .titel { margin-bottom: 6px; }
/* ---------- Zahlen ---------------------------------------------------- */
.zahlen-block { margin-bottom: 34px; }
.zahlen {
list-style: none; margin: 0; padding: 0;
display: grid; gap: 10px;
grid-template-columns: repeat(auto-fit, minmax(122px, 1fr));
}
.zahl {
padding: 14px 16px;
background: rgba(255, 255, 255, .03);
border: 1px solid var(--rand);
border-radius: var(--radius-klein);
}
.zahl__wert { display: block; font-size: 1.7rem; font-weight: 700; line-height: 1.1; }
.zahl__name { display: block; margin-top: 2px; font-size: .78rem; color: var(--text-still); }
/* Nur wenn wirklich etwas überfällig ist, wird die Kachel hervorgehoben --
sonst stumpft die Farbe ab und man übersieht sie im Ernstfall. */
.zahl[data-warn="ja"] { border-color: rgba(255, 154, 162, .42); background: rgba(255, 154, 162, .07); }
.zahl[data-warn="ja"] .zahl__wert { color: var(--warn); }
/* ---------- Bereiche: Gruppen und Kacheln --------------------------------
Vorher: siebzehn gleiche Rechtecke mit "ÖFFNEN" darauf. Wenn alles
gleich aussieht, ist alles gleich wichtig -- also nichts.
Drei Dinge tragen jetzt die Gestaltung:
1. Die Kachel ist ruhig, bis man sie ansieht. Der Farbton sitzt im
Zeichen und in einer schmalen Kante -- nicht als Fläche. Siebzehn
farbige Flächen wären ein Bonbonglas, kein Werkzeug.
2. Beim Überfahren geht ein sehr weicher Schimmer aus der oberen
linken Ecke an. Kein Blitzen, kein Springen: Alphawerte unter 0.25,
Bewegung unter einem Pixel.
3. Der Einlauf ist gestaffelt -- genau EINE Bewegung beim Aufbau,
danach Ruhe. Bei "weniger Bewegung" fällt sie ersatzlos weg.
Die acht Farbtöne sind gegen genau diesen Hintergrund gerechnet:
Helligkeitsband, Farbstärke, Abstand bei Farbfehlsichtigkeit
(schlechtestes Paar dE 8.4), Normalsicht-Abstand (19.3) und Kontrast
-- alle Prüfungen bestanden. Warum acht und nicht siebzehn: Siebzehn
unterscheidbare Farben gibt es nicht; ein Versuch mit einer eigenen
Farbe je Kachel ergab Paare mit Abstand 1.4, also praktisch dieselbe
Farbe. Sie stützen nur -- WAS eine Kachel ist, sagen Zeichen und Name.
------------------------------------------------------------------------- */
.gruppe { margin-bottom: 34px; }
.gruppe:last-child { margin-bottom: 0; }
.gruppe__kopf {
position: relative;
display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
margin-bottom: 14px; padding-bottom: 10px;
/* Die Trennlinie läuft nach rechts aus, statt hart abzubrechen --
eine durchgezogene Linie über die ganze Breite zerschneidet die
Seite, eine auslaufende gliedert sie nur. */
border-bottom: 1px solid transparent;
border-image: linear-gradient(90deg, var(--rand-hell), transparent 70%) 1;
}
.gruppe__name {
margin: 0; font-size: .76rem; font-weight: 700;
letter-spacing: .1em; text-transform: uppercase; color: var(--text-leise);
}
.gruppe__unter { font-size: .78rem; color: var(--text-still); }
.kacheln {
list-style: none; margin: 0; padding: 0;
display: grid; gap: 12px;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
/* Siebzehn eigene Töne -- einer je Kachel. Nicht gewählt, sondern
gerechnet: gleichmäßig um den Farbkreis verteilt, in OKLCH bei
konstanter Helligkeit und Farbstärke, damit keine sich vordrängt.
Erzeugt von tools/kachel-farben.mjs; dort steht auch, warum.
Die ZUORDNUNG ist ebenfalls gerechnet. Das Auge vergleicht nur, was
NEBENEINANDER liegt -- gesucht wurde deshalb die Anordnung mit dem
größtmöglichen kleinsten Abstand zweier Nachbarn im Raster. Ergebnis:
mindestens 105,9 Grad zwischen allen Nachbarn. Zum Vergleich: Der
erste Versuch mit frei gewählten Farben hatte Paare bei 1,4 Grad.
Unter allen Anordnungen, die das schaffen, wurde die passendste
genommen -- LIVE rot, Technik gelb, Reports grün, Personen rot-gold.
Geprüft gegen genau diesen Hintergrund: Helligkeitsband, Farbstärke
und Kontrast bestehen für alle siebzehn. */
.kachel[data-ton="1"] { --ton: #3e95e7; } /* 250° */
.kachel[data-ton="2"] { --ton: #7089ea; } /* 271° */
.kachel[data-ton="3"] { --ton: #947de2; } /* 292° */
.kachel[data-ton="4"] { --ton: #b173d0; } /* 314° */
.kachel[data-ton="5"] { --ton: #c66ab5; } /* 335° */
.kachel[data-ton="6"] { --ton: #d56694; } /* 356° */
.kachel[data-ton="7"] { --ton: #dc666f; } /* 17° */
.kachel[data-ton="8"] { --ton: #db6c48; } /* 38° */
.kachel[data-ton="9"] { --ton: #d17717; } /* 59° */
.kachel[data-ton="10"] { --ton: #ba8610; } /* 81° */
.kachel[data-ton="11"] { --ton: #a29205; } /* 102° */
.kachel[data-ton="12"] { --ton: #809e23; } /* 123° */
.kachel[data-ton="13"] { --ton: #50a752; } /* 144° */
.kachel[data-ton="14"] { --ton: #07aa7c; } /* 165° */
.kachel[data-ton="15"] { --ton: #12a69b; } /* 186° */
.kachel[data-ton="16"] { --ton: #04a3b4; } /* 208° */
.kachel[data-ton="17"] { --ton: #059ece; } /* 229° */
.kachel {
--ton: var(--akzent);
/* Wo der Zeiger steht. Wird von start.js gesetzt; die Mitte ist der
Rückfallwert, damit ohne Zeiger nichts springt. */
--mx: 50%;
--my: 0%;
position: relative;
border-radius: 18px;
background:
linear-gradient(155deg,
color-mix(in srgb, var(--ton) 10%, transparent),
rgba(255, 255, 255, .022) 48%);
border: 1px solid var(--rand);
/* Die feine helle Linie an der Oberkante. Ein Pixel, kaum sichtbar --
aber die Kachel wirkt dadurch von oben beleuchtet statt aufgeklebt.
Das ist der Unterschied zwischen "Kasten" und "Objekt". */
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .055);
overflow: hidden;
transition: border-color var(--tempo), transform var(--tempo), box-shadow var(--tempo);
}
.kachel[data-gross="ja"] { grid-column: span 2; }
/* Die farbige Kante links. Sie ist das Einzige, was den Ton kräftig
zeigt -- drei Pixel reichen, um eine Kachel wiederzuerkennen. */
.kachel::before {
content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
background: linear-gradient(180deg, var(--ton), transparent 92%);
opacity: .55;
transition: opacity var(--tempo), width var(--tempo);
}
/* Das Licht. Es liegt schwach auf der Kachel und FOLGT beim Überfahren
dem Zeiger -- das ist die Kleinigkeit, die man nicht sieht, sondern
erst beim Benutzen merkt. Kein Blinken, keine Bewegung des Inhalts:
Es wird nur an einer anderen Stelle heller.
Ruhend sitzt es in der oberen linken Ecke (--my: 0%), damit die
Kacheln auch ohne Zeiger von einer Seite beleuchtet wirken. */
.kachel::after {
content: ""; position: absolute; inset: 0; pointer-events: none;
background: radial-gradient(130% 150% at var(--mx) var(--my),
color-mix(in srgb, var(--ton) 20%, transparent), transparent 60%);
opacity: .38;
transition: opacity var(--tempo);
}
.kachel:hover {
border-color: color-mix(in srgb, var(--ton) 45%, var(--rand));
transform: translateY(-2px);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, .09),
0 10px 26px -14px color-mix(in srgb, var(--ton) 55%, transparent);
}
.kachel:hover::before { opacity: 1; width: 4px; }
.kachel:hover::after { opacity: 1; }
.kachel:has(:focus-visible) { border-color: var(--ton); }
.kachel__link {
position: relative; z-index: 1;
display: flex; align-items: center; gap: 15px;
padding: 21px 20px 21px 23px;
color: inherit; text-decoration: none;
}
/* Das Wasserzeichen. Dasselbe Zeichen wie im Feld links, aber riesig,
angeschnitten und fast unsichtbar in der rechten unteren Ecke.
Es ist der Grund, warum siebzehn Kacheln nicht mehr wie siebzehn
Kästen aussehen: Jede bekommt eine eigene große Form, ohne dass ein
einziges zusätzliches Bild geladen wird. Bewusst so schwach, dass man
es nicht liest, sondern nur spürt -- eine Kachel soll ihren Namen
tragen, nicht ihr Zeichen. */
.kachel__wasserzeichen {
position: absolute; right: -14px; bottom: -26px;
width: 132px; height: 132px;
pointer-events: none; z-index: 0;
opacity: .07;
transform: rotate(-8deg);
transition: opacity var(--tempo), transform var(--tempo);
}
.kachel__wasserzeichen svg {
width: 100%; height: 100%;
fill: none; stroke: var(--ton);
stroke-width: 1.1; stroke-linecap: round; stroke-linejoin: round;
}
.kachel:hover .kachel__wasserzeichen {
opacity: .13;
transform: rotate(-8deg) translate(-4px, -4px) scale(1.04);
}
.kachel[data-gross="ja"] .kachel__wasserzeichen {
width: 168px; height: 168px; right: -18px; bottom: -34px;
}
.kachel__link:focus-visible {
outline: 2px solid var(--ton); outline-offset: -4px; border-radius: 15px;
}
/* Das Zeichen sitzt in einem eigenen Feld -- dadurch bekommt der Ton
eine Fläche, ohne dass die ganze Kachel farbig wird. Der Verlauf und
der innere Lichtrand darin lassen es geprägt statt gemalt wirken. */
.kachel__zeichen {
position: relative; z-index: 1;
flex: none;
display: grid; place-items: center;
width: 52px; height: 52px; border-radius: 15px;
background:
linear-gradient(160deg,
color-mix(in srgb, var(--ton) 30%, transparent),
color-mix(in srgb, var(--ton) 10%, transparent));
border: 1px solid color-mix(in srgb, var(--ton) 32%, transparent);
/* Lichtkante innen oben, weicher Schein nach aussen -- dadurch wirkt
das Feld geprägt statt gemalt. Der Schein bleibt schwach; ein
leuchtender Kasten wäre in einer dunklen Oberfläche eine Lampe. */
box-shadow:
inset 0 1px 0 color-mix(in srgb, var(--ton) 38%, transparent),
0 4px 14px -8px color-mix(in srgb, var(--ton) 60%, transparent);
transition: background var(--tempo), border-color var(--tempo),
transform var(--tempo), box-shadow var(--tempo);
}
.kachel:hover .kachel__zeichen {
background:
linear-gradient(160deg,
color-mix(in srgb, var(--ton) 46%, transparent),
color-mix(in srgb, var(--ton) 16%, transparent));
border-color: color-mix(in srgb, var(--ton) 62%, transparent);
transform: scale(1.05);
box-shadow:
inset 0 1px 0 color-mix(in srgb, var(--ton) 55%, transparent),
0 6px 20px -8px color-mix(in srgb, var(--ton) 85%, transparent);
}
.kachel[data-gross="ja"] .kachel__zeichen { width: 62px; height: 62px; border-radius: 18px; }
.kachel__svg {
width: 25px; height: 25px;
fill: none; stroke: var(--ton);
stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.kachel[data-gross="ja"] .kachel__svg { width: 31px; height: 31px; }
.kachel__text { position: relative; z-index: 1; display: grid; gap: 3px; min-width: 0; }
.kachel__name {
font-size: 1.06rem; font-weight: 650; letter-spacing: -.014em; line-height: 1.25;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kachel[data-gross="ja"] .kachel__name { font-size: 1.24rem; }
.kachel__unter {
font-size: .78rem; color: var(--text-still); line-height: 1.3;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kachel[data-gross="ja"] .kachel__unter { font-size: .83rem; }
/* Die Zahl auf der Kachel. Sie ersetzt den Pfeil, wenn dort Arbeit
liegt -- eine Kachel, die "3" sagt, ist zehnmal mehr wert als eine,
die "öffnen" sagt. */
.kachel__zahl {
position: relative; z-index: 1;
flex: none; margin-left: auto;
min-width: 32px; padding: 5px 11px;
text-align: center; font-size: .9rem; font-weight: 700;
font-variant-numeric: tabular-nums;
color: var(--ton);
background: color-mix(in srgb, var(--ton) 15%, transparent);
border: 1px solid color-mix(in srgb, var(--ton) 36%, transparent);
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ton) 26%, transparent);
border-radius: 999px;
transition: background var(--tempo), border-color var(--tempo);
}
.kachel:hover .kachel__zahl {
background: color-mix(in srgb, var(--ton) 24%, transparent);
border-color: color-mix(in srgb, var(--ton) 55%, transparent);
}
.kachel__zahl[data-warn="ja"] {
color: var(--warn);
background: rgba(255, 154, 162, .13);
border-color: rgba(255, 154, 162, .44);
box-shadow: inset 0 1px 0 rgba(255, 154, 162, .28);
}
.kachel:hover .kachel__zahl[data-warn="ja"] {
background: rgba(255, 154, 162, .2); border-color: rgba(255, 154, 162, .62);
}
.kachel__pfeil {
position: relative; z-index: 1;
flex: none; margin-left: auto;
color: var(--text-still); font-size: 1rem;
transition: color var(--tempo), transform var(--tempo);
}
.kachel:hover .kachel__pfeil { color: var(--ton); transform: translateX(4px); }
/* Gestaffelter Einlauf. Die Verzögerung kommt aus --nr, das start.js je
Kachel setzt. Steht in einer no-preference-Abfrage, damit bei
"weniger Bewegung" gar keine Animation entsteht -- nicht nur eine
ohne Dauer. */
@media (prefers-reduced-motion: no-preference) {
.kachel {
animation: kachel-ein .38s cubic-bezier(.32, 0, .2, 1) backwards;
animation-delay: calc(var(--nr, 0) * 26ms);
}
@keyframes kachel-ein {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: none; }
}
}
.zahlen-leer {
margin: 0; padding: 15px 17px;
font-size: .88rem; color: var(--text-still);
border: 1px dashed var(--rand); border-radius: var(--radius-klein);
}
@media (max-width: 900px) {
.kacheln { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.kachel[data-gross="ja"] { grid-column: span 2; }
}
@media (max-width: 560px) {
.kacheln { grid-template-columns: 1fr; }
.kachel[data-gross="ja"] { grid-column: span 1; }
.gruppe__kopf { gap: 4px; }
.gruppe__unter { flex-basis: 100%; }
}
.inhalt .fuss { margin-top: 40px; text-align: left; }
@media (max-width: 520px) {
.kopfleiste { padding: 12px 16px; gap: 10px; }
/* Die Marke wich zwar zurueck, nahm aber immer noch so viel Platz,
dass vom Namen nur noch drei Pixel uebrig blieben. Auf dem Handy
verschwindet sie deshalb -- genau derselbe Text steht zwei Zeilen
tiefer als Ueberschrift, der Name des Angemeldeten dagegen
nirgends sonst.
Aber nur dort, wo der Zurueck-Knopf sichtbar ist: Auf der
Startseite gibt es ihn nicht, dort waere die linke Haelfte sonst
leer und die Leiste saehe kaputt aus. */
.kopfleiste:has(.zurueck-knopf:not([hidden])) .marke { display: none; }
.kopfleiste__rechts { margin-left: auto; }
.wer { max-width: none; }
/* Der Pfeil allein sagt "zurueck" -- das Wort daneben kostet auf dem
Handy 65 px, die dem Abmelden-Knopf fehlen. Vorlesende Programme
verlieren nichts: Der Text bleibt im Dokument und wird nur
unsichtbar gerechnet. */
.zurueck-knopf { padding: 9px 12px 9px 11px; }
.zurueck-knopf__text {
position: absolute; width: 1px; height: 1px;
margin: -1px; padding: 0; overflow: hidden;
clip-path: inset(50%); white-space: nowrap;
}
.zurueck-knopf { gap: 0; }
}
/* Zurueckhaltender Zweitknopf. Gehoert bewusst hierher und nicht in eine
einzelne Seite: Er wird ueberall gebraucht, und als Seiten-CSS fehlte er
prompt dort, wo die Datei nicht geladen war -- dann standen nackte
Systemknoepfe in der Oberflaeche. */
/* ---------- Was ist dran ------------------------------------------------
Steht vor den Zahlen. Zahlen sagen, WIE VIEL anliegt -- diese Liste
sagt, WAS zu tun ist. Nur drei Stufen, und nur die oberste ist farbig:
Waere alles hervorgehoben, waere nichts hervorgehoben. */
/* Die Seite ist breit, damit die Kacheln Platz haben. Eine Zeile Text
ueber 1240 px zu ziehen macht sie aber nicht lesbarer, sondern
anstrengender -- Hinweise und Begruessung behalten deshalb ihr Mass. */
.willkommen, .dran__liste, .inhalt .fuss { max-width: 940px; }
.dran { margin-bottom: 30px; }
.dran__liste { display: grid; gap: 7px; margin: 0; padding: 0; list-style: none; }
.dran__punkt {
position: relative;
border: 1px solid var(--rand);
border-radius: var(--radius-klein);
background: rgba(255, 255, 255, .028);
transition: border-color .18s ease, background .18s ease;
}
.dran__punkt::before {
content: ""; position: absolute; left: 0; top: 9px; bottom: 9px; width: 2px;
border-radius: 0 2px 2px 0;
background: var(--rand);
}
.dran__punkt[data-stufe="warnung"] {
border-color: rgba(255, 154, 162, .3);
background: rgba(255, 154, 162, .055);
}
.dran__punkt[data-stufe="warnung"]::before { background: var(--warn); }
.dran__punkt[data-stufe="offen"]::before { background: var(--akzent); opacity: .7; }
.dran__punkt[data-stufe="ruhig"] { opacity: .78; }
.dran__punkt:hover { border-color: rgba(255, 255, 255, .2); background: rgba(255, 255, 255, .05); }
.dran__punkt[data-stufe="ruhig"]:hover { opacity: 1; }
.dran__link {
display: flex; align-items: center; justify-content: space-between; gap: 14px;
padding: 12px 15px 12px 18px;
color: var(--text); text-decoration: none; font-size: .9rem;
}
.dran__punkt[data-stufe="warnung"] .dran__text { font-weight: 600; }
.dran__pfeil {
color: var(--text-still); font-size: .95rem;
transition: transform .18s ease, color .18s ease;
}
.dran__link:hover .dran__pfeil { color: var(--akzent); transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) {
.kachel__wasserzeichen, .kachel__zeichen { transition: none; }
.kachel:hover .kachel__wasserzeichen { transform: rotate(-8deg); }
.kachel:hover .kachel__zeichen { transform: none; }
.dran__punkt, .dran__pfeil { transition: none; }
.dran__link:hover .dran__pfeil { transform: none; }
}
/* =====================================================================
Kopfleiste, Begrüßung, Hinweise -- der Feinschliff vom 01.09.2026.
Vorher waren das vier Stellen mit reinem Systemtext: eine Marke in
Großbuchstaben, eine Überschrift, ein Name, eine Liste. Alles
funktionierte, nichts sagte etwas über den Ort, an dem man ist.
===================================================================== */
/* ---------- Die Marke oben links ----------------------------------------
Der DogFather-Kopf davor. Als MASKE, nicht als Bild: Die Form wird im
Stil eingefärbt (siehe tools/buehne-bauen.mjs). Ein fertig
eingefärbtes Bild müsste man für jede Farbe neu bauen -- und beim
Überfahren wechselt die Farbe. */
.kopfleiste .marke {
display: flex; align-items: center; gap: 9px;
letter-spacing: .14em;
/* min-width: 0 muss auch INNEN gelten. Die Marke ist jetzt selbst eine
Flex-Zeile (Zeichen + Text); ohne diese Angabe behaelt ihr Text seine
volle Wunschbreite und die Marke schrumpft nicht mehr -- der
Abmelden-Knopf stand dadurch auf dem Handy 9 px aus dem Bild.
Derselbe Fehler wie im August, eine Ebene tiefer. */
min-width: 0;
}
.kopfleiste .marke__text { min-width: 0; flex: 1 1 auto; }
.kopfleiste .marke::before {
content: "";
flex: none; width: 22px; height: 22px;
background: linear-gradient(150deg, var(--akzent), var(--violett));
-webkit-mask: url("../img/marke-husky.webp") center / contain no-repeat;
mask: url("../img/marke-husky.webp") center / contain no-repeat;
transition: filter var(--tempo);
}
.kopfleiste:hover .marke::before { filter: brightness(1.25); }
/* KEIN Farbverlauf IM Text.
Ich hatte das zuerst gebaut (background-clip: text mit durchsichtiger
Schrift) -- es sah gut aus, und der eigene Kontrasttest schlug sofort
Alarm: 1,05:1. Zu Recht. Durchsichtige Schrift ist auf eine einzige
Technik angewiesen; faellt sie aus (Kontrastmodus des Betriebssystems,
Druck, aeltere Browser), ist der Text UNSICHTBAR statt nur anders
gefaerbt. Das ist kein Schoenheitsfehler, das ist ein Ausfall.
Stattdessen: feste Farbe, ein Hauch Leuchten. Sieht fast gleich aus
und kann nicht verschwinden. */
.kopfleiste .marke__text {
color: #a8dbfb;
text-shadow: 0 0 14px rgba(63, 189, 245, .28);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* ---------- Wer angemeldet ist ------------------------------------------
Vorher stand da "Dogfather · DogFather" -- Name und Rolle lasen sich
gleich und es sah nach einem Fehler aus. Jetzt: ein Zeichen mit dem
Anfangsbuchstaben, der Name, und die Rolle als eigene Marke in ihrer
Farbe. Drei verschiedene Dinge sehen jetzt auch verschieden aus. */
.wer {
display: flex; align-items: center; gap: 9px;
max-width: none; overflow: visible;
}
.wer__zeichen {
flex: none;
display: grid; place-items: center;
width: 28px; height: 28px; border-radius: 9px;
font-size: .8rem; font-weight: 700; color: #eaf3ff;
background: linear-gradient(150deg,
color-mix(in srgb, var(--rollen-ton) 62%, transparent),
color-mix(in srgb, var(--rollen-ton) 24%, transparent));
border: 1px solid color-mix(in srgb, var(--rollen-ton) 55%, transparent);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16);
}
.wer__name {
font-size: .88rem; font-weight: 600; color: var(--text);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
min-width: 0;
}
.wer__rolle {
flex: none;
padding: 2px 9px; border-radius: 999px;
font-size: .66rem; font-weight: 700;
letter-spacing: .08em; text-transform: uppercase;
color: color-mix(in srgb, var(--rollen-ton) 75%, #ffffff);
background: color-mix(in srgb, var(--rollen-ton) 15%, transparent);
border: 1px solid color-mix(in srgb, var(--rollen-ton) 38%, transparent);
}
/* Eine Farbe je Rolle -- dieselbe Sprache wie in der Personenliste. */
.wer[data-rolle="admin"] { --rollen-ton: #e8c07d; }
.wer[data-rolle="manager"] { --rollen-ton: #9085e9; }
.wer[data-rolle="scout"] { --rollen-ton: #4fc38f; }
.wer[data-rolle="creator"] { --rollen-ton: #3fbdf5; }
/* Auf dem Handy faellt die Rollen-Marke weg und der Name darf schrumpfen.
Ohne das schob die neue Kennung die Kopfleiste um 8 px aus dem Bild --
gemessen, nicht vermutet: Der Pruefflauf zaehlt den waagerechten
Ueberstand auf jeder Seite mit.
Die Rolle wegzulassen kostet hier nichts: Wer angemeldet ist, weiss,
welche Rolle er hat -- und auf der Startseite steht sie zwei Zeilen
tiefer noch einmal ausgeschrieben. */
@media (max-width: 620px) {
.wer__rolle { display: none; }
.wer { min-width: 0; gap: 7px; }
.wer__name { max-width: 34vw; }
/* Auf dem Handy bleibt vom Markenzug nur der Kopf stehen. Gemessen:
Marke 209 px + rechte Gruppe 139 px + Polsterung passten nicht in
390 px, der Abmelden-Knopf stand 9 px aus dem Bild. Schrumpfen
allein reichte nicht.
Der Text kostet hier nichts: Er steht auf jeder Seite zwei Zeilen
tiefer noch einmal als Ueberschrift. Der Kopf dagegen sagt in 22 px,
wo man ist -- und bleibt.
Vorlesende Programme verlieren nichts: Der Text bleibt im Dokument
und wird nur unsichtbar gerechnet. */
.kopfleiste .marke__text {
position: absolute; width: 1px; height: 1px;
margin: -1px; padding: 0; overflow: hidden;
clip-path: inset(50%); white-space: nowrap;
}
.kopfleiste .marke { flex: none; }
}
/* ---------- Die Begrüßung ------------------------------------------------
Aus drei gestapelten Zeilen wird ein Kopf: ein leuchtender Strich, die
Zeile "ANGEMELDET" daneben, darunter der Gruß mit einem sehr sanften
Verlauf. Kein zusätzliches Element -- der Strich ist ein
Pseudo-Element der Marke. */
.willkommen .marke {
display: flex; align-items: center; gap: 10px;
margin-bottom: 10px;
}
.willkommen .marke::before {
content: "";
width: 26px; height: 2px; border-radius: 2px;
background: linear-gradient(90deg, var(--akzent), transparent);
}
.willkommen .titel {
font-size: clamp(1.9rem, 3.4vw, 2.6rem);
letter-spacing: -.025em;
/* Auch hier bewusst eine feste Farbe statt eines Verlaufs im Text --
siehe die Begruendung bei .marke__text. Die Wirkung kommt aus
Groesse, engerem Satz und dem Leuchten, nicht aus einem Kniff, der
im Kontrastmodus zu unsichtbarer Schrift wird. */
color: #f2f7fd;
text-shadow: 0 2px 26px rgba(63, 189, 245, .18);
}
.willkommen .unterzeile { margin-bottom: 0; }
/* Die Trennlinie unter der Begrüßung. Läuft nach rechts aus, statt hart
abzubrechen -- eine durchgezogene Linie zerschneidet die Seite. */
.willkommen {
padding-bottom: 22px; margin-bottom: 30px;
border-bottom: 1px solid transparent;
border-image: linear-gradient(90deg, var(--rand-hell), transparent 62%) 1;
}
/* ---------- Abschnittsschilder ------------------------------------------
"WAS IST DRAN" und "DEINE AUFGABEN" bekommen denselben leuchtenden
Strich wie die Begrüßung -- so gehört sichtbar zusammen, was
zusammengehört. */
.dran .feldschild,
.zahlen-block .feldschild {
display: flex; align-items: center; gap: 9px;
}
.dran .feldschild::before,
.zahlen-block .feldschild::before {
content: "";
width: 18px; height: 2px; border-radius: 2px;
background: linear-gradient(90deg, var(--akzent), transparent);
opacity: .8;
}