/* =========================================================================
   KOMPONENTEN

   Aufbau der Datei:
     1. Platzhalter-Markierung        7. Zweispalter
     2. Knöpfe                        8. Zeitleiste
     3. Kopf und Navigation           9. Stammbaum
     4. Hero                         10. Projekte, Filter, Lightbox
     5. Traditionsband               11. Kontakt und Formular
     6. Leistungsraster              12. Fuß
   ========================================================================= */

:root {
  --kopf-hoehe: 72px;
  --leiste-hoehe: 0px;   /* wird unter 1000px auf 60px gesetzt, s. Abschnitt 3 */
}


/* =========================================================================
   0. RASTERELEMENTE DÜRFEN SCHRUMPFEN

   Ein Element in einem Grid hat von sich aus `min-width: auto`. Es wird
   dadurch nie schmaler als sein längstes Wort — und lange deutsche
   Komposita in Versalien sind sehr lang: „SCHIMMELSANIERUNG" misst als
   Kachelüberschrift rund 230 Pixel.

   Auf einem 480px breiten Display sprengt das die zweispaltige Kachelreihe
   und schiebt die ganze Seite seitlich hinaus. `hyphens: auto` hilft
   dagegen nicht: Weiche Trennstellen werden bei der Berechnung der
   Mindestbreite nicht berücksichtigt.

   `min-width: 0` hebt die Sperre auf. Zusammen mit dem `overflow-wrap`
   aus basis.css bricht das Wort dann um, statt überzustehen.
   ========================================================================= */

.leistungen > li,
.projektraster > li,
.kontaktwege > li,
.zeitleiste--band > li,
.zweispalter > *,
.kennzahlen > *,
.fuss__raster > * {
  min-width: 0;
}


/* =========================================================================
   1. PLATZHALTER-MARKIERUNG

   Jede noch fehlende Information steht als doppelt eckig geklammerter
   PH-Marker im Text (die Schreibweise steht in tools/check-platzhalter.mjs
   und wird hier bewusst nicht wiederholt, sonst zählt das Werkzeug diesen
   Kommentar als offene Stelle mit) und ist in
   einen <span class="ph"> gefasst. Dadurch ist sie im Browser sofort
   sichtbar und lässt sich trotzdem mit `node tools/check-platzhalter.mjs`
   maschinell finden.

   Beim Ausfüllen wird der komplette <span> gelöscht und durch den echten
   Text ersetzt — nicht nur der Inhalt.
   ========================================================================= */

.ph {
  display: inline;
  padding: 0.1em 0.35em;
  background: repeating-linear-gradient(
    -45deg,
    #FFF3CC, #FFF3CC 6px,
    #FFE79A 6px, #FFE79A 12px
  );
  border: 1px dashed #B8860B;
  border-radius: var(--radius-s);
  color: #4A3800;
  font-size: 0.9em;
  font-style: normal;
  font-weight: 600;
  text-decoration: none;
}

/* Auch auf dunklem Grund lesbar halten. */
.sektion--dunkel .ph,
.fuss .ph {
  background: #FFE79A;
  color: #3A2C00;
}


/* =========================================================================
   2. KNÖPFE

   Regel aus der Kontrastprüfung: Auf Gelb steht immer Schwarz.
   Weiß auf Gelb erreicht nur 1,75 : 1 und ist damit unzulässig.
   ========================================================================= */

.knopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--abstand-2xs);
  /* 48px Mindesthöhe — die von den WCAG empfohlene Zielgröße für Finger. */
  min-height: 48px;
  padding: var(--abstand-xs) var(--abstand-m);
  border: 2px solid transparent;
  border-radius: var(--radius-m);
  font-family: var(--schrift-headline);
  font-size: var(--schrift-m);
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  box-shadow: var(--schatten-knopf);
  transition: background-color var(--dauer-schnell) var(--verlauf),
              border-color var(--dauer-schnell) var(--verlauf),
              color var(--dauer-schnell) var(--verlauf),
              box-shadow var(--dauer-mittel) var(--verlauf),
              transform var(--dauer-schnell) var(--verlauf);
}

/* Der Knopf steigt unter dem Zeiger von Höhenstufe 2 auf 3 und fällt beim
   Drücken unter beide zurück. Der Versatz begleitet den Schattenwechsel:
   ohne ihn sieht der größere Schatten nach einem Fehler aus, mit ihm nach
   einer Bewegung.

   Nur bei :hover, nicht bei :focus-visible — sonst wandert das Element
   unter dem Finger weg, während der Fokusring darauf zeigt. */
.knopf:hover {
  box-shadow: var(--schatten-knopf-hover);
  transform: translateY(-2px);
}

.knopf:active {
  box-shadow: var(--schatten-knopf-aktiv);
  transform: translateY(1px);
}

/* Auf dunklem Grund und über Bildern trägt kein schwarzer Schatten — er
   wäre entweder unsichtbar oder eine schmutzige Kante. Dort macht allein
   der weiße Rahmen die Abgrenzung. */
.knopf--invers,
.knopf--invers:hover,
.knopf--invers:active {
  box-shadow: none;
}

/* Der sekundäre Knopf ist anthrazit auf durchsichtig — auf schwarzem
   Grund wäre er praktisch unsichtbar.

   Diese Regel stellt ihn dort automatisch auf helle Kontur um. Der Grund
   ist Wartbarkeit, nicht Bequemlichkeit: Eine Sektion von hell auf dunkel
   umzustellen ist eine Klasse. Ohne diese Regel müsste man jedes Mal daran
   denken, auch jeden Knopf darin nachzuziehen — und irgendwann vergisst
   es jemand, ohne dass ein Werkzeug anschlägt. */
.sektion--dunkel .knopf--sekundaer,
.fuss .knopf--sekundaer {
  border-color: var(--farbe-linie-invers);
  color: var(--farbe-text-invers);
  box-shadow: none;
}

.sektion--dunkel .knopf--sekundaer:hover,
.fuss .knopf--sekundaer:hover {
  background: var(--farbe-grund);
  color: var(--farbe-text-stark);
}

.knopf--primaer {
  background: var(--farbe-akzent);
  border-color: var(--farbe-akzent);
  color: var(--farbe-text-stark);
}

.knopf--primaer:hover {
  background: var(--farbe-akzent-hover);
  border-color: var(--farbe-akzent-hover);
}

/* Umrandet, dunkle Schrift — für helle Flächen. */
.knopf--sekundaer {
  background: transparent;
  border-color: var(--farbe-linie-stark);
  color: var(--farbe-text);
}

.knopf--sekundaer:hover {
  background: var(--farbe-grund-dunkel-weich);
  color: var(--farbe-text-invers);
}

/* Umrandet, helle Schrift — für Bilder und dunkle Flächen. */
.knopf--invers {
  background: transparent;
  border-color: var(--farbe-linie-invers);
  color: var(--farbe-text-invers);
}

.knopf--invers:hover {
  background: var(--farbe-grund);
  color: var(--farbe-text-stark);
}

/* 44px und nicht 40px: Der kleine Knopf ist auf dem Handy sichtbar — „In
   Google Maps öffnen" auf der Kontaktseite benutzt ihn. 40px lag unter der
   Empfehlung von Apple und Google. */
.knopf--klein {
  min-height: 44px;
  padding: var(--abstand-3xs) var(--abstand-s);
  font-size: var(--schrift-s);
}

.knopf--breit {
  width: 100%;
}

.knopf__icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  fill: currentColor;
}

/* Eine Rufnummer wird nie umbrochen — „06781" über „33114" ist als
   Telefonnummer schlechter lesbar und wirkt wie ein Fehler. */
.kopf__telefon {
  white-space: nowrap;
}

/* Textlink mit Pfeil — „Mehr erfahren".

   Dieser Link bekommt bewusst KEINE Mindesthöhe von 44px, anders als die
   Links in Fußzeile und Brotkrumen. Grund ist der gelbe Strich: Er ist ein
   border-bottom und sitzt damit immer an der Unterkante des Kastens. Eine
   Mindesthöhe würde den Kasten vergrößern und den Strich vom Text ablösen —
   er schwebte dann zwei Zeilen tiefer.

   Vertretbar ist das, weil der Link gemessen 32px hoch ist (bei 1280px
   35px) und damit über der AA-Mindestgröße von 24px liegt (WCAG 2.2,
   Kriterium 2.5.8). Er steht außerdem immer allein in einem Absatz und hat
   über sich 2rem Abstand — verwechseln kann man ihn also nicht.

   _vorschau-fokus.html nimmt Links innerhalb von p und figcaption von der
   Zielgrößenprüfung aus — dieser fällt darunter. */
.weiterlink {
  display: inline-flex;
  align-items: center;
  gap: var(--abstand-3xs);
  font-family: var(--schrift-headline);
  font-weight: 700;
  text-decoration: none;
  border-bottom: 2px solid var(--farbe-akzent);
  padding-bottom: 2px;
}

.weiterlink:hover {
  border-bottom-color: currentColor;
}

.weiterlink::after {
  content: "→";
  transition: transform var(--dauer-schnell) var(--verlauf);
}

.weiterlink:hover::after {
  transform: translateX(3px);
}


/* =========================================================================
   3. KOPF UND NAVIGATION

   Der Kopf klebt oben. Die mobile Navigation ist mit <details>/<summary>
   gebaut und funktioniert dadurch vollständig ohne JavaScript. Das
   Skript in assets/js/navigation.js verbessert sie nur noch (Schließen
   mit ESC, Schließen beim Klick nach außen).
   ========================================================================= */

.kopf {
  position: sticky;
  top: 0;
  z-index: var(--ebene-kopf);
  background: var(--farbe-grund);
  border-bottom: var(--rahmen-staerke) solid var(--farbe-linie);
}

.kopf__zeile {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--abstand-m);
  min-height: var(--kopf-hoehe);
}

/* Mindestmaße nur für die Trefferfläche. Das Logo ist der Weg zurück zur
   Startseite und war bei 360px nur 37x38px groß — die Maße des Zeichens.

   Die Grafik selbst verschiebt sich dadurch nicht: In der Höhe wird sie in
   den 44px zentriert, in der Breite bleibt sie am linken Rand stehen, weil
   dieser Flex-Container ohne justify-content linksbündig ausrichtet. Die
   Fläche wächst also nur nach rechts in den Zwischenraum hinein. Oberhalb
   von 1100px steht die Wortmarke mit im Link, dort greift die Mindestbreite
   ohnehin nicht. */
.kopf__logo {
  display: flex;
  align-items: center;
  min-height: 44px;
  min-width: 44px;
  flex: none;
  text-decoration: none;
}

.kopf__logo svg {
  height: 46px;
  width: auto;
  flex: none;
}

/* Wortmarke neben dem Zeichen. Bewusst als Text und nicht als Teil des
   SVG: So bleibt sie durchsuchbar, skaliert mit den Systemeinstellungen
   und ist für Screenreader lesbar. */
.kopf__wortmarke {
  display: flex;
  flex-direction: column;
  margin-left: var(--abstand-2xs);
  line-height: 1.2;
}

.kopf__firma {
  font-family: var(--schrift-headline);
  font-size: var(--schrift-m);
  font-weight: 700;
  color: var(--farbe-text-stark);
  letter-spacing: var(--sperrung-eng);
}

.kopf__zusatz {
  font-size: var(--schrift-xs);
  color: var(--farbe-text-leise);
}

/* Der Kopf muss in einer Zeile bleiben. Zwischen 1000 und 1280 px steht
   noch die volle Navigation, gleichzeitig wird der Platz knapp — deshalb
   wird die Wortmarke stufenweise gekürzt, bevor bei 1000 px das
   Klappmenü übernimmt.

   Ohne diese Stufen läuft der Kopf bei 1024 px um rund 90 px über den
   rechten Rand hinaus und die ganze Seite lässt sich seitlich schieben. */
@media (max-width: 1280px) {
  .kopf__zusatz { display: none; }
  .navigation__liste { gap: var(--abstand-s); }
}

@media (max-width: 1100px) {
  .kopf__wortmarke { display: none; }
}

.kopf__rechts {
  display: flex;
  align-items: center;
  gap: var(--abstand-m);
}

/* --- Navigation ab Tablet --------------------------------------------- */

.navigation__liste {
  display: flex;
  align-items: center;
  gap: var(--abstand-m);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Die 44px fehlten hier um ein Viertelpixel — gemessen 43,75. Sichtbar ist
   das nicht, aber es hätte in jedem Durchlauf von _vorschau-fokus.html sechs
   Zeilen Rauschen erzeugt, und ein Prüfwerkzeug, dessen Ausgabe man
   überliest, prüft nichts. */
.navigation__link {
  display: block;
  min-height: 44px;
  padding: var(--abstand-2xs) 0;
  font-family: var(--schrift-headline);
  font-size: var(--schrift-s);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--sperrung-eng);
  text-decoration: none;
  border-bottom: var(--rahmen-stark) solid transparent;
}

.navigation__link:hover {
  border-bottom-color: var(--farbe-akzent);
}

/* aria-current markiert die aktuelle Position — für Screenreader hörbar
   und durch den gelben Balken sichtbar.
   "page" = genau diese Seite, "true" = eine Unterseite dieses Bereichs
   (etwa eine Leistungsdetailseite unter dem Menüpunkt „Leistungen"). */
.navigation__link[aria-current] {
  border-bottom-color: var(--farbe-akzent);
}

/* --- Mobile Klappnavigation ------------------------------------------- */

.mobilnav {
  display: none;
}

.mobilnav__schalter {
  display: flex;
  align-items: center;
  gap: var(--abstand-2xs);
  min-height: 48px;
  padding: var(--abstand-2xs) var(--abstand-xs);
  border: 2px solid var(--farbe-linie-stark);
  border-radius: var(--radius-m);
  font-family: var(--schrift-headline);
  font-size: var(--schrift-s);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--sperrung-eng);
  cursor: pointer;
  list-style: none;
}

/* Der Standard-Marker von <summary> wird in allen Browsern entfernt. */
.mobilnav__schalter::-webkit-details-marker { display: none; }
.mobilnav__schalter::marker { content: ""; }

.mobilnav__balken {
  position: relative;
  width: 20px;
  height: 2px;
  background: currentColor;
}

.mobilnav__balken::before,
.mobilnav__balken::after {
  content: "";
  position: absolute;
  left: 0;
  width: 20px;
  height: 2px;
  background: currentColor;
}

.mobilnav__balken::before { top: -6px; }
.mobilnav__balken::after  { top: 6px; }

.mobilnav[open] .mobilnav__balken {
  background: transparent;
}

.mobilnav[open] .mobilnav__balken::before {
  top: 0;
  transform: rotate(45deg);
}

.mobilnav[open] .mobilnav__balken::after {
  top: 0;
  transform: rotate(-45deg);
}

.mobilnav__panel {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  z-index: var(--ebene-mobilnav);
  background: var(--farbe-grund);
  border-top: var(--rahmen-staerke) solid var(--farbe-linie);
  border-bottom: var(--rahmen-stark) solid var(--farbe-akzent);
  box-shadow: var(--schatten-hoch);
  max-height: calc(100vh - var(--kopf-hoehe));
  overflow-y: auto;
}

.mobilnav__liste {
  list-style: none;
  margin: 0;
  padding: var(--abstand-s) var(--seitenrand) var(--abstand-m);
}

.mobilnav__liste li + li {
  border-top: var(--rahmen-staerke) solid var(--farbe-linie);
}

.mobilnav__liste a {
  display: block;
  padding: var(--abstand-s) 0;
  font-family: var(--schrift-headline);
  font-size: var(--schrift-l);
  font-weight: 600;
  text-decoration: none;
}

.mobilnav__liste a[aria-current] {
  color: var(--farbe-akzent-text);
  font-weight: 700;
}

/* --- Feste Anrufleiste unten (Daumenzone) ------------------------------ */

.mobilleiste {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--ebene-leiste);
  background: var(--farbe-grund-dunkel);
  /* Auf iPhones mit Home-Indikator nicht unter die Systemleiste rutschen. */
  padding-bottom: env(safe-area-inset-bottom, 0);
}

.mobilleiste__liste {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.mobilleiste__knopf {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--abstand-2xs);
  min-height: 60px;
  color: var(--farbe-text-invers);
  font-family: var(--schrift-headline);
  font-size: var(--schrift-s);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--sperrung-eng);
  text-decoration: none;
}

.mobilleiste__knopf--gelb {
  background: var(--farbe-akzent);
  color: var(--farbe-text-stark);
}

.mobilleiste__knopf svg {
  width: 20px;
  height: 20px;
  fill: currentColor;
}

/* Ab hier übernimmt das Klappmenü.

   Die Grenze liegt bei 1000px und nicht tiefer, weil darunter die volle
   Navigation zusammen mit Logo und Telefonknopf nicht mehr in eine Zeile
   passt. Gemessen: Bei 900px steht der Kopf um 36 Pixel über. Ein
   umbrechender Kopf oder eine seitlich verschiebbare Seite sind
   schlechter als ein Klappmenü. */
@media (max-width: 1000px) {
  :root { --leiste-hoehe: 60px; }

  .navigation { display: none; }
  .mobilnav { display: block; }
  .mobilleiste { display: block; }

  /* Der Kopf braucht hier keine Telefonnummer mehr — sie steht dauerhaft
     in der Leiste am unteren Displayrand. */
  .kopf__telefon { display: none; }
}

@media (max-width: 400px) {
  .kopf__logo svg { height: 38px; }
}


/* =========================================================================
   4. HERO
   ========================================================================= */

.hero {
  position: relative;
  display: grid;
  align-items: end;
  /* Mehr Präsenz als vorher (420–640px). Ein Hero, der kaum höher ist
     als der Abschnitt darunter, wirkt wie eine Kopfzeile mit Bild und
     nicht wie ein Auftakt. Nach oben durch 78vh begrenzt, damit auf
     kleinen Bildschirmen noch etwas vom Inhalt sichtbar bleibt. */
  min-height: clamp(480px, 78vh, 780px);
  background: var(--farbe-grund-dunkel);
  color: var(--farbe-text-invers);
  overflow: clip;
}

.hero__bild {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Fassaden- und Gebäudefotos haben ihr Motiv selten in der Bildmitte.
     Einzelne Seiten können den Ausschnitt über --hero-bildlage anpassen,
     ohne dass hier etwas geändert werden muss. */
  object-position: var(--hero-bildlage, center 38%);
}

/* Dunkler Schleier. Ohne ihn ist Weiß auf einem hellen Fassadenfoto nicht
   sicher lesbar — und der Kontrast muss stimmen, bevor das Motiv bekannt
   ist. Der Verlauf selbst steht als --hero-schleier in tokens.css, weil
   in dieser Datei kein Farbwert stehen darf. */
.hero__schleier {
  position: absolute;
  inset: 0;
  background: var(--hero-schleier);
}

.hero__inhalt {
  position: relative;
  padding-block: var(--abstand-2xl) var(--abstand-xl);
}

/* Der Traditionsanker war ein gelber Kasten. Gelb ist in dieser Seite aber
   die Farbe der Bedienelemente — ein gelber Kasten neben einem gelben Knopf
   lässt beide gleich wichtig aussehen und nimmt dem Knopf seine Wirkung.

   Jetzt: goldene Haarlinie und gesperrte Versalien, die Geste aus Vorlage 3.
   Gold erreicht auf dem abgedunkelten Hero 9,10 : 1. */
.hero__anker {
  display: inline-flex;
  align-items: center;
  gap: var(--abstand-xs);
  margin-bottom: var(--abstand-m);
  color: var(--farbe-auszeichnung);
  font-family: var(--schrift-headline);
  font-size: var(--schrift-s);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--sperrung-weit);
}

.hero__anker::before {
  content: "";
  flex: none;
  width: clamp(1.5rem, 4vw, 3rem);
  height: 2px;
  background: var(--farbe-auszeichnung);
}

.hero h1 {
  font-size: var(--schrift-hero);
  /* Schwerer als die übrigen Überschriften. Bei dieser Größe trägt die
     Fläche das zusätzliche Gewicht — es wirkt als Präsenz, nicht als
     Schwere. Möglich erst, seit die Schrift variabel ist. */
  font-weight: var(--gewicht-hero);
  color: var(--farbe-text-invers);
  max-width: 21ch;
  /* Enger als die globale Überschriftenzeile. Bei dieser Schriftgröße
     wirken die Zeilen sonst auseinandergerissen. */
  line-height: 1.06;
  letter-spacing: -0.015em;
}

/* „Qualität, die bleibt." — in Vorlage 3 steht dieser Satz in Gold und
   gesperrt unter dem Namen. Genau so hier: Auszeichnung, nicht Signal. */
.hero__claim {
  margin-top: var(--abstand-m);
  font-family: var(--schrift-headline);
  font-size: var(--schrift-h3);
  font-weight: 600;
  color: var(--farbe-auszeichnung);
  letter-spacing: var(--sperrung-eng);
  max-width: none;
}

.hero__knoepfe {
  display: flex;
  flex-wrap: wrap;
  gap: var(--abstand-s);
  margin-top: var(--abstand-l);
}


/* =========================================================================
   5. TRADITIONSBAND

   Das schwarz-gelbe Band ist das wiedererkennbare Gestaltungselement aus
   Vorlage 2 und taucht auf mehreren Seiten als Abschluss auf.
   ========================================================================= */

.band {
  background: var(--farbe-grund-dunkel);
  color: var(--farbe-text-invers);
  border-top: var(--rahmen-stark) solid var(--farbe-akzent);
  border-bottom: var(--rahmen-stark) solid var(--farbe-akzent);
}

.band__zeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  /* Absätze sind global auf 68 Zeichen begrenzt, damit Fließtext lesbar
     bleibt. Diese Zeile ist kein Fließtext, sondern ein Band über die
     volle Breite — deshalb die Begrenzung hier aufheben. */
  max-width: none;
  gap: var(--abstand-xs) var(--abstand-m);
  padding-block: var(--abstand-s);
  font-family: var(--schrift-headline);
  font-size: var(--schrift-s);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--sperrung-weit);
  text-align: center;
}

.band__trenner {
  color: var(--farbe-akzent);
}

/* Kennzahlenband — die Werte sind noch Platzhalter. Bewusst ohne
   Zählanimation: eine Animation von 0 auf eine unbelegte Zahl wäre
   Blickfangwerbung ohne Grundlage. */
.kennzahlen {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--abstand-m);
  padding-block: var(--abstand-l);
}

.kennzahl {
  text-align: center;
}

.kennzahl__wert {
  display: block;
  font-family: var(--schrift-headline);
  font-size: var(--schrift-h2);
  font-weight: 700;
  color: var(--farbe-akzent);
  line-height: 1.1;
}

.kennzahl__text {
  display: block;
  margin-top: var(--abstand-3xs);
  font-size: var(--schrift-s);
  color: var(--farbe-text-leise-invers);
}


/* =========================================================================
   6. LEISTUNGSRASTER

   Aufbau je Kachel nach Vorlage 2: gelbes Quadrat mit weißem Outline-Icon,
   darunter Versalien-Label und ein einzeiliger Erklärtext.
   ========================================================================= */

.leistungen {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--abstand-m);
  list-style: none;
  margin: 0;
  padding: 0;
}

@media (max-width: 1100px) { .leistungen { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 780px)  { .leistungen { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 430px)  { .leistungen { grid-template-columns: 1fr; } }

.leistung {
  display: flex;
  flex-direction: column;
  gap: var(--abstand-2xs);
  height: 100%;
  padding: var(--abstand-m);
  background: var(--farbe-grund);
  border: var(--rahmen-staerke) solid var(--farbe-linie);
  border-radius: var(--radius-m);
  text-decoration: none;
  /* Im Ruhezustand Stufe 1: Die Kachel liegt spürbar auf der Fläche,
     statt nur ein Rechteck mit Rand zu sein. */
  box-shadow: var(--hoehe-1);
  transition: border-color var(--dauer-mittel) var(--verlauf),
              box-shadow var(--dauer-mittel) var(--verlauf),
              transform var(--dauer-mittel) var(--verlauf);
}

/* Unter dem Zeiger auf Stufe 3 — derselbe Sprung wie beim Knopf, damit
   sich alles Anklickbare gleich anfühlt. */
.leistung:hover {
  border-color: var(--farbe-akzent);
  box-shadow: var(--hoehe-3);
  transform: translateY(-2px);
}

.leistung__icon {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  margin-bottom: var(--abstand-2xs);
  background: var(--farbe-akzent);
  border-radius: var(--radius-s);
  flex: none;
}

.leistung__icon svg {
  width: 32px;
  height: 32px;
  /* Die Icons sind reine Konturzeichnungen — auf Gelb in Schwarz, weil
     Weiß auf Gelb den Kontrasttest nicht besteht. */
  stroke: var(--farbe-text-stark);
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}

.leistung__titel {
  font-family: var(--schrift-headline);
  font-size: var(--schrift-h4);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--sperrung-eng);
  line-height: var(--zeilenhoehe-mittel);
  color: var(--farbe-text-stark);
  /* Lange Komposita wie „Schadstoff­sanierung" dürfen brechen. */
  hyphens: auto;
}

.leistung__text {
  font-size: var(--schrift-s);
  color: var(--farbe-text-leise);
  max-width: none;
}


/* =========================================================================
   7. ZWEISPALTER — Text neben Bild
   ========================================================================= */

.zweispalter {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--abstand-xl);
  align-items: center;
}

.zweispalter--umgekehrt .zweispalter__bild {
  order: -1;
}

@media (max-width: 860px) {
  .zweispalter {
    grid-template-columns: 1fr;
    gap: var(--abstand-l);
  }
  .zweispalter--umgekehrt .zweispalter__bild {
    order: 0;
  }
}

.zweispalter__text > * + * {
  margin-top: var(--abstand-s);
}

.zweispalter__text .knopf,
.zweispalter__text .weiterlink {
  margin-top: var(--abstand-m);
}

/* --- Bild mit Unterschrift -------------------------------------------- */

.bildblock {
  margin: 0;
}

.bildblock img {
  width: 100%;
  border-radius: var(--radius-m);
}

.bildblock figcaption {
  margin-top: var(--abstand-2xs);
  font-size: var(--schrift-xs);
  color: var(--farbe-text-leise);
}

.bildblock--historisch img {
  filter: grayscale(1) sepia(0.25);
}


/* =========================================================================
   8. ZEITLEISTE

   Zwei Ausprägungen:
     .zeitleiste--band  waagerecht, für den Teaser auf der Startseite
     .zeitleiste--voll  senkrecht (Mobil) bzw. wechselseitig (Desktop),
                        für die Seite /geschichte/
   ========================================================================= */

.zeitleiste--band {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--abstand-l);
  list-style: none;
  margin: 0;
  padding: 0;
}

.zeitleiste--band li {
  position: relative;
  padding-top: var(--abstand-m);
  border-top: 2px solid var(--farbe-auszeichnung);
  max-width: none;
}

.zeitleiste--band li::before {
  content: "";
  position: absolute;
  top: -7px;
  left: 0;
  width: 12px;
  height: 12px;
  background: var(--farbe-auszeichnung);
  border-radius: var(--radius-rund);
}

.zeitleiste__jahr {
  display: block;
  font-family: var(--schrift-headline);
  font-size: var(--schrift-h3);
  font-weight: 700;
  color: var(--farbe-auszeichnung);
  line-height: 1.1;
}

.zeitleiste__titel {
  display: block;
  margin-top: var(--abstand-3xs);
  font-family: var(--schrift-headline);
  font-size: var(--schrift-h4);
  font-weight: 700;
}

.zeitleiste__text {
  margin-top: var(--abstand-2xs);
  font-size: var(--schrift-s);
  color: var(--farbe-text-leise-invers);
  max-width: none;
}

/* --- Volle Zeitleiste -------------------------------------------------- */

.zeitleiste--voll {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Die durchgehende Linie. */
.zeitleiste--voll::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 9px;
  width: 2px;
  background: var(--farbe-auszeichnung);
}

.zeitleiste--voll > li {
  position: relative;
  padding-left: calc(var(--abstand-l) + var(--abstand-s));
  padding-bottom: var(--abstand-xl);
  max-width: none;
}

.zeitleiste--voll > li::before {
  content: "";
  position: absolute;
  top: 6px;
  left: 0;
  width: 20px;
  height: 20px;
  background: var(--farbe-grund-dunkel);
  border: 3px solid var(--farbe-auszeichnung);
  border-radius: var(--radius-rund);
}

.zeitleiste--voll .zeitleiste__bild {
  margin-top: var(--abstand-s);
  max-width: 420px;
}

@media (min-width: 900px) {
  /* Wechselseitig: ungerade Stationen links, gerade rechts. */
  .zeitleiste--voll::before {
    left: 50%;
    transform: translateX(-1px);
  }

  .zeitleiste--voll > li {
    width: 50%;
    padding-left: 0;
    padding-right: var(--abstand-xl);
    text-align: right;
  }

  .zeitleiste--voll > li::before {
    left: auto;
    right: -10px;
  }

  .zeitleiste--voll > li:nth-child(even) {
    margin-left: 50%;
    padding-left: var(--abstand-xl);
    padding-right: 0;
    text-align: left;
  }

  .zeitleiste--voll > li:nth-child(even)::before {
    right: auto;
    left: -10px;
  }

  .zeitleiste--voll > li:nth-child(odd) .zeitleiste__bild {
    margin-left: auto;
  }
}


/* =========================================================================
   9. STAMMBAUM

   Reines CSS-Grid, keine Bibliothek. Auf Mobil bricht der Baum in eine
   saubere senkrechte Liste um — Verbindungslinien werden dann ausgeblendet,
   weil ein waagerechter Baum auf 360px unlesbar wäre.
   ========================================================================= */

.stammbaum {
  overflow-x: auto;
  padding-bottom: var(--abstand-s);
}

.stammbaum ul {
  display: flex;
  justify-content: center;
  gap: var(--abstand-m);
  list-style: none;
  margin: 0;
  padding: 0;
}

.stammbaum > ul {
  padding-top: 0;
}

.stammbaum li {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: var(--abstand-l);
  max-width: none;
}

/* Senkrechte Linie vom Elternknoten herunter. */
.stammbaum li::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: 2px;
  height: var(--abstand-l);
  background: var(--farbe-auszeichnung);
}

/* Waagerechte Linie zwischen Geschwistern. */
.stammbaum li::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--farbe-auszeichnung);
}

/* Die äußeren Kanten der Geschwisterlinie werden gekappt. */
.stammbaum li:first-child::after { left: 50%; }
.stammbaum li:last-child::after  { right: 50%; }
.stammbaum li:only-child::after  { display: none; }

/* Der Wurzelknoten hat keinen Elternknoten. */
.stammbaum > ul > li::before,
.stammbaum > ul > li::after {
  display: none;
}

.stammbaum > ul > li {
  padding-top: 0;
}

.knoten {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--abstand-2xs);
  width: 190px;
  padding: var(--abstand-s);
  background: rgba(255, 255, 255, 0.04);
  /* Das Gold kam hier bis zum 31.07.2026 als Zahl an — rgba(201, 177, 119).
     Damit hätte jede andere Markenpalette an dieser einen Kante weiterhin
     Magards Gold getragen, und kein Prüfwerkzeug hätte es gemeldet:
     Abschnitt 8 von abnahme-pruefen.mjs prüft, ob benutzte Tokens
     deklariert sind, nicht ob irgendwo eine Farbe als Zahl steht.

     Das Mischverhältnis bleibt bei 35 %, damit sich der Wert nicht
     verschiebt. --farbe-linie-gold liegt bei 42 % und ist deshalb NICHT
     dasselbe Token. */
  border: var(--rahmen-staerke) solid
          color-mix(in srgb, var(--farbe-auszeichnung) 35%, transparent);
  border-radius: var(--radius-m);
  text-align: center;
}

.knoten__portraet {
  width: 84px;
  height: 84px;
  border-radius: var(--radius-rund);
  object-fit: cover;
  border: 3px solid var(--farbe-text-leise);
}

/* Graustufen trug bis zum 15.08.2026 jedes Porträt außer dem der aktuellen
   Generation. Seither entscheidet die Person selbst darüber, über
   portraetHistorisch in daten/stammbaum.json — dieselbe Regel wie in der
   Zeitleiste, wo nur die Aufnahme von 1906 eingefärbt bleibt. Am Inhalt
   festgemacht und nicht an der Baumtiefe, damit eine eingeschobene
   Generation nicht lautlos das falsche Bild trifft. */
.knoten__portraet--historisch {
  filter: grayscale(1);
}

/* Die aktuell führende Generation bekommt den gelben Ring. */
.knoten--aktuell .knoten__portraet {
  border-color: var(--farbe-akzent);
}

.knoten__name {
  font-family: var(--schrift-headline);
  font-size: var(--schrift-m);
  font-weight: 700;
  line-height: var(--zeilenhoehe-mittel);
}

.knoten__titel {
  font-size: var(--schrift-xs);
  color: var(--farbe-auszeichnung);
  text-transform: uppercase;
  letter-spacing: var(--sperrung-eng);
}

.knoten__zeitraum {
  font-size: var(--schrift-xs);
  color: var(--farbe-text-leise-invers);
}

.knoten__notiz {
  font-size: var(--schrift-xs);
  color: var(--farbe-text-leise-invers);
  max-width: none;
}

@media (max-width: 760px) {
  .stammbaum ul {
    flex-direction: column;
    align-items: stretch;
    gap: var(--abstand-s);
  }

  .stammbaum li {
    padding-top: 0;
    padding-left: var(--abstand-m);
    align-items: stretch;
    border-left: 2px solid var(--farbe-auszeichnung);
  }

  .stammbaum > ul > li {
    border-left: 0;
    padding-left: 0;
  }

  .stammbaum li::before,
  .stammbaum li::after {
    display: none;
  }

  .knoten {
    flex-direction: row;
    align-items: center;
    width: 100%;
    text-align: left;
    gap: var(--abstand-s);
  }

  .knoten__portraet {
    width: 60px;
    height: 60px;
    flex: none;
  }

  .knoten__daten {
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
}


/* =========================================================================
   10. PROJEKTE, FILTER UND LIGHTBOX

   Die Galerie ist ohne JavaScript eine vollständige Liste aller Projekte.
   Das Skript blendet nur zusätzlich Filterknöpfe und die Lightbox ein.
   ========================================================================= */

.projektraster {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--abstand-m);
  list-style: none;
  margin: 0;
  padding: 0;
}

@media (max-width: 900px) { .projektraster { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .projektraster { grid-template-columns: 1fr; } }

/* --- Projektliste auf /projekte/ ----------------------------------------
   Seit dem 13.08.2026 stehen dort fuenf breite Zeilen statt eines Rasters
   aus schmalen Kacheln. Grund: jedes Projekt zeigt jetzt Vorher UND
   Nachher. Zwei Bilder in einer Drittelspalte waeren je rund 180 px breit
   gewesen. Das Kachelraster darueber bleibt, es traegt den Teaser auf der
   Startseite — dort steht weiterhin nur das Nachher-Bild.
   ------------------------------------------------------------------------ */
.projektliste {
  display: grid;
  gap: var(--abstand-l);
  list-style: none;
  /* Der obere Abstand ist Pflicht, nicht Geschmack: Bis zum 13.08.2026 lag
     zwischen der Ueberschrift „Alle Projekte" und den Bildern die
     Filterleiste mit ihren eigenen Abstaenden. Ohne sie stiess die
     Unterlaenge des j in „Projekte" direkt auf das erste Bild —
     _vorschau-messung.html meldete das bei allen fuenfzehn Breiten als
     Kollision. */
  margin: var(--abstand-l) 0 0;
  padding: 0;
}

/* Die Kachel setzt height 100%, damit im Raster alle gleich hoch sind.
   In der einspaltigen Liste gibt es nichts anzugleichen. */
.projekt--zeile { height: auto; }

/* Die Fuge zwischen Vorher und Nachher ist die Linienfarbe, die auch den
   Kartenrahmen traegt — so wirken die beiden Bilder wie zwei Haelften
   eines Elements und nicht wie zwei Karten. */
.projekt__paar {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--rahmen-staerke);
  background: var(--farbe-linie);
}

/* Faellt bildVorher weg, steht das Nachher-Bild allein ueber die Breite. */
.projekt__paar--einzeln { grid-template-columns: 1fr; }

/* Unter 640 px untereinander: zwei Bilder nebeneinander waeren auf dem
   Handy je 160 px breit. */
@media (max-width: 640px) { .projekt__paar { grid-template-columns: 1fr; } }

.projekt__seite {
  position: relative;
  margin: 0;
  overflow: clip;
}

/* Gleiche Marke wie frueher der Kategorie-Chip: Schwarz auf Gelb, die
   einzige Kombination, die auf jedem Bilduntergrund steht (Regel 6). */
.projekt__marke {
  position: absolute;
  top: var(--abstand-2xs);
  left: var(--abstand-2xs);
  padding: 2px var(--abstand-2xs);
  background: var(--farbe-akzent);
  color: var(--farbe-text-stark);
  border-radius: var(--radius-s);
  font-family: var(--schrift-headline);
  font-size: var(--schrift-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--sperrung-eng);
}

.projekt__beschreibung { margin-top: var(--abstand-2xs); }

.projekt {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--farbe-grund);
  border: var(--rahmen-staerke) solid var(--farbe-linie);
  border-radius: var(--radius-m);
  /* Stufe 1, aber kein Hover-Sprung: Die Karte als Ganzes ist nicht
     anklickbar, nur das Bild öffnet die Lightbox. Ein Element anzuheben,
     das auf einen Klick nicht reagiert, ist ein falsches Versprechen. */
  box-shadow: var(--hoehe-1);
  overflow: hidden;
  max-width: none;
}

/* clip, damit das Bild beim langsamen Heranfahren (Abschnitt 13) nicht
   in den Textbereich der Karte hineinragt. Der Chip liegt absolut
   innerhalb und wird davon nicht berührt.

   Hier ist das clip anders als bei den Sektionsübergängen wirklich nötig:
   Das Bild wächst um vier Prozent über seinen Rahmen hinaus. Die Folge für
   den Fokusring wird eine Zeile weiter unten aufgefangen. */
.projekt__bildbereich {
  position: relative;
  overflow: clip;
}

/* Der Fokusring liegt normalerweise zwei Pixel AUSSERHALB des Elements.
   Bei diesem Bild wird er dadurch gleich dreifach abgeschnitten — vom
   Bildbereich, von der Karte und vom Bild selbst.

   Ein negativer Versatz legt ihn nach innen, wo ihn nichts beschneidet.
   Diese Zeile bleibt als Rückfall: Sie greift auch dort, wo :has() unten
   nicht zieht, und sie überlebt den Kontrastmodus des Betriebssystems, der
   Schatten verwirft und Umrisse behält. */
.projekt__bild:focus-visible {
  outline-offset: calc(var(--fokus-abstand) * -2);
}

/* WARUM DAS ALLEIN NICHT REICHTE

   Ein einzelner schwarzer Strich, drei Pixel breit, vier Pixel innerhalb
   der Kante — der liegt auf dem Foto. Welche Farbe ein Foto an genau
   dieser Stelle hat, weiß niemand im Voraus. Beim Durchtabben war der Ring
   deshalb kaum auszumachen.

   Kein Werkzeug konnte das melden. _vorschau-fokus.html prüft, OB ein Ring
   deklariert ist und ob er abgeschnitten wird — nicht, ob er sich von
   seinem Untergrund abhebt. Diese Grenze steht seit dem Bau im Kopf jener
   Datei; das ist nach dem Schließen-Knopf der Lightbox (16877f8) das
   zweite Mal, dass sie zuschlägt.

   Die Lösung ist ein zweifarbiger Ring: eine gelbe Fläche von neun Pixeln
   von der Kante nach innen, darin bei drei bis sechs Pixeln der schwarze
   Strich. Schwarz auf Gelb ist die einzige Kombination, die auf jedem
   Untergrund steht — sie trägt die Seite ohnehin (Regel 6). Damit hängt
   die Sichtbarkeit nicht mehr am Bildinhalt.

   Warum ein Pseudoelement und nicht das Bild selbst: Ein eingesetzter
   Schatten auf einem <img> läge unter der Bilddatei und wäre unsichtbar.
   Das ::after liegt darüber und nimmt keine Klicks an. Beide Angaben
   sitzen auf DEMSELBEN Element, deshalb ist ihre Reihenfolge beim Zeichnen
   eindeutig: Der Umriss liegt über dem eigenen Schatten. */
/* .projekt__seite steht seit dem 13.08.2026 mit in beiden Selektoren: die
   Bilder der Vorher-Nachher-Zeilen sind dieselben klickbaren
   .projekt__bild, sitzen aber in einem anderen Behaelter. Ohne diese
   Erweiterung haetten sie nur den schwarzen Strich aus der Regel darueber
   und waeren je nach Bildinhalt kaum zu sehen — genau der Fehler, den der
   Kommentar oben beschreibt. */
.projekt__bildbereich::after,
.projekt__seite::after {
  content: "";
  position: absolute;
  inset: 0;
  display: none;
  pointer-events: none;
}

.projekt__bildbereich:has(.projekt__bild:focus-visible)::after,
.projekt__seite:has(.projekt__bild:focus-visible)::after {
  display: block;
  box-shadow: inset 0 0 0 calc(var(--fokus-breite) * 3) var(--farbe-akzent);
  outline: var(--fokus-breite) solid var(--farbe-fokus);
  outline-offset: calc(var(--fokus-breite) * -2);
}

.projekt__bild {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* Seit dem 13.08.2026 unbenutzt: die Kategorie-Chips sind mit der
   Filterleiste entfallen. Bleibt stehen, weil der dokumentierte Weg
   zurueck (daten/projekte.json, _anleitung.filter) diese Regel braucht. */
.projekt__chip {
  position: absolute;
  top: var(--abstand-2xs);
  left: var(--abstand-2xs);
  padding: 2px var(--abstand-2xs);
  background: var(--farbe-akzent);
  color: var(--farbe-text-stark);
  border-radius: var(--radius-s);
  font-family: var(--schrift-headline);
  font-size: var(--schrift-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--sperrung-eng);
}

.projekt__text {
  padding: var(--abstand-s);
}

.projekt__titel {
  font-family: var(--schrift-headline);
  font-size: var(--schrift-h4);
  font-weight: 700;
}

.projekt__meta {
  margin-top: var(--abstand-3xs);
  font-size: var(--schrift-s);
  color: var(--farbe-text-leise);
}

/* --- Filter ------------------------------------------------------------ */

/* Wird per JavaScript sichtbar gemacht. Ohne JS bleibt der Bereich
   verborgen, weil die Knöpfe dann nichts bewirken würden. */
/* margin-top ist hier keine Geschmacksfrage, sondern nötig.

   basis.css setzt `* { margin: 0 }`; Abstände kommen ausschliesslich aus
   gezielten Regeln wie `p + p`. Diese Leiste hatte nur einen Abstand nach
   unten. Über ihr steht ein <h2> — und Überschriften tragen seit dem
   Wechsel auf variable Schriften `text-box-trim: trim-both` mit
   `text-box-edge: cap alphabetic`. Ihr Kasten endet damit an der
   Grundlinie statt an der Zeilenbox: 26 px hoch bei 43,7 px Zeilenhöhe.

   Die Folge war nicht bloss ein knapper Abstand, sondern Überlappung. Die
   Unterlänge des „j" in „Alle Projekte" ragt unter den Kasten, und der
   Kasten der Leiste begann exakt dort — gemessene Lücke: 0 px.

   Das Trimmen ist richtig und bleibt. Nur muss ein Element, das direkt auf
   eine Überschrift folgt, seinen Abstand selbst mitbringen. Gefunden hat
   das ein Mensch auf einem Bildschirmfoto; die Breitenmessung prüft
   seitlichen Überlauf, keine senkrechten Kollisionen. */
/* Seit dem 13.08.2026 unbenutzt, siehe Hinweis bei .projekt__chip. */
.filter {
  display: none;
  flex-wrap: wrap;
  gap: var(--abstand-2xs);
  margin-top: var(--abstand-m);
  margin-bottom: var(--abstand-l);
}

.filter.ist-aktiv {
  display: flex;
}

.filter__knopf {
  min-height: 44px;
  padding: var(--abstand-3xs) var(--abstand-s);
  background: var(--farbe-grund);
  border: 2px solid var(--farbe-linie);
  border-radius: var(--radius-m);
  font-family: var(--schrift-headline);
  font-size: var(--schrift-s);
  font-weight: 600;
}

.filter__knopf:hover {
  border-color: var(--farbe-linie-stark);
}

.filter__knopf[aria-pressed="true"] {
  background: var(--farbe-akzent);
  border-color: var(--farbe-akzent);
  color: var(--farbe-text-stark);
}

/* --- Lightbox ---------------------------------------------------------- */

/* Die Lightbox ist ein <dialog>. Der bringt eigene Vorgaben mit — Rahmen,
   Polsterung, begrenzte Breite, automatischer Rand —, die hier alle
   zurückgenommen werden, damit sie die Fläche wie vorher füllt.

   Ein geöffnetes <dialog> liegt im „top layer" des Browsers, also über
   allem anderen. Das bisherige z-index ist damit gegenstandslos; das Token
   --ebene-lightbox bleibt trotzdem stehen, falls die Lightbox jemals
   wieder ein gewöhnliches Element wird. */
.lightbox {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  border: 0;
  display: none;
  place-items: center;
  padding: var(--abstand-m);
  background: transparent;
  color: var(--farbe-text-invers);
}

/* [open] setzt der Browser, nicht mehr eine eigene Klasse. */
.lightbox[open] {
  display: grid;
}

/* Die dunkle Fläche dahinter. Vorher war das der Hintergrund des
   Dialog-Elements selbst; jetzt ist es ::backdrop, das der Browser
   eigens dafür bereitstellt. */
.lightbox::backdrop {
  /* Ebenfalls seit dem 31.07.2026 kein fester Wert mehr — vorher stand hier
     rgba(17, 17, 17, 0.94). Ein Projekt mit wärmerem oder hellerem Dunkel
     bekam sonst eine kalte Auflage über eine sonst warme Seite.
     94 % bleibt 94 %, der Wert verschiebt sich nicht.

     EIN VORBEHALT, DER FÜR DEN FESTEN WERT NICHT GALT

     ::backdrop liegt im Top-Layer und hat Custom Properties lange NICHT
     geerbt. Wo das so ist, greift var() ins Leere, die ganze Deklaration
     wird ungültig, und die Lightbox öffnet ohne abgedunkelte Fläche.

     Nachgemessen am offenen Dialog in Chrome 148: --farbe-grund-dunkel
     kommt im ::backdrop an, die Fläche löst auf rgb(17, 17, 17) bei 0,94
     auf. Geerbt wird das seit Chrome 122, Firefox 125 und Safari 17.4,
     alle aus dem Frühjahr 2024.

     Ein Ersatzwert — var(--farbe-grund-dunkel, #111) — würde das
     abfangen, aber die Stelle zugleich aus Abschnitt 8 von
     abnahme-pruefen.mjs herausnehmen: Der zählt ausdrücklich nur Tokens
     OHNE Ersatzwert. Die Prüfbarkeit ist hier mehr wert als ein Rückfall
     für Browser von vor 2024 — dieselbe Abwägung, die --farbe-linie-gold
     in tokens.css schon einmal getroffen hat. */
  background: color-mix(in srgb, var(--farbe-grund-dunkel) 94%, transparent);
}

/* Solange die Lightbox offen ist, soll die Seite dahinter nicht scrollen.
   Das erledigte bisher JavaScript über body.style.overflow. Als
   CSS-Regel ist es eine Zeile und kann nicht hängen bleiben, wenn beim
   Schließen einmal etwas schiefgeht. */
html:has(.lightbox[open]) {
  overflow: hidden;
}

.lightbox__figur {
  margin: 0;
  max-width: min(1100px, 100%);
  max-height: 100%;
}

.lightbox__bild {
  max-width: 100%;
  max-height: 78vh;
  object-fit: contain;
  margin-inline: auto;
}

.lightbox__unterschrift {
  margin-top: var(--abstand-s);
  color: var(--farbe-text-invers);
  text-align: center;
}

.lightbox__schliessen {
  position: absolute;
  top: var(--abstand-s);
  right: var(--abstand-s);
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  background: var(--farbe-akzent);
  color: var(--farbe-text-stark);
  border: 0;
  border-radius: var(--radius-m);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1;
}


/* =========================================================================
   11. KONTAKT UND FORMULAR
   ========================================================================= */

/* --- Kontaktwege als vollwertige Bedienelemente ------------------------ */

.kontaktwege {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--abstand-m);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Die Kachel bringt ihre eigene weiße Fläche mit und funktioniert deshalb
   auch in einer dunklen Sektion. Die Textfarbe wird trotzdem ausdrücklich
   gesetzt: In .sektion--dunkel steht sie sonst auf Weiß, und alles darin,
   was einmal keine eigene Farbe bekommt, wäre unsichtbar. */
.kontaktweg {
  display: flex;
  align-items: flex-start;
  gap: var(--abstand-s);
  padding: var(--abstand-m);
  background: var(--farbe-grund);
  color: var(--farbe-text);

  /* Die Kachel bringt eine eigene weiße Fläche mit und muss deshalb auch
     den Textkontext einer hellen Fläche wiederherstellen.

     Ohne diese Zeile erbt sie in einer dunklen Sektion deren helles Grau
     für gedämpften Text — und das steht dann auf Weiß, mit 1,4 : 1. Genau
     das ist beim ersten Versuch passiert, die dunklen Sektionen zu
     reparieren: Die Korrektur an einer Stelle erzeugte den umgekehrten
     Fehler an der anderen. Aufgefallen ist es sofort, weil die
     Kontrastprüfung inzwischen im Browser rechnet. */
  --farbe-text-leise: var(--farbe-text-leise-hell);
  border: var(--rahmen-staerke) solid var(--farbe-linie);
  border-left: var(--rahmen-stark) solid var(--farbe-akzent);
  border-radius: var(--radius-m);
  text-decoration: none;
  max-width: none;
}

.kontaktweg:hover {
  box-shadow: var(--schatten-kachel);
}

.kontaktweg__icon {
  width: 28px;
  height: 28px;
  flex: none;
  margin-top: 2px;
  stroke: var(--farbe-text);
  stroke-width: 1.75;
  fill: none;
}

.kontaktweg__label {
  display: block;
  font-size: var(--schrift-s);
  color: var(--farbe-text-leise);
}

.kontaktweg__wert {
  display: block;
  font-family: var(--schrift-headline);
  font-size: var(--schrift-h4);
  font-weight: 700;
  color: var(--farbe-text-stark);
  /* Rufnummern nie umbrechen. */
  white-space: nowrap;
}

.kontaktweg__zusatz {
  display: block;
  margin-top: 2px;
  font-size: var(--schrift-xs);
  color: var(--farbe-text-leise);
}

/* --- Formular ---------------------------------------------------------- */

.formular {
  display: grid;
  gap: var(--abstand-m);
  max-width: 720px;
}

.feld {
  display: grid;
  gap: var(--abstand-3xs);
}

.feld > label {
  font-weight: 600;
}

.feld__pflicht {
  color: var(--farbe-akzent-text);
}

.feld input,
.feld textarea,
.feld select {
  width: 100%;
  min-height: 48px;
  padding: var(--abstand-2xs) var(--abstand-xs);
  background: var(--farbe-grund);
  /* #767676 erreicht 4,54 : 1 gegen Weiß. Ein helleres Grau würde die
     3 : 1 für Bedienelemente verfehlen. */
  border: var(--rahmen-staerke) solid var(--farbe-linie-formular);
  border-radius: var(--radius-m);
  box-shadow: var(--schatten-feld);
  transition: box-shadow var(--dauer-mittel) var(--verlauf),
              border-color var(--dauer-schnell) var(--verlauf);
}

/* Hier :focus und nicht :focus-visible: Ein Feld wird auch mit der Maus
   angeklickt, und dann soll man sehen, wo man schreibt. Der schwarze
   Fokusring aus basis.css kommt bei Tastaturbedienung zusätzlich dazu —
   der Schatten ersetzt ihn nicht, er begleitet ihn. */
.feld input:focus,
.feld textarea:focus,
.feld select:focus {
  border-color: var(--farbe-linie-stark);
  box-shadow: var(--schatten-feld-fokus);
}

.feld textarea {
  min-height: 160px;
  resize: vertical;
}

.feld__hinweis {
  font-size: var(--schrift-xs);
  color: var(--farbe-text-leise);
}

.feld--kontrollkaestchen {
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: var(--abstand-2xs);
}

/* Ohne Schatten: Der Wert ist für Elemente ab 48px gemacht und wirkt an
   einem 22px-Kästchen unverhältnismäßig groß. */
.feld--kontrollkaestchen input {
  width: 22px;
  min-height: 22px;
  height: 22px;
  margin-top: 4px;
  box-shadow: none;
}

/* Honeypot: für Menschen unsichtbar, für viele Bots ausfüllbar.
   Bewusst nicht display:none — manche Bots erkennen das. */
.honigtopf {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* --- Hinweisbox -------------------------------------------------------- */

.hinweis {
  padding: var(--abstand-m);
  background: var(--farbe-grund-wechsel);
  border-left: var(--rahmen-stark) solid var(--farbe-akzent);
  border-radius: var(--radius-m);
}

/* Die dunkle Ausprägung greift ab jetzt AUTOMATISCH in jeder dunklen
   Sektion — nicht nur, wenn jemand daran denkt, die Klasse zu setzen.

   Vorher stand der Kasten dort mit seinem hellgrauen Hintergrund, und der
   Text darin erbte das Weiß der Sektion: weiß auf hellgrau, praktisch
   unlesbar. Genau das ist auf der Karriereseite passiert, nachdem der
   Bewerbungsabschnitt zu einem dunklen Ankermoment wurde.

   Dasselbe Muster wie beim sekundären Knopf weiter oben: Eine Sektion von
   hell auf dunkel umzustellen ist eine Klasse. Alles darin muss von selbst
   mitgehen, sonst vergisst es irgendwann jemand — und kein Werkzeug
   schlägt an, weil beide Farben für sich genommen erlaubt sind. */
.hinweis--dunkel,
.sektion--dunkel .hinweis {
  background: rgba(255, 255, 255, 0.05);
  border-left-color: var(--farbe-auszeichnung);
}

.hinweis__titel {
  font-family: var(--schrift-headline);
  font-size: var(--schrift-h4);
  font-weight: 700;
  margin-bottom: var(--abstand-2xs);
}

/* --- Brotkrumen -------------------------------------------------------- */

/* Die Brotkrumenlinks waren 25 px hoch. Sie bekommen dieselbe Zielgröße wie
   die Fußzeilenlinks — und weil der eigene Innenabstand des Bandes dadurch
   entbehrlich wird, wandert er von 1 rem auf 0,25 rem. Gemessen bei 375 px
   ist das Band danach 53 px hoch statt 57 px, also eher schmaler als vorher. */
.brotkrumen {
  padding-block: var(--abstand-3xs);
  border-bottom: var(--rahmen-staerke) solid var(--farbe-linie);
  font-size: var(--schrift-s);
}

.brotkrumen a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.brotkrumen ol {
  display: flex;
  flex-wrap: wrap;
  gap: var(--abstand-3xs);
  list-style: none;
  margin: 0;
  padding: 0;
}

.brotkrumen li {
  display: flex;
  align-items: center;
  gap: var(--abstand-3xs);
  color: var(--farbe-text-leise);
  max-width: none;
}

.brotkrumen li + li::before {
  content: "›";
  color: var(--farbe-text-leise);
}

/* --- Ortsliste Arbeitsgebiet ------------------------------------------- */

.orte {
  display: flex;
  flex-wrap: wrap;
  gap: var(--abstand-2xs);
  list-style: none;
  margin: 0;
  padding: 0;
}

.orte li {
  padding: var(--abstand-3xs) var(--abstand-xs);
  background: var(--farbe-grund);
  border: var(--rahmen-staerke) solid var(--farbe-linie);
  border-radius: var(--radius-m);
  font-size: var(--schrift-s);
  max-width: none;
}

/* --- Aufzählung mit gelbem Haken --------------------------------------- */

.hakenliste {
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--abstand-2xs);
}

.hakenliste li {
  position: relative;
  padding-left: 1.75em;
}

/* Ein Link in dieser Liste ist ein Bedienelement, kein Fließtext.

   Die Inhaltsverzeichnisse von Impressum und Datenschutz sind als
   .hakenliste gebaut. Ihre Links waren 23 px hoch — ein Pixel unter der
   Pflichtgrenze von WCAG 2.2, Kriterium 2.5.8, Stufe AA.

   Die Inline-Ausnahme des Kriteriums greift hier NICHT. Sie gilt für ein
   Ziel „in einem Satz" oder eines, dessen Größe durch die Zeilenhöhe
   umgebenden Nicht-Ziel-Textes bestimmt wird. In einem Verzeichnis steht
   der Link allein im Listenpunkt — genau der Fall, für den die Ausnahme
   nicht gedacht ist.

   Gefunden am 30.07.2026, als die Fokusprüfung ihre Seitenliste aus der
   Sitemap ableitete statt aus sechs handverlesenen Adressen. /datenschutz/
   und /impressum/ waren nie darunter. Die Verstöße lagen seit dem Bau da,
   auf zwei Seiten, die jeder Betrieb hat.

   inline-block statt inline, weil ein inline-Element Höhenangaben
   ignoriert. Die Zeilenhöhe wird mitgesetzt, sonst sitzt der Text oben im
   Kasten statt in der Mitte. */
.hakenliste a {
  display: inline-block;
  min-height: 24px;
  line-height: 24px;
}


.hakenliste li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 0.7em;
  height: 0.35em;
  border-left: 3px solid var(--farbe-akzent);
  border-bottom: 3px solid var(--farbe-akzent);
  transform: rotate(-45deg);
}


/* =========================================================================
   12. FUSS
   ========================================================================= */

/* Goldene Haarlinie als oberer Abschluss — dieselbe Geste wie über dem
   Traditionsanker im Hero. Sie trennt den Fuß von der letzten Sektion,
   ohne ihn wie einen eigenen Kasten wirken zu lassen; eine volle Linie
   in Gelb wäre an dieser Stelle ein Signal, wo keines nötig ist.

   Die Putzstruktur der dunklen Sektionen bekommt der Fuß bewusst nicht:
   Er ist Abbinder, nicht Fläche. */
.fuss {
  background: var(--farbe-grund-dunkel);
  color: var(--farbe-text-leise-invers);
  border-top: var(--rahmen-staerke) solid var(--farbe-linie-gold);
}

.fuss a {
  color: var(--farbe-text-invers);
  text-decoration: none;
}

.fuss a:hover {
  text-decoration: underline;
  text-decoration-color: var(--farbe-akzent);
  text-decoration-thickness: 2px;
}

.fuss__raster {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: var(--abstand-xl);
  padding-block: var(--sektion-abstand) var(--abstand-xl);
}

@media (max-width: 860px) {
  .fuss__raster {
    grid-template-columns: 1fr 1fr;
    gap: var(--abstand-l);
  }
  .fuss__spalte--breit {
    grid-column: 1 / -1;
  }
}

@media (max-width: 520px) {
  .fuss__raster { grid-template-columns: 1fr; }
}

.fuss__logo svg {
  height: 52px;
  width: auto;
  margin-bottom: var(--abstand-s);
}

.fuss__titel {
  font-family: var(--schrift-headline);
  font-size: var(--schrift-h4);
  font-weight: 700;
  color: var(--farbe-text-invers);
  text-transform: uppercase;
  letter-spacing: var(--sperrung-eng);
  margin-bottom: var(--abstand-s);
}

/* Die Linklisten im Fuß sind auf dem Handy die vollständige Navigation —
   neun Leistungen, fünf Bereiche, zwei Rechtstexte, zwei Rufnummern.

   Mit der Zeilenhöhe allein waren sie 21 px hoch. Das liegt unter den 24 px,
   die WCAG 2.2 im Kriterium 2.5.8 als Mindestzielgröße verlangt, und weit
   unter den 48 px, auf die sich dieses Projekt bei den Knöpfen festgelegt
   hat. Gemessen mit _vorschau-fokus.html: 246 Treffer über sechs Seiten und
   zwei Breiten — der größte Einzelbefund im ganzen Projekt.

   Der Abstand zwischen den Einträgen wandert dabei von `gap` in die Höhe des
   Links selbst. Sonst käme beides zusammen und der Fuß würde doppelt so
   lang. Zwischen zwei Texten liegen jetzt rund 22 px statt der bisherigen
   8 px, die Liste ist also luftiger als vorher, nicht enger.

   inline-flex und nicht block: Die Trefferfläche soll so breit sein wie der
   Text. Eine über die ganze Spalte reichende Fläche würde neben dem Text ins
   Leere führen und wäre für Screenreader unverändert — sie sieht nur nach
   Bedienbarkeit aus, wo keine ist. */
.fuss__liste {
  list-style: none;
  padding: 0;
  display: grid;
  gap: 0;
}

.fuss__liste li {
  max-width: none;
}

.fuss__liste a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.fuss address {
  font-style: normal;
  line-height: var(--zeilenhoehe-text);
}

/* Die Abschlussleiste im Stil von Vorlage 2. */
.fuss__leiste {
  border-top: var(--rahmen-stark) solid var(--farbe-akzent);
  padding-block: var(--abstand-s);
}

.fuss__leiste-zeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--abstand-2xs) var(--abstand-m);
  font-family: var(--schrift-headline);
  font-size: var(--schrift-s);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--sperrung-eng);
  color: var(--farbe-text-invers);
}

.fuss__marke {
  color: var(--farbe-akzent);
}

.fuss__recht {
  font-family: var(--schrift-text);
  font-size: var(--schrift-xs);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--farbe-text-leise-invers);
}


/* =========================================================================
   12b. RENDERN ÜBERSPRINGEN, WAS NICHT ZU SEHEN IST

   Der Browser darf Layout und Zeichnen für alles auslassen, was noch weit
   unterhalb des Bildschirms liegt, und holt es nach, sobald es näher kommt.

   EHRLICH ZUM NUTZEN: Bei den heutigen Seitenlängen — die Projektseite ist
   rund 3100 px hoch — ist der Gewinn kaum messbar. Das steht hier für den
   Fall, dass aus zwölf Projekten irgendwann vierzig werden; dann greift es
   von selbst, ohne dass jemand daran denken muss.

   contain-intrinsic-size ist dabei Pflicht, nicht Kür: Ohne eine Angabe,
   wie hoch der übersprungene Bereich ungefähr ist, hätte er die Höhe null
   und die Scrollleiste würde beim Herunterscrollen springen. `auto` heißt:
   Der Browser merkt sich die tatsächliche Höhe, sobald er sie einmal
   berechnet hat, und benutzt ab dann diese.

   Geprüft: Die scrollgesteuerten Animationen aus Abschnitt 13 laufen damit
   unverändert weiter. Das war die Sorge — ein Bereich, der nicht gerendert
   wird, könnte auch keine Bewegung auslösen. Nachgemessen an zwölf
   Projektkarten: keine bleibt unsichtbar.
   ========================================================================= */

.projektraster,
.zeitleiste--voll,
.stammbaum {
  content-visibility: auto;
  contain-intrinsic-size: auto 600px;
}


/* =========================================================================
   13. BEWEGUNG BEIM SCROLLEN

   Abschnitte setzen sich beim Erscheinen. Sie fliegen nicht herein, sie
   blinken nicht und sie zählen nichts hoch — die Bewegung soll auffallen,
   wenn man darauf achtet, und sonst nicht.

   Gemacht mit `animation-timeline: view()`, also ohne eine einzige Zeile
   JavaScript. Das ist der eigentliche Grund für diese Lösung und nicht
   nur eine technische Vorliebe: Es gibt keine Bibliothek, die veraltet,
   keinen Beobachter, der bei einer späteren Änderung hängen bleibt, und
   nichts, was in fünf Jahren jemand mitpflegen muss. Wer die Bewegung
   loswerden will, löscht diesen Block — mehr ist nicht nötig.

   DIE REIHENFOLGE IST WICHTIG: Der Ausgangszustand steht INNERHALB von
   @supports. Kann ein Browser keine scrollgesteuerten Animationen, wird
   der ganze Block übersprungen und der Inhalt ist sofort und vollständig
   sichtbar. Stünde `opacity: 0` außerhalb, bliebe die halbe Seite in
   diesen Browsern unsichtbar — der klassische Fehler bei dieser Technik.

   Die Kennzahlen sind bewusst nicht dabei. Eine Zahl, die hochzählt,
   ist Blickfangwerbung — und die Werte sind noch nicht belegt.
   ========================================================================= */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    @keyframes einblenden {
      from {
        opacity: 0;
        transform: translateY(var(--versatz-einblenden));
      }
      to {
        opacity: 1;
        transform: none;
      }
    }

    .sektion__kopf,
    .leistung,
    .projekt,
    .zeitleiste > li,
    /* Nicht die Bilder innerhalb der Zeitleiste — deren <li> bewegt sich
       schon, beides zusammen ergäbe eine doppelte Bewegung. */
    .bildblock:not(.zeitleiste__bild) {
      animation: einblenden var(--dauer-einblenden) var(--verlauf) both;
      animation-timeline: view();
      /* Fertig, bevor das Element in der Bildmitte steht — sonst liest
         man den Text, während er noch in Bewegung ist. */
      animation-range: entry 8% entry 55%;
    }

    /* --- Staffelung im Leistungsraster ---------------------------------

       animation-delay hilft hier nicht: Bei einer scrollgesteuerten
       Animation ist die Zeitachse die Scrollposition, keine Uhr. Die
       Staffelung entsteht deshalb über verschobene Bereiche.

       Versetzt wird nach Spalte, nicht nach Reihenfolge — das Raster hat
       vier Spalten, und so läuft die Bewegung als Diagonale über die
       Fläche statt als Schlange durch die Zeilen.

       Gezählt wird das <li>, nicht die Kachel: .leistung ist ein <a> und
       das einzige Kind seines Listenpunkts. Auf die Kachel angewandt wäre
       jede von ihnen nth-child(1) und die Staffelung bliebe wirkungslos —
       ohne dass man es dem CSS ansieht. */
    .leistungen > li:nth-child(4n + 1) .leistung { animation-range: entry 6%  entry 50%; }
    .leistungen > li:nth-child(4n + 2) .leistung { animation-range: entry 10% entry 54%; }
    .leistungen > li:nth-child(4n + 3) .leistung { animation-range: entry 14% entry 58%; }
    .leistungen > li:nth-child(4n + 4) .leistung { animation-range: entry 18% entry 62%; }

    /* --- Hero: das Bild wandert langsamer als der Text ------------------

       Der Klassiker, den man sonst mit einer Bibliothek und einem
       Scroll-Beobachter baut. Hier sind es acht Zeilen CSS.

       Die Vergrößerung auf 108 % ist kein Effekt, sondern Voraussetzung:
       Ohne sie liefe beim Verschieben eine leere Kante ins Bild. */
    @keyframes hero-parallax {
      from { transform: translateY(0)  scale(1.08); }
      to   { transform: translateY(7%) scale(1.08); }
    }

    .hero__bild {
      animation: hero-parallax linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }

    /* --- Zeitleiste: die Linie zeichnet sich ---------------------------

       Die goldene Linie wächst von oben nach unten mit, während man
       durch die Firmengeschichte scrollt. Sie ist bei 60 % des Durchlaufs
       fertig, nicht erst am Ende — eine Linie, die dem Blick hinterher-
       hinkt, wirkt zäh statt begleitend. */
    @keyframes linie-zeichnen {
      from { transform: scaleY(0); }
      to   { transform: scaleY(1); }
    }

    .zeitleiste--voll::before {
      transform-origin: top center;
      animation: linie-zeichnen linear both;
      animation-timeline: view();
      animation-range: entry 40% cover 60%;
    }

    /* --- Projektbilder kommen beim Vorbeiscrollen näher ----------------

       Vier Prozent über den ganzen Durchlauf. Das ist bewusst so wenig,
       dass man es nicht als Bewegung wahrnimmt, sondern nur als Leben in
       der Fläche — sichtbarer eingestellt kippt es sofort ins Kitschige.

       Nur hier und nicht bei allen Bildern: .projekt__bildbereich ist der
       einzige Bildrahmen, der das Bild sauber beschneidet. Ein <figure>
       ohne eigenen Rahmen müsste dafür erst umgebaut werden, und das wäre
       eine HTML-Änderung an 26 Seiten für einen kaum sichtbaren Effekt. */
    @keyframes naeher-kommen {
      from { transform: scale(1); }
      to   { transform: scale(1.04); }
    }

    .projekt__bild {
      animation: naeher-kommen linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
  }
}

/* --- Mannschaft ---------------------------------------------------------

   Die namentliche Liste unter dem Team-Abschnitt, gespeist aus
   daten/team.json. Sie ist Kuer: Ohne Eintraege erzeugt render-daten.mjs
   nur einen Kommentar, und dieses Bauteil kommt nie zum Einsatz.

   Bewusst schlicht. Ein Betrieb mit vier Leuten braucht keine Galerie mit
   Rahmen und Schatten — er braucht vier Namen, die man lesen kann. Und ein
   Eintrag ohne Portraet darf nicht wie ein Fehler aussehen, weil das am
   Anfang der Regelfall ist: Namen sind unbedenklich, ein Foto braucht die
   schriftliche Einwilligung der Person. */
.mannschaft {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr));
  gap: var(--abstand-m);
  list-style: none;
  padding: 0;
  margin-top: var(--abstand-xl);
  max-width: none;
}

.mensch {
  display: flex;
  align-items: center;
  gap: var(--abstand-s);
  min-width: 0;
  max-width: none;
}

/* Rund, weil es ein Gesicht ist und kein Produktfoto. Die feste Groesse
   haelt die Zeile ruhig, auch wenn ein Eintrag kein Bild hat. */
.mensch__portraet {
  flex: 0 0 auto;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--farbe-grund-wechsel);
}

.mensch__daten {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.mensch__name {
  font-family: var(--schrift-headline);
  font-weight: var(--gewicht-betont);
  color: var(--farbe-text-stark);
}

.mensch__rolle {
  font-size: var(--schrift-s);
  color: var(--farbe-text-leise);
}

.mensch__seit {
  font-size: var(--schrift-xs);
  color: var(--farbe-text-leise);
}

/* In einer dunklen Sektion erbt der gedaempfte Text sonst das Grau fuer
   hellen Grund — 3,5 : 1 auf Schwarz. Derselbe Fehler wie in 95fe39a. */
.sektion--dunkel .mensch__name {
  color: var(--farbe-text-invers);
}
