/* ===== schriften.css ===== */
/* Selbst gehostete Webschriften.
   Erzeugt von werkzeuge/schriften_laden.py - nicht von Hand bearbeiten.
   Keine Verbindung zu Google-Servern, daher datenschutzrechtlich unbedenklich.
   Google liefert variable Schriften aus: eine Datei deckt den gesamten
   Gewichtsbereich ab, deshalb steht in font-weight eine Spanne. */

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('../schriften/cormorant-garamond-400-kursiv.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../schriften/cormorant-garamond-variabel.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url('../schriften/jost-variabel.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../schriften/inter-variabel.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}


/* ===== tokens.css ===== */
/* ===========================================================================
   Designtokens - Björn Stuhr Fotografie
   Farben und Masse aus dem Design-Handoff V5.
   =========================================================================== */

:root {
  color-scheme: light;

  /* --- Offizielle Markenfarben ------------------------------------------
     Die Werte stammen unveraendert aus dem Design-Handoff V5. */
  --weiss: #ffffff;
  --sand: #b9a87d;
  --blau: #5c8e98;
  --bordeaux: #5b1116;
  --salbei: #d2eec3;
  --akzentrot: #921920;

  /* --- Ergaenzungen ------------------------------------------------------
     Warmes Ivory als zweite Grundflaeche, im Handoff unter "Weiss und warmes
     Ivory" beschrieben, aber ohne Hexwert. */
  --ivory: #fbf8f1;
  --tinte: #1d1d1b;

  /* Flaeche hinter der Grossansicht. Kein Verlauf, sondern --tinte mit
     Restdurchsicht: Der Besucher soll sehen, dass die Seite darunter
     weitersteht und er sie nicht verlassen hat.
     Gemessen ueber der hellsten Flaeche der Website (--weiss), also im
     unguenstigsten Fall: wirksam rgb(38,38,36), Weiss darauf 15,16:1.
     Der Knopfrahmen rgba(255,255,255,0.45) erreicht darauf 4,27:1 und
     bleibt damit ueber den 3:1, die fuer Bedienelemente gelten. */
  --schleier: rgba(29, 29, 27, 0.96);

  /* Abgedunkelte Stufe derselben Blaufamilie.
     Grund: Weisser Text auf dem offiziellen Blau #5C8E98 erreicht nur 3,63:1
     und verfehlt damit die WCAG-AA-Anforderung von 4,5:1 fuer Fliesstext.
     Diese Stufe erreicht 6,02:1 und bleibt farblich nah am Original.
     Das offizielle Blau bleibt fuer Flaechen ohne Fliesstext und fuer
     Ueberschriften ab 24 px im Einsatz - dort genuegen 3:1, die es erfuellt. */
  --blau-tief: #416972;

  /* Abgeleitete Toene fuer Linien, Randflaechen und ruhige Hintergruende.
     Keine neuen Farben, nur Aufhellungen der Markenpalette. */
  --sand-hell: #e6dec9;
  --sand-blass: #f2ede1;
  --salbei-tief: #7f9e6d;
  --linie: #ddd4c0;
  --linie-zart: #ebe4d6;
  /* 16 % --blau auf --ivory, gerechnet. Wasserflaeche der Einsatzgebietskarte
     und deren Traegerflaeche auf "Ueber mich" - beide muessen denselben Ton
     treffen, sonst zeichnet sich die Kante der Grafik ab. Gemessen darauf:
     --text-gedaempft 5,72:1, --tinte 13,49:1. */
  --blau-blass: #e2e7e3;

  /* --- Textfarben auf farbigen Flaechen ---------------------------------
     Bewusst als eigene Tokens, damit im Markup nie die falsche Kombination
     entstehen kann. Die Kontrastwerte stehen jeweils dahinter. */
  --text-auf-weiss: #1d1d1b;        /* 16,88:1 */
  --text-auf-ivory: #1d1d1b;        /* 15,92:1 */
  --text-auf-sand: #3a1418;         /* > 7:1  - Weiss waere hier mit 2,34:1 unbrauchbar */
  --text-auf-blau: #ffffff;         /* nur auf --blau-tief: 6,02:1 */
  --text-auf-bordeaux: #ffffff;     /* 13,67:1 */
  --text-auf-salbei: #2c1b1d;       /* > 12:1 */
  --text-gedaempft: #5d574c;        /* 6,9:1 auf Weiss - fuer Sekundaertext */

  /* --- Typografie -------------------------------------------------------- */
  --schrift-serif: "Cormorant Garamond", "Iowan Old Style", "Palatino Linotype",
                   Palatino, "Times New Roman", serif;
  --schrift-versal: "Jost", "Futura", "Avenir Next", "Helvetica Neue", sans-serif;
  --schrift-text: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
                  "Helvetica Neue", Arial, sans-serif;

  /* Fliessende Groessen: min bei 360 px Viewport, max bei 1240 px.
     Fliesstext startet bei 16 px, wie im Handoff gefordert. */
  --gr-winzig: 0.75rem;
  --gr-klein: 0.8125rem;
  --gr-label: clamp(0.6875rem, 0.62rem + 0.3vw, 0.8125rem);
  --gr-text: clamp(1rem, 0.96rem + 0.18vw, 1.0625rem);
  --gr-text-gross: clamp(1.0625rem, 1rem + 0.3vw, 1.1875rem);
  --gr-lead: clamp(1.125rem, 1.02rem + 0.48vw, 1.4375rem);
  --gr-h4: clamp(1.125rem, 1.05rem + 0.35vw, 1.3125rem);
  --gr-h3: clamp(1.375rem, 1.2rem + 0.8vw, 1.75rem);
  --gr-h2: clamp(1.875rem, 1.5rem + 1.7vw, 3rem);
  --gr-h1: clamp(2.125rem, 1.6rem + 2.4vw, 3.75rem);
  --gr-hero: clamp(2.5rem, 1.7rem + 3.6vw, 4.75rem);
  --gr-statement: clamp(1.5rem, 1.05rem + 2vw, 2.625rem);

  --zeilen-eng: 1.12;
  --zeilen-headline: 1.16;
  --zeilen-lead: 1.5;
  --zeilen-text: 1.72;

  --spatium-versal: 0.16em;   /* grosszuegige Laufweite fuer Versalien */
  --spatium-weit: 0.24em;

  /* --- Raster und Abstaende ---------------------------------------------- */
  --inhaltsbreite: 1240px;
  --lesebreite: 68ch;
  --rand: clamp(1.25rem, 1rem + 2.2vw, 3rem);   /* mobil 20 px, wie gefordert */

  --abstand-1: 0.25rem;
  --abstand-2: 0.5rem;
  --abstand-3: 0.75rem;
  --abstand-4: 1rem;
  --abstand-5: 1.5rem;
  --abstand-6: 2rem;
  --abstand-7: 2.5rem;
  --abstand-8: 3rem;
  --abstand-9: 4rem;
  --abstand-10: 5rem;

  /* Grosse Farbflaechen brauchen grosszuegige Innenabstaende (Handoff). */
  --sektion-y: clamp(4rem, 2.5rem + 6vw, 7.5rem);
  --flaeche-innen: clamp(2rem, 1.2rem + 4vw, 4.5rem);

  /* Bewusst sehr klein: Der Handoff verbietet stark gerundete Ecken. */
  --radius: 2px;

  --uebergang: 260ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

@media (prefers-reduced-motion: reduce) {
  :root { --uebergang: 1ms; }
}


/* ===== basis.css ===== */
/* ===========================================================================
   Grundlagen: Zuruecksetzen, Typografie, Layoutbausteine
   =========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--weiss);
  color: var(--text-auf-weiss);
  font-family: var(--schrift-text);
  font-size: var(--gr-text);
  line-height: var(--zeilen-text);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--schrift-serif);
  font-weight: 400;
  line-height: var(--zeilen-headline);
  letter-spacing: -0.005em;
  text-wrap: balance;
}

h1 { font-size: var(--gr-h1); line-height: var(--zeilen-eng); }
h2 { font-size: var(--gr-h2); }
h3 { font-size: var(--gr-h3); }
h4 { font-size: var(--gr-h4); font-weight: 600; }

p { margin: 0 0 var(--abstand-5); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.22em; }

strong { font-weight: 600; }
em { font-style: italic; }

ul, ol { margin: 0 0 var(--abstand-5); padding-left: 1.35em; }
li { margin-bottom: var(--abstand-2); }
li:last-child { margin-bottom: 0; }

img, svg, picture { max-width: 100%; display: block; }
img { height: auto; }

hr {
  border: 0;
  border-top: 1px solid var(--linie);
  margin: var(--abstand-8) 0;
}

blockquote { margin: 0; }

/* --- Fokus: immer deutlich sichtbar, auch auf farbigen Flaechen ---------- */
:focus-visible {
  outline: 3px solid var(--akzentrot);
  outline-offset: 3px;
  border-radius: 1px;
}

.auf-dunkel :focus-visible,
.flaeche--bordeaux :focus-visible,
.flaeche--blau :focus-visible {
  outline-color: var(--weiss);
}

/* --- Sprunglink zum Inhalt ----------------------------------------------- */
.sprunglink {
  position: absolute;
  left: var(--abstand-4);
  top: var(--abstand-4);
  z-index: 200;
  transform: translateY(-200%);
  background: var(--bordeaux);
  color: var(--weiss);
  padding: var(--abstand-3) var(--abstand-5);
  font-family: var(--schrift-versal);
  font-size: var(--gr-klein);
  letter-spacing: var(--spatium-versal);
  text-transform: uppercase;
  text-decoration: none;
  transition: transform var(--uebergang);
}

.sprunglink:focus { transform: translateY(0); }

/* --- Nur fuer Screenreader ----------------------------------------------- */
.nur-vorlesen {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ===========================================================================
   Layout
   =========================================================================== */

.huelle {
  width: 100%;
  max-width: var(--inhaltsbreite);
  margin-inline: auto;
  padding-inline: var(--rand);
}

.huelle--schmal { max-width: 860px; }
.huelle--breit { max-width: 1440px; }

.sektion { padding-block: var(--sektion-y); }
.sektion--knapp { padding-block: clamp(2.75rem, 1.8rem + 4vw, 5rem); }

/* Flaechenfarben. Jede Variante bringt ihre eigene Textfarbe mit, damit im
   Markup keine unlesbare Kombination entstehen kann. */
.flaeche--weiss    { background: var(--weiss);      color: var(--text-auf-weiss); }
.flaeche--ivory    { background: var(--ivory);      color: var(--text-auf-ivory); }
.flaeche--sand     { background: var(--sand);       color: var(--text-auf-sand); }
.flaeche--sandhell { background: var(--sand-blass); color: var(--text-auf-ivory); }
.flaeche--blau     { background: var(--blau-tief);  color: var(--text-auf-blau); }
.flaeche--bordeaux { background: var(--bordeaux);   color: var(--text-auf-bordeaux); }
.flaeche--salbei   { background: var(--salbei);     color: var(--text-auf-salbei); }
.flaeche--tinte    { background: var(--tinte);      color: var(--weiss); }

.raster {
  display: grid;
  gap: clamp(1.5rem, 1rem + 2vw, 3rem);
}

/* Rasterspalten sind standardmaessig mindestens so breit wie ihr Inhalt
   (min-width: auto). Ein Bild oder ein langes Wort kann eine Spalte dadurch
   ueber ihre zugewiesene Breite hinaus aufblaehen und den Nachbarn
   verdraengen. min-width: 0 unterbindet das. */
.raster > *,
.duo > *,
.person > *,
.referenz > *,
.schritte > * { min-width: 0; }
/* Die Galerie stand hier mit, solange sie ein Raster war. Sie ist seit dem
   14. August ein Spaltensatz, und dort gibt es keine Rasterspalte, die sich
   aufblaehen koennte - die Regel waere wirkungslos und ihre Begruendung
   irrefuehrend. Siehe komponenten.css, Abschnitt Galerie. */

@media (min-width: 48rem) {
  .raster--2 { grid-template-columns: repeat(2, 1fr); }
  .raster--3 { grid-template-columns: repeat(3, 1fr); }
  .raster--4 { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 64rem) {
  .raster--4 { grid-template-columns: repeat(4, 1fr); }
}

.stapel > * + * { margin-top: var(--abstand-5); }
.stapel--eng > * + * { margin-top: var(--abstand-3); }
.stapel--weit > * + * { margin-top: var(--abstand-7); }

/* ===========================================================================
   Typografische Bausteine
   =========================================================================== */

/* Kleine Versalzeile ueber Ueberschriften. Traegt im Design den Akzentrot-Anteil. */
.augenbraue {
  display: block;
  margin-bottom: var(--abstand-4);
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  font-weight: 500;
  letter-spacing: var(--spatium-weit);
  text-transform: uppercase;
  color: var(--akzentrot);
}

.flaeche--blau .augenbraue,
.flaeche--bordeaux .augenbraue,
.flaeche--tinte .augenbraue { color: rgba(255, 255, 255, 0.82); }

.flaeche--sand .augenbraue { color: var(--bordeaux); }

.lead {
  font-size: var(--gr-lead);
  line-height: var(--zeilen-lead);
  font-weight: 400;
  max-width: 46ch;
}

.fliesstext { max-width: var(--lesebreite); }
.fliesstext p { font-size: var(--gr-text-gross); }

.fliesstext h2 { margin-top: var(--abstand-9); margin-bottom: var(--abstand-4); }
.fliesstext h3 { margin-top: var(--abstand-7); margin-bottom: var(--abstand-3); }
.fliesstext > :first-child { margin-top: 0; }

.fliesstext a { color: var(--akzentrot); text-decoration: underline; }
.fliesstext a:hover { text-decoration-thickness: 2px; }

/* Feiner Trennstrich unter Ueberschriften, wie in den Entwuerfen. */
.strich {
  width: 3.5rem;
  height: 1px;
  margin: var(--abstand-5) 0;
  background: currentColor;
  opacity: 0.45;
  border: 0;
}

.zentriert { text-align: center; }
.zentriert .lead, .zentriert .strich { margin-inline: auto; }


/* ===== komponenten.css ===== */
/* ===========================================================================
   Komponenten
   =========================================================================== */

/* ---------------------------------------------------------------- Kopfbereich */

.kopf {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--weiss);
  border-bottom: 1px solid var(--linie-zart);
  transition: background var(--uebergang), border-color var(--uebergang);
}

/* Auf Seiten mit dunklem Foto-Hero liegt der Kopf zunaechst transparent darueber.
   Sobald gescrollt wird, blendet er sich weiss ein (Klasse wird per JS gesetzt). */
.hat-dunklen-hero .kopf {
  position: fixed;
  inset-inline: 0;
  background: transparent;
  border-bottom-color: transparent;
}

/* Leseschutz hinter der Navigation.
   Der Kopfbereich liegt auf Seiten mit Foto-Hero transparent ueber dem Bild.
   Ohne diese Abdunklung haengt die Lesbarkeit der weissen Navigation davon ab,
   wie hell das jeweilige Foto an dieser Stelle ist - und die Fotos werden
   spaeter ausgetauscht. Der Verlauf ist kein gestalterisches Element, sondern
   sichert den Kontrast unabhaengig vom Motiv. Sobald der Kopf beim Scrollen
   weiss wird, blendet er sich aus. */
.hat-dunklen-hero .kopf::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 190px;
  background: linear-gradient(to bottom,
              rgba(10, 9, 9, 0.58) 0%,
              rgba(10, 9, 9, 0.3) 48%,
              rgba(10, 9, 9, 0) 100%);
  pointer-events: none;
  transition: opacity var(--uebergang);
}

.hat-dunklen-hero .kopf.kopf--fest::before { opacity: 0; }

/* Der Inhalt der Kopfzeile muss ueber dem Leseschutz liegen. */
.kopf__innen { position: relative; z-index: 1; }

.hat-dunklen-hero .kopf.kopf--fest {
  background: var(--weiss);
  border-bottom-color: var(--linie-zart);
}

.kopf__innen {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--abstand-5);
  min-height: 84px;
}

@media (min-width: 64rem) {
  .kopf__innen { min-height: 96px; }
}

.kopf__rechts {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 0.4rem + 1.6vw, 2.25rem);
}

/* Die hervorgehobene Handlungsaufforderung steht auf schmalen Geraeten in der
   Navigationsflaeche, damit die Kopfzeile ruhig bleibt.
   Der Vorfahrenselektor ist notwendig: Das Element traegt zugleich die Klasse
   .knopf, die weiter unten display: inline-flex setzt. Bei gleicher
   Spezifitaet gewaenne die spaetere Regel und der Knopf waere auch mobil
   sichtbar. */
.kopf .kopf__aktion {
  display: none;
  padding: 0.8rem 1.4rem;
  font-size: clamp(0.6875rem, 0.58rem + 0.22vw, 0.78rem);
  letter-spacing: 0.1em;
  white-space: nowrap;
}

@media (min-width: 64rem) {
  .kopf .kopf__aktion { display: inline-flex; }
}

.kopf__marke { display: block; flex-shrink: 0; line-height: 0; padding-block: var(--abstand-2); }

.kopf__marke img {
  width: clamp(148px, 15vw, 190px);
  height: auto;
}

/* Zwei Farbvarianten der Wortmarke, je nach Untergrund. */
.marke--hell { display: none; }
.hat-dunklen-hero .kopf:not(.kopf--fest) .marke--dunkel { display: none; }
.hat-dunklen-hero .kopf:not(.kopf--fest) .marke--hell { display: block; }

/* ---------------------------------------------------------------- Navigation */

.navigation__liste {
  display: flex;
  align-items: center;
  gap: clamp(0.9rem, 0.1rem + 1vw, 1.75rem);
  margin: 0;
  padding: 0;
  list-style: none;
}

.navigation__liste li { margin: 0; }

.navigation__link {
  display: inline-block;
  padding-block: var(--abstand-2);
  font-family: var(--schrift-versal);
  /* Etwas kleiner und mit engerer Laufweite als die uebrigen Versalien:
     Sieben Eintraege muessen ab 1024 px nebeneinander passen, ohne umzubrechen. */
  font-size: clamp(0.6875rem, 0.58rem + 0.22vw, 0.78rem);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  color: var(--tinte);
  position: relative;
  transition: color var(--uebergang);
}

.hat-dunklen-hero .kopf:not(.kopf--fest) .navigation__link { color: var(--weiss); }

.navigation__link::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--akzentrot);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--uebergang);
}

.navigation__link:hover::after,
.navigation__link:focus-visible::after { transform: scaleX(1); }

.navigation__link[aria-current="page"] { color: var(--akzentrot); }
.navigation__link[aria-current="page"]::after { transform: scaleX(1); }

.hat-dunklen-hero .kopf:not(.kopf--fest) .navigation__link[aria-current="page"] {
  color: var(--weiss);
}

.hat-dunklen-hero .kopf:not(.kopf--fest) .navigation__link::after { background: var(--weiss); }

.navigation--haupt { display: none; }

@media (min-width: 64rem) {
  .navigation--haupt { display: block; }
}

/* --- Schalter fuer die mobile Navigation --------------------------------- */

.nav-schalter {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  width: 44px; height: 44px;
  padding: 0 6px;
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--tinte);
}

.hat-dunklen-hero .kopf:not(.kopf--fest) .nav-schalter { color: var(--weiss); }

@media (min-width: 64rem) {
  .nav-schalter { display: none; }
}

.nav-schalter span {
  display: block;
  height: 1.5px;
  background: currentColor;
  transition: transform var(--uebergang), opacity var(--uebergang);
}

.nav-schalter[aria-expanded="true"] { color: var(--tinte); }
.nav-schalter[aria-expanded="true"] span:nth-child(1) { transform: translateY(7.5px) rotate(45deg); }
.nav-schalter[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-schalter[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7.5px) rotate(-45deg); }

/* --- Mobile Navigationsflaeche ------------------------------------------- */

.navigation--mobil {
  position: fixed;
  inset: 0;
  z-index: 99;
  display: flex;
  flex-direction: column;
  padding: 112px var(--rand) var(--abstand-8);
  background: var(--ivory);
  overflow-y: auto;
  visibility: hidden;
  opacity: 0;
  transition: opacity var(--uebergang), visibility var(--uebergang);
}

.navigation--mobil[data-offen="true"] { visibility: visible; opacity: 1; }

@media (min-width: 64rem) {
  .navigation--mobil { display: none; }
}

.navigation--mobil ul {
  margin: 0 0 var(--abstand-7);
  padding: 0;
  list-style: none;
}

.navigation--mobil li { margin: 0; border-bottom: 1px solid var(--linie); }

/* Bewusst auf nav eingegrenzt: Die Handlungsaufforderung steht als Geschwister
   neben dem nav-Element. Ohne diese Eingrenzung wuerde die Regel deren Textfarbe
   ueberschreiben - dunkler Text auf rotem Grund ergaebe nur 1,92:1. */
.navigation--mobil nav a {
  display: block;
  padding: var(--abstand-4) 0;
  font-family: var(--schrift-serif);
  font-size: 1.5rem;
  text-decoration: none;
  color: var(--tinte);
}

.navigation--mobil nav a[aria-current="page"] { color: var(--akzentrot); }

.navigation--mobil nav .navigation__nebenliste a {
  font-family: var(--schrift-text);
  font-size: var(--gr-text);
  color: var(--text-gedaempft);
}

.navigation--mobil > .knopf { align-self: flex-start; }

/* ---------------------------------------------------------------- Schaltflaechen */

.knopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--abstand-3);
  padding: 1.05rem 2.1rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  font-weight: 500;
  letter-spacing: var(--spatium-versal);
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--uebergang), color var(--uebergang),
              border-color var(--uebergang);
}

/* Akzentrot ist im Handoff den Schaltflaechen und aktiven Elementen vorbehalten. */
.knopf--haupt {
  background: var(--akzentrot);
  border-color: var(--akzentrot);
  color: var(--weiss);
}

.knopf--haupt:hover, .knopf--haupt:focus-visible { background: var(--bordeaux); border-color: var(--bordeaux); }

.knopf--rand {
  background: transparent;
  border-color: currentColor;
  color: inherit;
}

.knopf--rand:hover, .knopf--rand:focus-visible { background: var(--tinte); border-color: var(--tinte); color: var(--weiss); }

.flaeche--bordeaux .knopf--rand:hover,
.flaeche--blau .knopf--rand:hover,
.auf-dunkel .knopf--rand:hover { background: var(--weiss); border-color: var(--weiss); color: var(--tinte); }

.knopf--weiss { background: var(--weiss); border-color: var(--weiss); color: var(--bordeaux); }
.knopf--weiss:hover, .knopf--weiss:focus-visible { background: transparent; color: var(--weiss); }

/* Textlink mit Unterstrich, im Entwurf unter dem Hauptknopf.
   Der Unterstrich liegt auf einem Pseudoelement statt auf dem Rahmen. Dadurch
   laesst sich die Trefferflaeche nach oben und unten vergroessern, ohne dass
   die Linie vom Text abrueckt - auf Mobilgeraeten waeren 24 px sonst zu knapp. */
.textlink {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--abstand-3);
  padding-block: 0.7rem;
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  font-weight: 500;
  letter-spacing: var(--spatium-versal);
  text-transform: uppercase;
  text-decoration: none;
  transition: gap var(--uebergang), color var(--uebergang);
}

.textlink::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(0.7rem - 5px);
  height: 1px;
  background: currentColor;
}

.textlink:hover, .textlink:focus-visible { gap: var(--abstand-4); }

.textlink--rot { color: var(--akzentrot); }

.pfeil { width: 1.6em; height: 0.6em; flex-shrink: 0; }

.knopfreihe {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--abstand-5) var(--abstand-6);
}

/* ---------------------------------------------------------------- Bildflaechen */

.bild {
  position: relative;
  display: block;
  overflow: hidden;
  background: var(--sand-blass);
  border-radius: var(--radius);
}

/* --fokus setzt der Erzeuger nur, wenn der Schwerpunkt eines Bildes nicht in
   der Mitte liegt. Wirksam wird er dort, wo die Flaeche ein anderes
   Seitenverhaeltnis hat als das Bild und cover deshalb beschneidet - vor allem
   im Hero, dessen Hoehe der Fensterhoehe folgt. */
.bild img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--fokus, 50% 50%);
}

/* Sobald das Fenster schmaler wird als 88 % seiner Hoehe, kippt die Herohoehe
   min(88svh, 820px) ins Hochformat - der Fall Telefon hochkant. Dann bleibt
   von einem Querformat nur ein schmaler Streifen, und die Stelle, die quer
   richtig sitzt, liegt oft schon ausserhalb. Deshalb ein eigener Schwerpunkt,
   der auf den allgemeinen zurueckfaellt, solange keiner gesetzt ist. */
@media (max-aspect-ratio: 88/100) {
  .bild img { object-position: var(--fokus-hoch, var(--fokus, 50% 50%)); }
}

.bild--3zu2 { aspect-ratio: 3 / 2; }
.bild--4zu3 { aspect-ratio: 4 / 3; }
.bild--1zu1 { aspect-ratio: 1 / 1; }
.bild--hoch { aspect-ratio: 4 / 5; }
.bild--breit { aspect-ratio: 16 / 9; }

/* Unbeschnitten: Die Flaeche uebernimmt die Masse der Aufnahme, die der
   Erzeuger als --seitenverhaeltnis mitgibt. Damit bleibt object-fit: cover
   wirkungslos, denn Flaeche und Bild haben dasselbe Verhaeltnis - es wird
   also nichts weggeschnitten, ohne dass irgendwo ein Rand entsteht.

   Der Rueckfall 3/2 gilt fuer den leeren Bildplatz: Dort gibt es keine Datei,
   aus der sich etwas ablesen liesse. */
.bild--original { aspect-ratio: var(--seitenverhaeltnis, 3 / 2); }

/* Fuellende Bilder richten sich nach ihrem Rahmen, nicht nach dem
   Seitenverhaeltnis. Das aspect-ratio muss hier ausdruecklich zurueckgenommen
   werden: Wird die Flaeche in einem Raster auf volle Hoehe gestreckt, rechnet
   der Browser sonst aus dieser Hoehe eine Breite - und die sprengt die Spalte,
   sodass das Bild in den Textbereich hineinragt. */
.bild--fuellend {
  aspect-ratio: auto;
  width: 100%;
  height: 100%;
}

figure { margin: 0; }

.bildunterschrift {
  margin-top: var(--abstand-3);
  font-size: var(--gr-klein);
  line-height: 1.5;
  color: var(--text-gedaempft);
}

.flaeche--blau .bildunterschrift,
.flaeche--bordeaux .bildunterschrift { color: rgba(255, 255, 255, 0.8); }

.flaeche--sand .bildunterschrift { color: var(--text-auf-sand); opacity: 0.8; }

/* --- Platzhalter, solange kein Foto hinterlegt ist ------------------------
   Bewusst ruhig und markenkonform: die Seite wirkt fertig, die Luecke bleibt
   trotzdem erkennbar. Sobald die Bilddatei existiert, entfaellt dieser Block. */
.bild--platzhalter {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--abstand-3);
  padding: var(--abstand-5);
  text-align: center;
  background: var(--sand-blass);
  border: 1px solid var(--linie);
  /* Der Rahmen gibt die Groesse vor, nicht der Beschriftungstext. */
  overflow: hidden;
}

.bild--platzhalter::before {
  content: "";
  width: clamp(34px, 6vw, 54px);
  aspect-ratio: 1;
  flex-shrink: 0;
  background: currentColor;
  color: var(--sand);
  opacity: 0.55;
  -webkit-mask: var(--platzhalter-symbol) center / contain no-repeat;
  mask: var(--platzhalter-symbol) center / contain no-repeat;
}

.bild--platzhalter span {
  max-width: 32ch;
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  letter-spacing: 0.08em;
  line-height: 1.5;
  text-transform: uppercase;
  color: var(--text-gedaempft);
  /* Lange Motivbeschreibungen werden nach der letzten passenden Zeile
     abgeschnitten, statt aus der Flaeche herauszulaufen. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 5;
  line-clamp: 5;
  overflow: hidden;
}

/* In flachen Flaechen bleibt weniger Platz für die Beschriftung. */
.bild--platzhalter.bild--breit span,
.bild--platzhalter.bild--3zu2 span { -webkit-line-clamp: 4; line-clamp: 4; }

/* Dunkle Variante fuer Hero-Flaechen: Dort liegt heller Text darueber, ein
   heller Platzhalter waere unlesbar. */
.bild--dunkel.bild--platzhalter {
  background: var(--tinte);
  border-color: transparent;
  justify-content: flex-end;
  align-items: flex-end;
  padding: var(--abstand-6);
}

.bild--dunkel.bild--platzhalter::before { color: var(--sand); opacity: 0.28; }

.bild--dunkel.bild--platzhalter span {
  max-width: 26ch;
  text-align: right;
  color: rgba(255, 255, 255, 0.4);
}

:root {
  --platzhalter-symbol: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath d='M57 16H45.6l-3.2-6.4A3 3 0 0 0 39.7 8H24.3a3 3 0 0 0-2.7 1.6L18.4 16H7a3 3 0 0 0-3 3v30a3 3 0 0 0 3 3h50a3 3 0 0 0 3-3V19a3 3 0 0 0-3-3Zm1 33a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V19a1 1 0 0 1 1-1h12.6l3.8-7.5a1 1 0 0 1 .9-.5h15.4a1 1 0 0 1 .9.5l3.8 7.5H57a1 1 0 0 1 1 1v30ZM32 22a12 12 0 1 0 0 24 12 12 0 0 0 0-24Zm0 22a10 10 0 1 1 0-20 10 10 0 0 1 0 20Z'/%3E%3C/svg%3E");
}

/* ---------------------------------------------------------------- Hero */

.hero {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: min(88svh, 820px);
  padding-top: 140px;
  padding-bottom: clamp(3rem, 2rem + 5vw, 6rem);
  background: var(--tinte);
  color: var(--weiss);
  overflow: hidden;
}

.hero__bild {
  position: absolute;
  inset: 0;
  z-index: 0;
}

/* Der Bildplatz fuellt den Hero vollstaendig aus. Das Seitenverhaeltnis der
   Bildklasse wird hier bewusst aufgehoben - massgeblich ist die Herohoehe. */
.hero__bild > .bild {
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
  height: 100%;
  border-radius: 0;
}

.hero__bild img { width: 100%; height: 100%; object-fit: cover; }

/* Im Hero darf sich das Bild etwas weiter bewegen als in kleineren Flaechen. */
.bewegung .hero__bild > .bild { --parallax-weg: 70px; }

/* Der Ueberhang fuer den Parallax liegt im Hero fast vollstaendig unten.
   Vorher lagen 70 px des Bildes dauerhaft ueber der Oberkante und waren nie zu
   sehen - der Schwerpunkt zielte damit auf eine Stelle im Unsichtbaren, und das
   Motiv rutschte nach oben unter die Navigationsleiste.
   Moeglich ist das, weil der Hero am Seitenanfang steht: Von dort wandert das
   Bild beim Scrollen nach oben, wofuer der Ueberhang unten da ist. Nach unten
   bewegt es sich nur, wenn die Hero-Oberkante nicht ganz oben liegt - dann um
   wenige Pixel. Die 12 px decken das ab, und das Skript begrenzt die Bewegung
   zusaetzlich auf genau diesen Wert (--parallax-oben, siehe seite.js). */
.bewegung .hero__bild > .bild {
  --parallax-oben: 12px;
}
.bewegung .hero__bild > .bild > img {
  height: calc(100% + var(--parallax-weg) + var(--parallax-oben));
  margin-top: calc(-1 * var(--parallax-oben));
}

/* Dunkler Verlauf nur zur Lesbarkeit des Textes, kein dekorativer Farbverlauf. */
.hero__bild::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to right, rgba(12, 10, 10, 0.88) 0%, rgba(12, 10, 10, 0.62) 42%,
                    rgba(12, 10, 10, 0.15) 78%, rgba(12, 10, 10, 0.05) 100%),
    linear-gradient(to top, rgba(12, 10, 10, 0.7) 0%, rgba(12, 10, 10, 0) 45%);
}

/* Auf schmalen Schirmen nimmt der Text die ganze Breite ein, und dort ist der
   Verlauf von links nach rechts fast durchsichtig. Gemessen am 18. August auf
   der Oldenburger Ortsseite bei 375 px: 12,5 Prozent der Ueberschrift und
   21,2 Prozent der Brotkrume lagen unter 4,5:1, weil die Zeilenenden ueber
   einem hellen Gesicht standen. Auf 1400 px war dieselbe Seite bei 6,5:1.

   Deshalb hier eine flaechigere Abdunklung statt eines Verlaufs zur Seite. Der
   Wert ist gerechnet, nicht geschaetzt: Weisser Text braucht gegen ein weisses
   Bildpixel 55 Prozent Deckung, um 4,5:1 zu erreichen (255 * (1 - a) + 12a
   <= 120). 0,58 als Grundwert haelt das auch am hellsten Punkt ein. */
@media (max-width: 47.999rem) {
  .hero__bild::after {
    background:
      linear-gradient(to right, rgba(12, 10, 10, 0.86) 0%, rgba(12, 10, 10, 0.7) 55%,
                      rgba(12, 10, 10, 0.58) 100%),
      linear-gradient(to top, rgba(12, 10, 10, 0.5) 0%, rgba(12, 10, 10, 0) 62%);
  }
}

.hero__innen { position: relative; z-index: 1; width: 100%; }

/* Die Zeilenlaenge des Textfeldes. Ohne Angabe 46ch wie bisher; wo eine Seite
   `"textbreite"` fuehrt, setzt der Erzeuger `--hero-breite` als Vielfaches von
   `--gr-hero` (siehe bausteine.hero).

   Warum zweierlei Mass: `ch` haengt an der Grundschrift, und die wandert nur
   zwischen 16 und 17 px, waehrend die Hero-Ueberschrift von 40 auf 76 px geht.
   Ein Feld von 46ch ist deshalb bei 1400 px und bei 1024 px fast gleich breit,
   die Schrift darin aber nicht - gemessen an der Verdener Ortsseite trug
   dieselbe Ueberschrift oben vier Zeilen und unten sechs. Als Vielfaches der
   Ueberschrift bleibt der Umbruch ueber alle Breiten derselbe, und genau das
   braucht, wer eine bestimmte Zeile zusammenhalten will.

   Umgestellt wird nur, wo jemand es einstellt. Ein neuer Rueckfallwert in
   derselben Einheit haette alle 30 Hero-Seiten getroffen: 6,5 (das Mass bei
   1400 px) macht aus den vier Zeilen der Verdener Seite bei 1024 px sechs und
   den Hero 244 px hoeher.

   Wer den Wert setzt, misst den Kontrast der Ueberschrift nach - ein breiteres
   Feld reicht weiter nach rechts, und dort ist der Verlauf durchsichtiger. */
.hero__text { max-width: var(--hero-breite, 46ch); }

/* Die Brotkrume einer Unterseite steht im Hero, nicht darueber - dort laege sie
   hinter der fest eingeblendeten Kopfzeile. Sie sitzt an der Stelle, an der ein
   Seitenkopf seine Augenbraue traegt, und uebernimmt deren Aufgabe.

   Gemessene Kontraste auf dem abgedunkelten Foto: Der Verlauf des Heros liegt
   bei 0,58 Deckung ueber Tinte, weisser Text darauf kommt sicher ueber 4,5:1.
   Die Zwischenstufen stehen deshalb auf 0,82 statt auf voller Deckung; damit
   ordnet sich die Zeile der Ueberschrift unter, ohne unter den Grenzwert zu
   fallen. Die eigene Huelle entfaellt, weil der Hero seine schon hat. */
.hero .brotkrumen {
  padding-block: 0;
  border-bottom: 0;
  margin-bottom: var(--abstand-5);
}
.hero .brotkrumen > .huelle { padding-inline: 0; max-width: none; }
.hero .brotkrumen a { color: rgba(255, 255, 255, 0.82); }
.hero .brotkrumen a:hover { color: var(--weiss); text-decoration: underline; }
.hero .brotkrumen [aria-current="page"] { color: var(--weiss); }
.hero .brotkrumen li + li::before { color: rgba(255, 255, 255, 0.55); }

.hero h1 {
  font-size: var(--gr-hero);
  font-weight: 300;
  letter-spacing: -0.015em;
}

/* Seit dem 21. August tragen auch die drei Leistungsseiten einen Hero, und
   ihre H1 sind laenger als die der Ortsseiten: „Veranstaltungsfotografie“ hat
   24 Zeichen, „Unternehmensfotografie“ 22. Gemessen bei 375 px stehen der
   Zeile 327 px zur Verfuegung, das Wort braucht bei 40,7 px Schriftgrad rund
   440 und lief aus dem Bild.

   Die Trennung greift deshalb nur unterhalb von 48 rem und erst ab 20
   Zeichen. Darueber ist Platz, und in der Display-Schrift sieht eine Trennung
   schlechter aus – das ist der Grund, aus dem die Sammelregel weiter unten
   Ueberschriften ausspart.

   Die Grenze von 20 ist gemessen, nicht gewaehlt: Ueber alle Hero-Seiten
   tragen genau drei H1 ein Wort ab 18 Zeichen. „Unternehmensfotografie“ (22)
   und „Veranstaltungsfotografie“ (24) passen nicht und werden getrennt,
   „Businessfotografie“ (18) auf der Startseite passt mit rund 318 px in die
   327 px breite Zeile und bleibt dadurch unveraendert. */
@media (max-width: 47.999rem) {
  .hero h1 {
    hyphens: auto;
    hyphenate-limit-chars: 20 6 5;
    overflow-wrap: break-word;
  }
}

/* Zweistufige Ueberschrift: Leistung gross, Ortszusatz untergeordnet.
   Beides steht in derselben H1 und bleibt fuer Suchmaschinen vollstaendig. */
.hero__h1-haupt { display: block; max-width: 18ch; }

.hero__h1-zusatz {
  display: block;
  margin-top: var(--abstand-4);
  font-family: var(--schrift-versal);
  font-size: clamp(0.8125rem, 0.72rem + 0.4vw, 1rem);
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: var(--spatium-versal);
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.78);
}

.hero__lead {
  margin-top: var(--abstand-5);
  font-size: var(--gr-lead);
  line-height: var(--zeilen-lead);
  color: rgba(255, 255, 255, 0.9);
  max-width: 40ch;
}

.hero .knopfreihe { margin-top: var(--abstand-7); }

/* --- Kleiner Kopfbereich fuer Unterseiten -------------------------------- */

.seitenkopf {
  padding-top: clamp(2.5rem, 1.5rem + 5vw, 5rem);
  padding-bottom: clamp(2.5rem, 1.5rem + 5vw, 5rem);
}

.seitenkopf h1 { max-width: 20ch; }
.seitenkopf .lead { margin-top: var(--abstand-5); }

.seitenkopf__raster { display: grid; gap: var(--abstand-7); align-items: end; }

/* Rasterspalten sind so breit wie ihr Inhalt, solange nichts anderes dasteht.
   Die Sammelregel in basis.css kennt diesen Kopf nicht, und ohne min-width: 0
   draengt ein Bild oder eine lange Ueberschrift die Nachbarspalte zusammen. */
.seitenkopf__raster > * { min-width: 0; }

@media (min-width: 60rem) {
  .seitenkopf__raster { grid-template-columns: 1.15fr 1fr; gap: var(--abstand-9); }
}

/* Kopf mit Bild: Text und Bild stehen gleich breit nebeneinander, an der Mitte
   ausgerichtet. Die Grundfassung richtet die Spalten unten aus, weil dort ein
   kurzer Nebentext neben einer hohen Ueberschrift steht; eine Bildflaeche ist
   dagegen selbst hoch, und unten ausgerichtet haengt der Text an ihrer
   Unterkante. */
.seitenkopf--mit-bild .seitenkopf__raster { align-items: center; }
.seitenkopf--mit-bild h1 { max-width: 16ch; }

@media (min-width: 60rem) {
  .seitenkopf--mit-bild .seitenkopf__raster { grid-template-columns: 1fr 1fr; }
}

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

.brotkrumen {
  padding-block: var(--abstand-4);
  border-bottom: 1px solid var(--linie-zart);
  font-size: var(--gr-klein);
}

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

.brotkrumen li { margin: 0; display: flex; align-items: center; gap: var(--abstand-3); }
.brotkrumen li + li::before { content: "›"; color: var(--sand); }
.brotkrumen a { color: var(--text-gedaempft); text-decoration: none; }
.brotkrumen a:hover { color: var(--akzentrot); text-decoration: underline; }
.brotkrumen [aria-current="page"] { color: var(--tinte); }

/* ---------------------------------------------------------------- Einleitung */

/* Ivory-Flaeche mit grosser, organisch angeschnittener Sandflaeche (Handoff). */
.einleitung { position: relative; overflow: hidden; }

.einleitung__form {
  position: absolute;
  top: -12%;
  right: -14%;
  width: min(58%, 620px);
  aspect-ratio: 1;
  background: var(--sand);
  border-radius: 46% 54% 38% 62% / 52% 42% 58% 48%;
  opacity: 0.5;
  z-index: 0;
  pointer-events: none;
}

@media (max-width: 47.99rem) {
  .einleitung__form { width: 78%; top: -6%; right: -28%; opacity: 0.42; }
}

.einleitung__innen { position: relative; z-index: 1; }
.einleitung__text { max-width: 62ch; }

/* ---------------------------------------------------------------- Vertrauenssignale */

.vertrauen {
  display: grid;
  gap: var(--abstand-5);
  margin-top: var(--abstand-8);
  padding-top: var(--abstand-7);
  border-top: 1px solid var(--linie);
}

@media (min-width: 40rem) { .vertrauen { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .vertrauen { grid-template-columns: repeat(4, 1fr); gap: var(--abstand-6); } }

.vertrauen__punkt { display: flex; flex-direction: column; gap: var(--abstand-2); }

.vertrauen__wert {
  font-family: var(--schrift-serif);
  font-size: var(--gr-h3);
  line-height: 1.15;
  color: var(--bordeaux);
}

.vertrauen__text { font-size: var(--gr-klein); line-height: 1.6; color: var(--text-gedaempft); }

/* ---------------------------------------------------------------- Duo-Flaeche */

/* Bild und Farbflaeche buendig nebeneinander - das Grundmuster des Entwurfs. */
.duo { display: grid; }

/* Umbruch bei 64rem, nicht bei 56rem. Dazwischen wird die Bildflaeche schmal
   und zugleich hoch, weil der Text in der Nachbarspalte mehr Zeilen braucht -
   und cover zoomt dann stark hinein: Bei 900 px Fensterbreite war die Flaeche
   450 x 794 px, das Bild musste auf 1059 px Breite skaliert werden, Faktor
   2,35. Untereinander nimmt die Flaeche die volle Breite und der Zoom faellt
   weg. Der Duo bleibt oberhalb davon wie gehabt. */
@media (min-width: 64rem) {
  .duo { grid-template-columns: 1fr 1fr; align-items: stretch; }
  .duo--bild-rechts .duo__bild { order: 2; }
  .duo--schmal-text { grid-template-columns: 1.15fr 1fr; }
}

.duo__bild { position: relative; min-height: clamp(260px, 42vw, 520px); }
.duo__bild .bild, .duo__bild img { height: 100%; }

/* Eine Grafik wird vollstaendig gezeigt, nicht wie ein Foto formatfuellend
   beschnitten - bei einer Karte fehlten sonst die Raender.
   Die Traegerflaeche traegt denselben Ton wie die Grafik selbst. Die
   Nachbarspalte ist meist hoeher: bei 1280 px Fenster 802 px gegen 663 px
   Kartenhoehe. Ohne den Ton stuende dort eine weisse Luecke von 139 px, mit
   ihm steht die Karte mittig in einer durchgehenden Flaeche. */
.duo__bild--grafik {
  min-height: 0;
  display: flex;
  align-items: center;
  background: var(--blau-blass);
}
.duo__bild--grafik > svg { display: block; width: 100%; height: auto; }

.duo__text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--flaeche-innen);
}

.duo__text > * { max-width: 42ch; }

.duo__text h2, .duo__text h3 { font-weight: 400; }

/* ---------------------------------------------------------------- Karten */

.karte {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--weiss);
  border: 1px solid var(--linie-zart);
  border-radius: var(--radius);
  overflow: hidden;
  transition: border-color var(--uebergang), transform var(--uebergang);
}

.karte:hover { border-color: var(--sand); }

a.karte { text-decoration: none; color: inherit; }
a.karte:hover h3 { color: var(--akzentrot); }

.karte__inhalt {
  display: flex;
  flex-direction: column;
  gap: var(--abstand-3);
  flex-grow: 1;
  padding: var(--abstand-6);
}

.karte h3 { transition: color var(--uebergang); }
.karte__meta {
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  letter-spacing: var(--spatium-versal);
  text-transform: uppercase;
  color: var(--akzentrot);
}

.karte__text { font-size: var(--gr-text); color: var(--text-gedaempft); }
.karte .textlink { margin-top: auto; padding-top: var(--abstand-4); align-self: flex-start; }

.karte--flach { background: transparent; border: 0; }
.karte--flach .karte__inhalt { padding: var(--abstand-5) 0 0; }

/* Karten auf farbigen Flaechen erben die Textfarbe der Flaeche. Ohne diese
   Regeln waere der gedaempfte Grauton auf Blau oder Bordeaux unlesbar. */
.flaeche--blau .karte--flach,
.flaeche--bordeaux .karte--flach,
.flaeche--sand .karte--flach { color: inherit; }

.flaeche--blau .karte__text,
.flaeche--bordeaux .karte__text { color: rgba(255, 255, 255, 0.86); }

.flaeche--sand .karte__text { color: var(--text-auf-sand); opacity: 0.88; }

.flaeche--blau .karte__meta,
.flaeche--bordeaux .karte__meta { color: rgba(255, 255, 255, 0.75); }

.flaeche--sand .karte__meta { color: var(--bordeaux); }

.flaeche--blau .karte--flach .textlink,
.flaeche--bordeaux .karte--flach .textlink { color: var(--weiss); }

.flaeche--sand .karte--flach .textlink { color: var(--bordeaux); }

/* Vorschau aus vielen kleinen Fotos, statt eines einzelnen Bildes.

   Sechs Spalten mal vier Zeilen: Bei 3:2 Gesamtformat ist eine Zelle
   Breite/6 mal Hoehe/4, und Hoehe ist Breite/1,5 - beides ergibt Breite/6,
   die Zellen sind also quadratisch. Bei 353 px Kachelbreite misst eine Zelle
   59 px; das ist klein, aber als Andeutung von "viele Bilder" gerade richtig.

   Die Fuge ist 2 px und nicht 0: Ohne sie liefen benachbarte Fotos ineinander
   und das Raster saehe aus wie ein einziges unscharfes Bild. */
/* Die Zahl der Zellen folgt der Breite der **Kachel**, nicht der des Fensters,
   und das ist hier der ganze Punkt. Gemessen am 26. August mit sechs Spalten:

     Fenster   Kachel   Zelle
       375 px   325 px   52 px
       600 px   540 px   88 px
       768 px   206 px   33 px   <- der engste Fall, nicht das Handy
      1400 px   351 px   57 px

   Ab 48 rem stehen drei Kacheln nebeneinander, deshalb faellt die Kachel dort
   auf ein Drittel und die Zellen werden kleiner als auf dem Telefon. Der
   Auftraggeber hat es am Handy gemeldet; ein Media-Query auf die Fensterbreite
   haette genau den schlimmsten Fall verfehlt.

   Deshalb ein Container-Query. Drei Spalten sind die Grundeinstellung, sechs
   erst ab 512 px Kachelbreite - dort traegt eine Zelle 84 px. Wo ein Browser
   keine Container-Queries kennt, bleibt es bei drei Spalten; das ist auf jeder
   gemessenen Breite brauchbar (67 bis 179 px).

   Die Schwelle stand zuerst auf 540 px, dem gemessenen Wert bei 600 px
   Fenster, und griff genau dort nicht: Ein Container-Query misst die
   Content-Box, und die ist wegen der 1 px Rahmen je Seite 539,61 px breit.
   Eine Schwelle gehoert nicht auf den gemessenen Wert selbst.

   Drei mal zwei und sechs mal vier sind dabei keine freie Wahl: Bei 3:2
   Gesamtformat ist eine Zelle nur dann quadratisch, wenn die Spaltenzahl das
   Anderthalbfache der Zeilenzahl ist. Ganzzahlig geht das bei (3,2), (6,4)
   und (9,6). */
.karte { container-type: inline-size; }

.karte__bildraster {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  aspect-ratio: 3 / 2;
  gap: 2px;
  background: var(--ivory);
  overflow: hidden;
}

/* Bei drei Spalten passen sechs Fotos in die Flaeche. Genommen wird jedes
   vierte und nicht die ersten sechs: Die 24 sind ueber die Projekte gestreut,
   und ein Griff von vorn zeigte sonst nur den Anfang dieser Streuung. */
.karte__bildraster img:not(:nth-child(4n + 1)) { display: none; }

/* Derselbe Selektor wie oben, nicht das kuerzere `img`: Eine Pseudoklasse
   zaehlt in der Spezifitaet mit, und ein Container-Query aendert daran nichts.
   Mit `.karte__bildraster img` blieben die 18 ausgeblendeten Zellen leer und
   das Raster zeigte sechs Fotos in sechs Spalten. */
@container (min-width: 512px) {
  .karte__bildraster { grid-template-columns: repeat(6, 1fr); }
  .karte__bildraster img:not(:nth-child(4n + 1)) { display: block; }
}

.karte__bildraster img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Karten mit oberer Trennlinie – ruhige Variante fuer Aufzaehlungen auf Farbe. */
.karte--linie { background: transparent; border: 0; border-top: 1px solid currentColor; }
.karte--linie .karte__inhalt { padding: var(--abstand-5) 0 0; }

/* Eingefaerbte Karten. Die Zuordnung folgt der Rollenfarbe der Leistung:
   Business/Portraet -> Sand, Event/Veranstaltung -> Blau. Wer von der
   Leistungsseite kommt, findet dieselbe Farbe wieder.

   Gemessen, nicht geschaetzt:
     #3a1418 auf --sand ......... 6,93:1
     Weiss auf --blau-tief ...... 6,02:1
   Deshalb traegt die blaue Karte die abgedunkelte Stufe, nicht --blau
   (Weiss darauf nur 3,63:1). */
/* Der Selektor nennt das a mit: Eine anklickbare Kachel ist ein a, und
   "a.karte { color: inherit }" weiter oben hat hoehere Spezifitaet als eine
   einfache Klasse. Ohne das stand der Text in Tinte auf Blau - gemessen
   2,80:1 statt der 6,02:1, die Weiss dort erreicht. */
.karte--sand, a.karte--sand {
  background: var(--sand);
  border-color: var(--sand);
  color: var(--text-auf-sand);
}
.karte--blau, a.karte--blau {
  background: var(--blau-tief);
  border-color: var(--blau-tief);
  color: var(--weiss);
}
/* Ivory fuer die Kachel zur Bilduebersicht auf /referenzen/, dieselbe Flaeche
   wie der Seitenkopf darueber. Zwei Anlaeufe stehen davor: Salbei war ein
   Bruch mit dem Farbkonzept (dort mit 5 bis 7 Prozent vorgesehen, eine ganze
   Kachel ist mehr), Bordeaux war zu laut - der Auftraggeber am 26. August:
   "das rot ist knall". Er hat die Farbe des Seitenkopfs genannt, und die
   traegt die Kachel jetzt. Tinte darauf: 15,92:1.

   Der Rahmen ist bewusst nicht Ivory: Auf der weissen Flaeche des Blocks
   waere die Kachel sonst randlos. --linie-zart grenzt sie ab, ohne sie
   herauszuheben. */
.karte--ivory, a.karte--ivory {
  background: var(--ivory);
  border-color: var(--linie-zart);
  color: var(--tinte);
}

/* Metazeile und Flaechentext erben die Textfarbe. Ihre Vorgabewerte waeren
   hier unlesbar: --akzentrot auf --blau-tief erreicht 1,46:1, auf --sand
   3,76:1 - beides zu wenig fuer die kleine Versalzeile. */
.karte--sand .karte__meta, .karte--blau .karte__meta, .karte--ivory .karte__meta,
.karte--sand .karte__text, .karte--blau .karte__text,
.karte--ivory .karte__text { color: inherit; }

/* Dasselbe gilt fuer den Verweis am Kachelfuss. Er stand bis zum 20. August
   2026 auf keiner farbigen Kachel; die Kacheln auf /referenzen/ sind selbst
   der Link und tragen keinen sichtbaren Verweistext. Auf /ratgeber/ tun sie es,
   und dort erreichte .textlink--rot auf Sand 3,76:1 und auf Blau 1,46:1,
   also einmal zu wenig und einmal praktisch unlesbar. Geerbt sind es 6,93:1
   beziehungsweise 6,02:1. */
.karte--sand .textlink--rot,
.karte--blau .textlink--rot,
.karte--ivory .textlink--rot { color: inherit; }

.karte--sand:hover, .karte--blau:hover,
.karte--ivory:hover { border-color: currentColor; }
a.karte--sand:hover h3, a.karte--blau:hover h3, a.karte--ivory:hover h3 {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* ---------------------------------------------------------------- Statement */

.statement { text-align: center; }

.statement__text {
  position: relative;
  max-width: 24ch;
  margin-inline: auto;
  font-family: var(--schrift-serif);
  font-size: var(--gr-statement);
  font-weight: 300;
  line-height: 1.3;
  text-wrap: balance;
}

.statement--breit .statement__text { max-width: 34ch; }

.statement__zeichen {
  display: block;
  margin-bottom: var(--abstand-4);
  font-family: var(--schrift-serif);
  font-size: 3.5rem;
  line-height: 0.6;
  opacity: 0.45;
}

.statement__quelle {
  margin-top: var(--abstand-6);
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  letter-spacing: var(--spatium-versal);
  text-transform: uppercase;
  opacity: 0.85;
}

/* ---------------------------------------------------------------- Person */

.person { display: grid; gap: 0; }

.person__bild { min-height: clamp(320px, 45vw, 560px); }
.person__bild .bild { height: 100%; }

/* Untereinander bleibt das Portraet hochkant und nimmt die volle Breite - auf
   einem Telefon genau richtig. Auf einem breiten, aber flachen Fenster nicht:
   Bei 1000 px Breite waere die Flaeche 985 x 1231 px und damit hoeher als der
   Schirm. Dort bekommt sie deshalb eine Obergrenze und steht mittig.

   420 px als fester Wert, nicht an die Fensterhoehe gebunden: Eine Bindung an
   svh waere naheliegend, macht das Ergebnis aber von einer Groesse abhaengig,
   die sich mit ausgefahrener Adressleiste oder eingeblendeter Tastatur
   aendert - das Bild spraenge dann waehrend der Bedienung um.

   Die untere Grenze von 34rem ist noetig, nicht schmueckend: Ohne sie griffe
   die Begrenzung auch auf dem Telefon und machte das Bild dort kleiner, statt
   es zu lassen. Betroffen ist also nur der Bereich dazwischen, in dem das
   Fenster breit genug fuer ein zu hohes Bild ist, aber noch zu schmal fuer
   zwei Spalten. */
@media (min-width: 34rem) and (max-width: 63.999rem) {
  .person__bild {
    max-width: 420px;
    margin-inline: auto;
  }
}

/* Nebeneinander gilt eine andere Regel als untereinander. Einspaltig gibt es
   keine Textspalte, die eine Hoehe vorgaebe - dort traegt das 4:5 des Bildes
   die Flaeche, und das bleibt so.

   Der Umbruch liegt hier bei 64rem statt der sonst ueblichen 56rem. Grund ist
   eine Rueckkopplung, die nur dieser Baustein hat: Das Bild nimmt die
   Zeilenhoehe und rechnet daraus seine Breite. Wird das Fenster schmaler,
   braucht der Text mehr Zeilen, die Zeile waechst, das Bild wird breiter, dem
   Text bleibt noch weniger - und so weiter. Gemessen belegte das Bild bei
   900 px siebzig Prozent der Breite, fuer den Text blieben 270 px.

   Eine Obergrenze allein loest das nicht: fit-content(36 %) wird von der
   Mindestbreite des Bildes ueberstimmt, sobald die Zeile hoch genug ist.
   Unterhalb von 64rem stehen Bild und Text deshalb untereinander, wo es keine
   gemeinsame Zeilenhoehe gibt und die Rueckkopplung nicht entstehen kann. */
@media (min-width: 64rem) {

  /* Die Spalte richtet sich nach dem Bild (auto) - so kann das Bild sie nie
     sprengen. Mit fit-content(36 %) tat es genau das: Die Spalte war 455 px
     breit, das Bild 497 px, und die ueberstehenden 42 px lagen ueber der
     Textflaeche. Bei 980 px waren es 144 px, dann stand das Bild im Text. */
  .person { grid-template-columns: auto 1fr; }

  /* Das Bild fuellt seine Flaeche ganz, wie jedes andere Bild der Website
     auch - es steht nicht mit Rand darin. Dafuer traegt die Flaeche das
     Seitenverhaeltnis der Aufnahme und nimmt die Zeilenhoehe an; ihre Breite
     ergibt sich daraus. cover hat damit nichts wegzuschneiden, das Portraet
     bleibt vollstaendig. */
  .person__bild {
    aspect-ratio: 4 / 5;
    height: 100%;

    /* Die Groesse folgt dem Fenster, nicht der Texthoehe. Ohne die min-height
       bliebe das Bild so hoch wie der Text daneben - auf einem grossen Schirm
       zu klein fuer ein Portraet, das die Seite traegt.

       Drei Groessen begrenzen es, und jede hat einen eigenen Grund:
         620 px  Untergrenze, damit es auf flachen Fenstern nicht schrumpft
         82 vh   die eigentliche Groesse: waechst mit der Fensterhoehe
         880 px  Obergrenze, damit der Abschnitt auf einen Schirm passt
         52 vw   Obergrenze gegen die Breite. Ohne sie belegte das Bild bei
                 1024 px Fenster 65 % der Breite und liess dem Text 258 px -
                 die Rueckkopplung von vorher in anderer Gestalt. */
    min-height: min(max(620px, 82vh), 880px, 52vw);

    /* Der Deckel unterbricht die Rueckkopplung endgueltig: Ab hier waechst das
       Bild nicht weiter mit, auch wenn die Zeile hoeher wird. */
    max-height: min(92vh, 960px, 56vw);
  }
}

.person__text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--flaeche-innen);
}

.person__text > * { max-width: 46ch; }

/* Der Innenabstand folgt nebeneinander zusaetzlich der Fensterhoehe. Ohne das
   waere der Abschnitt immer 666 px hoch - auf einem 1080er Schirm
   unproblematisch, auf einem Notebook mit 768 px aber mehr, als nach den
   Browserleisten uebrig bleibt. Gemessen: 602 px bei 640 px nutzbarer Hoehe,
   656 px bei 1080 px. Der Text selbst braucht 526 px; darunter kann kein
   Abstand mehr helfen.

   Diese Regel steht bewusst hinter der allgemeinen darueber: Beide sind
   gleich spezifisch, es gewinnt also die spaetere. Im Medienblock weiter oben
   waere sie wirkungslos. */
@media (min-width: 64rem) {
  .person__text { padding: min(var(--flaeche-innen), 6svh); }
}

/* Kamerazeichnung in der Salbeiflaeche hinter dem Text.

   Die Zeichnung kommt vom Auftraggeber (Vorlage *linienzeichnung*.png,
   oertlich, nicht versioniert). Freigestellt wird sie von
   werkzeuge/kamerazeichnung_aufbereiten.py - dort steht auch, warum ein
   einfacher Schwellenwert nicht reicht und wie die geschwungene Kante des
   Entwurfs herausfaellt. Wer die Vorlage austauscht, ruft das Werkzeug auf und
   traegt hier das Seitenverhaeltnis der neuen Maske ein.

   Als Maske, nicht als Bild: So bestimmt die CSS die Farbe, und die Zeichnung
   passt sich der Flaeche an, statt einen eigenen Ton mitzubringen. Die Datei
   traegt deshalb nur einen Alphakanal - waere sie ein reines Graustufenbild,
   erschiene die volle Flaeche als gruenes Rechteck.

   Seit dem 8. August liegt sie absolut gelegt neben dem Text und nicht mehr
   als Flex-Element in derselben Zeile. Dort bekam sie nur, was der Textblock
   uebrig liess: gemessen 651 x 393 px bei 1920 px Fenster, obwohl 46vw
   erlaubt waren. Die Kamera ist dadurch von 374 x 362 px auf ein Vielfaches
   gewachsen.

   Sie steht mittig in der freien Flaeche rechts vom Text, in Hoehe und
   Breite. Das ist die dritte Fassung des Tages: erst angeschnitten oben
   rechts wie im Entwurf, dann vollstaendig sichtbar, jetzt mittig. Vom Text
   haelt sie sich damit frei; er liegt ohnehin darueber (z-index) und bliebe
   auch bei Ueberdeckung lesbar.

   Der Kontrast darueber ist gemessen, und zwar fuer beide Flaechen: Der
   Baustein steht auf der Startseite auf --salbei, auf "Ueber mich" dagegen
   auf --weiss.

     Zeichnung ueber --salbei (#d2eec3)  ->  #afcc9f
       Fliesstext  --text-auf-salbei #2c1b1d    9,33:1
       Augenbraue  --akzentrot       #921920    5,02:1   <- die enge Stelle
     Zeichnung ueber --weiss (#ffffff)   ->  #c9d6c2
       Fliesstext  --text-auf-weiss  #1d1d1b   11,16:1
       Augenbraue  --akzentrot       #921920    5,83:1

   Alles ueber der Grenze von 4,5:1. Die enge Stelle ist die Augenbraue auf
   Salbei; wer die Deckkraft erhoeht, rechnet sie nach: Bei 0,55 sind es genau
   4,50:1, darueber faellt sie durch. Die Zeichnung besteht ausserdem aus
   Linien, der Text kreuzt also duenne Striche und keine volle Flaeche - die
   Rechnung ist der schlechteste Fall.

   Erst ab 100rem eingeblendet. Der Grund ist seit dem Umbau ein anderer:
   nicht mehr, dass sie darunter auf Briefmarkengroesse schrumpfte, sondern
   dass die Textspalte darunter zu schmal wird - die Zeichnung wuerde den Text
   dann nicht anschneiden, sondern ganz hinter sich nehmen. */
@media (min-width: 100rem) {
  .person__text {
    position: relative;
    overflow: hidden;
  }

  /* Der Text gehoert ueber die Zeichnung. Ohne eigenen Platz im Stapel stuende
     er mit ihr im selben und verschwaende darunter. */
  .person__text > * {
    position: relative;
    z-index: 1;
  }

  .person__text::after {
    content: "";
    position: absolute;

    /* Bezugsraum ist die freie Flaeche rechts vom Text, allseits um den
       Innenabstand der Flaeche eingerueckt - denselben, den auch der Text
       einhaelt. Die Einrueckung ist nicht schmueckend: Ohne sie reichte der
       Bezugsraum bis an beide Kanten, und die Kamera stiess bei 1728 x 1117
       (MacBook Pro 16 Zoll, Standardauflösung) mit 0 px sowohl an den Text
       als auch an den rechten Rand. */
    --kamera-rand: min(var(--flaeche-innen), 6svh);
    top: var(--kamera-rand);
    right: var(--kamera-rand);
    bottom: var(--kamera-rand);
    left: calc(2 * var(--kamera-rand) + 46ch);

    /* Die Maske ist seit dem 8. August abends auf die Kamera beschnitten, das
       Element also genau so gross wie die Zeichnung. Vorher trug sie links
       30 % Leerraum; jede Groessen- und Lageangabe hier meinte deshalb den
       Kasten und nicht die Kamera, und "mittig" waere um 8,5 % daneben
       gewesen. Wer die Vorlage tauscht, ruft das Werkzeug auf und traegt das
       gemeldete Verhaeltnis unten ein.

       Die Breite fuellt den eingerueckten Raum: 100 % der Flaeche minus dem
       Textblock und minus dreimal dem Rand (links vom Text, zwischen Text und
       Kamera, rechts von der Kamera). Zwei Deckel begrenzen sie: 700px, damit
       sie auf sehr breiten Schirmen nicht ins Riesenhafte waechst, und 62vh
       fuer flache Fenster, wo sonst die Hoehe nicht mehr in die Flaeche passt
       (die Kamera ist fast quadratisch, Hoehe = Breite * 1353/1400). */
    width: min(calc(100% - 3 * var(--kamera-rand) - 46ch), 700px, 62vh);
    aspect-ratio: 1400 / 1353;

    /* Mittig in beiden Achsen, wie vom Auftraggeber gewuenscht: Sind top,
       bottom, left und right gesetzt und Breite und Hoehe bestimmt, teilen
       auto-Raender den uebrigen Platz gleichmaessig auf. */
    margin: auto;

    background: var(--salbei-tief);
    opacity: 0.42;

    /* Nicht schmueckend: Im Text steht ein Verweis ("Mehr ueber meine
       Arbeitsweise"), und die Zeichnung liegt darueber. Ohne das schluckte
       sie den Klick. */
    pointer-events: none;

    /* Ausgeschrieben statt als Kurzform. Firefox wertet
       "mask: url(...) top right / contain no-repeat" nicht aus und zeigt
       dann die ungemaskierte Flaeche - also ein grosses gruenes Rechteck
       statt der Zeichnung. Mit den Einzelangaben stellen beide Familien
       dasselbe dar. */
    -webkit-mask-image: url("/logo/kamera-zeichnung.webp");
    mask-image: url("/logo/kamera-zeichnung.webp");
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
  }
}

/* ---------------------------------------------------------------- Galerie */

/* Ein Zeilensatz mit einheitlicher Zeilenhoehe.

   Seit die Projektseiten nicht mehr beschneiden, sind die Bilder verschieden
   hoch: ein Hochformat wird bei gleicher Breite gut doppelt so hoch wie ein
   Querformat. Ein Raster fester Spaltenbreite macht daraus Leerraum, denn eine
   Zeile ist so hoch wie ihr hoechstes Bild. Umsortieren hilft nicht - bei
   sieben hochkanten und vier querformatigen Bildern auf drei Spalten bleibt
   immer eine gemischte Zeile uebrig, egal in welcher Folge.

   Hier ist es umgekehrt: Nicht die Breite steht fest, sondern die Hoehe je
   Zeile. Die Breite jedes Bildes folgt seinem Format, die Zeile endet buendig
   am rechten Rand, und wie viele Bilder hineinpassen, ergibt sich - vier
   Hochformate, zwei Querformate, drei gemischte. Das ist die Anordnung, die
   der Auftraggeber von seiner Kundengalerie kennt; dort rechnet sie ein
   Skript, hier das Stilblatt.

   Wie es rechnet: Jede Kachel traegt ihr Format als --v (Breite durch Hoehe,
   gesetzt von bausteine.galerie). Sie waechst mit flex-grow: var(--v) und
   beginnt bei flex-basis: var(--v) * Zielhoehe. Weil beide Groessen
   proportional zu --v sind, ist die zugeteilte Breite es auch, und alle Bilder
   einer Zeile kommen auf dieselbe Hoehe. Die Zielhoehe entscheidet dabei nur,
   wo umgebrochen wird; die tatsaechliche Hoehe stellt die Zeile selbst ein. */
.galerie {
  display: flex;
  flex-wrap: wrap;
  gap: var(--abstand-4);
  /* Die Zielhoehe steht als Anteil der Galeriebreite, nicht in Pixeln: flex-
     basis rechnet Prozent auf die Breite des Flex-Containers. Ein fester Wert
     wirkt bei jeder Fensterbreite anders - mit 240 px standen bei 1146 px vier
     Hochformate nebeneinander, bei 718 px aber ein einzelnes Querformat ueber
     die volle Breite, 479 px hoch neben Zeilen von 251 px. Als Anteil bleibt
     die Zeilenbelegung ueber alle Breiten dieselbe. */
  --zielanteil: 55%;
  /* Ohne das streckt eine Zeile mit einem hohen und einem niedrigen Bild die
     niedrigere Kachel auf die Zeilenhoehe. Rechnerisch sind beide gleich hoch,
     Rundungen von einem Pixel gibt es trotzdem. */
  align-items: flex-start;
}

/* Die letzte Zeile ist meist nicht voll, und Flexbox wuerde ihre Bilder auf
   die ganze Breite ziehen - ein einzelnes Querformat waere dann 1146 px breit
   und dreimal so hoch wie alles darueber. Dieses Pseudoelement nimmt den
   Restplatz auf, sodass die Bilder der letzten Zeile bei der Zielhoehe
   bleiben. Ist die Zeile voll, rutscht es in eine eigene Zeile und ist mit
   Hoehe 0 unsichtbar. */
.galerie::after {
  content: "";
  flex-grow: 999999;
}

.galerie__kachel {
  flex-grow: var(--v, 1.5);
  flex-shrink: 1;
  flex-basis: calc(var(--v, 1.5) * var(--zielanteil));
  /* Sonst setzt der Inhalt eine Mindestbreite und ueberstimmt die Basis. */
  min-width: 0;
}

/* Ab Tablet die dichtere Stufe. Der Umschaltpunkt ist am Vorbild gemessen:
   Die Kundengalerie des Auftraggebers zeigt bei 785 px Inhaltsbreite vier
   Hochformate je Zeile (190 px breit) und bei 390 px zwei (187 px). Sie haelt
   die Bildbreite also ungefaehr fest und aendert dafuer die Anzahl. */
@media (min-width: 48rem) {
  .galerie { --zielanteil: 31%; }
}

@media (min-width: 56rem) {
  .galerie { gap: var(--abstand-5); }
}

/* Die Gesamtgalerie auf /referenzen/alle-fotos/ steht randlos ueber die volle
   Fensterbreite (bausteine.gesamtgalerie gibt sie ausserhalb der Huelle aus).
   Der Auftraggeber am 26. August: "die darstellung der bilder sollte ueber den
   komplett verfuegbaren bildschirmteil erfolgen, da es soviele bilder sind,
   dass ausnahmsweise das raster verlassen werden kann."

   Sie braucht dabei eine dritte Zielhoehen-Stufe, und das ist der Teil, der
   sich nicht von selbst versteht. Der Zielanteil rechnet in Prozent der
   Galeriebreite, also bleibt die Belegung ueber alle Fensterbreiten gleich:
   Bei 31 % stehen immer zwei Querformate je Zeile, ob die Galerie 1144 oder
   1713 px breit ist. In einer festen Spalte ist das richtig. Randlos heisst
   es, dass die gewonnene Breite in groessere Bilder geht statt in mehr davon -
   gemessen bei 1728 px: das Querformat waechst von 580 auf 845 px und die
   Seite von 63 238 auf 93 126 px, also 47 % mehr Scrollen bei gleicher
   Bildzahl. Das ist das Gegenteil dessen, wofuer die Breite geholt wurde.

   Mit 21 % ab 75 rem bleibt das Querformat bei 568 px, also so gross wie
   vorher in der Huelle, und die Seite faellt auf 39 448 px. Der Wert ist eine
   ganze Stufe im Sinne des Absatzes weiter unten: drei Querformate je Zeile
   statt zwei, oder sieben Hochformate statt vier.

   Aus demselben Grund braucht es eine vierte Stufe: Bei 2560 px waere das
   Querformat mit 21 % wieder 856 px breit und die Seite 57 428 px lang. Mit
   16 % sind es 622 px und 31 951 px, also vier Querformate je Zeile.

   Die Bildbreite schwankt damit zwischen 369 px (768 px Fenster) und 650 px
   (kurz vor 125 rem) und faellt an jeder Stufengrenze um ein Drittel. Das ist
   der Preis der Prozentkonstruktion und derselbe Sprung, den die Galerie
   zwischen 55 und 31 % schon immer macht; eine Zielhoehe in Pixeln waere
   glatter, bricht aber knapp unter einer Grenze auf die naechstkleinere
   Belegung um - bei 1728 px und 380 px Zielhoehe braeuchten drei Querformate
   1758 px und es blieben zwei zu je 852 px. */
.sektion--randlos > * + * { margin-top: var(--abstand-7); }

@media (min-width: 75rem) {
  .galerie--gesamt { --zielanteil: 21%; }
}

@media (min-width: 125rem) {
  .galerie--gesamt { --zielanteil: 16%; }
}

/* Warum 55 und 31 Prozent, und warum es dazwischen nichts gibt: Die Formate
   geben das Verhaeltnis vor. Ein Hochformat ist 2,25-mal schmaler als ein
   Querformat, also passen immer rund 2,25-mal so viele davon in eine Zeile.
   Waehlbar sind daher nur ganze Stufen, und zwei davon sind brauchbar:

     55 %  ein Querformat je Zeile, zwei Hochformate  (Handy)
     31 %  zwei Querformate je Zeile, vier Hochformate

   In beiden Stufen werden die Zeilen annaehernd gleich hoch, und darum geht
   es. Ein Wert dazwischen (etwa 42 %) laesst drei Hochformate zu, aber nur
   noch ein Querformat - dann steht eine Zeile von 549 px neben einer von
   764 px, und das sieht nach einem Fehler aus. Dieselben zwei Stufen benutzt
   das Vorbild, gemessen am 14. August. */

/* ---------------------------------------------------------------- Schritte */

.schritte { counter-reset: schritt; display: grid; gap: 0; }

@media (min-width: 48rem) { .schritte { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 72rem) { .schritte--vier { grid-template-columns: repeat(4, 1fr); } }

.schritt {
  counter-increment: schritt;
  padding: var(--abstand-6) var(--abstand-6) var(--abstand-7);
  border-top: 1px solid var(--linie);
}

@media (min-width: 48rem) {
  .schritt { border-left: 1px solid var(--linie); }
  .schritt:nth-child(2n + 1) { border-left: 0; }
}

@media (min-width: 72rem) {
  .schritte--vier .schritt:nth-child(2n + 1) { border-left: 1px solid var(--linie); }
  .schritte--vier .schritt:nth-child(4n + 1) { border-left: 0; }
}

.schritt::before {
  content: counter(schritt, decimal-leading-zero);
  display: block;
  margin-bottom: var(--abstand-4);
  font-family: var(--schrift-serif);
  font-size: 2.25rem;
  line-height: 1;
  color: var(--sand);
}

.schritt h3 { font-size: var(--gr-h4); font-weight: 600; margin-bottom: var(--abstand-3); }
.schritt p { font-size: var(--gr-text); color: var(--text-gedaempft); }

.flaeche--sand .schritt { border-color: rgba(58, 20, 24, 0.22); }
.flaeche--sand .schritt::before { color: var(--bordeaux); opacity: 0.55; }
.flaeche--sand .schritt p { color: var(--text-auf-sand); opacity: 0.86; }

/* ---------------------------------------------------------------- Merkmalliste */

.merkmale { list-style: none; margin: 0; padding: 0; }

.merkmale li {
  position: relative;
  margin-bottom: var(--abstand-4);
  padding-left: 1.9rem;
}

.merkmale li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.68em;
  width: 0.85rem;
  height: 1px;
  background: currentColor;
  opacity: 0.6;
}

/* ---------------------------------------------------------------- Häufige Fragen */

.fragen { border-top: 1px solid var(--linie); }

.frage { border-bottom: 1px solid var(--linie); }

.frage summary {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--abstand-5);
  padding: var(--abstand-5) 0;
  font-family: var(--schrift-serif);
  font-size: var(--gr-h4);
  line-height: 1.35;
  cursor: pointer;
  list-style: none;
}

/* Auf farbigen Flaechen verschwindet --linie. Der Fragen-Baustein stand bis
   zum 20. August 2026 nur auf Weiss und Ivory; auf /ratgeber/ steht er seither
   auf Salbei, und dort erreicht --linie (#ddd4c0) nur 1,18:1 statt der
   gewohnten 1,47:1. Der Ersatz ist gerechnet: --text-auf-salbei bei 20 %
   Deckung ueber --salbei ergibt exakt dieselben 1,47:1. */
.flaeche--salbei .fragen,
.flaeche--salbei .frage { border-color: #b1c5a2; }

.frage summary::-webkit-details-marker { display: none; }

.frage summary::after {
  content: "";
  flex-shrink: 0;
  width: 14px; height: 14px;
  margin-top: 0.45em;
  background: currentColor;
  -webkit-mask: var(--symbol-plus) center / contain no-repeat;
  mask: var(--symbol-plus) center / contain no-repeat;
  color: var(--akzentrot);
  transition: transform var(--uebergang);
}

.frage[open] summary::after { transform: rotate(45deg); }

.frage__antwort { padding-bottom: var(--abstand-6); max-width: var(--lesebreite); }
.frage__antwort p { color: var(--text-gedaempft); }
.frage__antwort a { color: var(--akzentrot); }

:root {
  --symbol-plus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M6.25 0h1.5v14h-1.5z'/%3E%3Cpath d='M0 6.25h14v1.5H0z'/%3E%3C/svg%3E");
}

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

.ortsliste {
  display: grid;
  gap: var(--abstand-3) var(--abstand-5);
  margin: 0;
  padding: 0;
  list-style: none;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
}

.ortsliste li { margin: 0; }

.ortsliste a, .ortsliste span {
  display: block;
  padding: var(--abstand-3) 0;
  border-bottom: 1px solid currentColor;
  font-family: var(--schrift-versal);
  font-size: var(--gr-text);
  letter-spacing: 0.04em;
  text-decoration: none;
  transition: color var(--uebergang);
}

.ortsliste a:hover { color: var(--akzentrot); }

/* Ein Gebiet ohne eigene Seite steht gedaempft - es wird bedient, ist aber
   nicht anklickbar. Die Daempfung stand bis zum 6. August 2026 auf 0,62 und
   war damit zu stark: gemessen 3,29:1 auf Sand, wo WCAG AA fuer Text dieser
   Groesse 4,5:1 verlangt. Auf Weiss lag sie mit 4,76:1 knapp darueber - die
   Sandflaeche traegt die Kurzliste auf drei Seiten, und dort fiel sie durch.
   Ein Verstoss, den die Pruefung am 3. August uebersehen hatte: Der Farbwert
   selbst ist in Ordnung, erst die Deckkraft nimmt ihn zurueck.
   Gerechnet liegt die Grenze bei 0,77 auf Sand und 0,605 auf Weiss. 0,82
   laesst auf beiden Luft. */
.ortsliste span { opacity: 0.82; }        /* Sand 5,02:1 · Weiss 9,50:1 */

/* Kurzfassung: die Schwerpunktgebiete auf Start-, Event- und Businessseite.
   Das Raster oben ist fuer neunzehn Eintraege gebaut und verteilt sechs davon
   ueber die ganze Breite - gemessen bei 1280 px lagen zwischen „Ammerland"
   und „Wilhelmshaven" 250 px Luft, und die Unterstriche zogen eine Zeile
   quer durch den Abschnitt. Hier stehen die Namen deshalb nebeneinander im
   Fluss, durch einen Punkt getrennt, und brechen um, wenn es eng wird. */
.ortsliste--kern {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--abstand-2) var(--abstand-4);
}

.ortsliste--kern a, .ortsliste--kern span {
  display: inline;
  padding: 0;
  border-bottom: none;
}

/* Der Trenner gehoert zur Gestaltung und nicht zum Text: Ein Screenreader
   liest die Eintraege als Liste, ein vorgelesenes „Punkt" waere Laerm.
   Er haengt hinten am Namen und nicht vorn am naechsten - sonst beginnt beim
   Umbruch eine Zeile mit dem Punkt. Gemessen auf dem Telefon bei 390 px:
   zwei von drei Zeilen fingen so an. */
.ortsliste--kern li:not(:last-child)::after {
  content: "·";
  margin-inline-start: var(--abstand-3);
  opacity: 0.45;
}

/* Ein Verweis muss auch ohne Unterstrich als solcher erkennbar sein - die
   Farbe allein reicht nach WCAG 1.4.1 nicht. */
.ortsliste--kern a {
  text-decoration: underline;
  text-underline-offset: 0.3em;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
}

.ortsliste--kern a:hover {
  text-decoration-color: currentColor;
}

/* ===========================================================================
   Bewegung beim Scrollen

   Alle Regeln haengen an der Klasse .bewegung, die das Skript am html-Element
   setzt. Ohne JavaScript und bei aktivierter Einstellung "Bewegung reduzieren"
   wird die Klasse nie gesetzt - dann steht die Seite vollstaendig still und
   sieht trotzdem richtig aus. Deshalb darf hier nichts stehen, was das Layout
   veraendert; bewegt wird ausschliesslich per transform.
   =========================================================================== */

/* Bilder, die sich langsamer bewegen als die Seite. Der Bildinhalt wird nach
   oben und unten ueber den sichtbaren Rahmen hinaus vergroessert, damit beim
   Verschieben keine Blitzer an den Kanten entstehen. */
.bewegung [data-parallax="bild"] { overflow: hidden; }

.bewegung [data-parallax="bild"] > img {
  height: calc(100% + 2 * var(--parallax-weg, 44px));
  margin-top: calc(-1 * var(--parallax-weg, 44px));
  will-change: transform;
}

/* Der Platzhalter hat kein img-Element. Statt der Flaeche bewegt sich hier ihr
   Inhalt - wuerde sich der Rahmen selbst verschieben, entstuenden Luecken zum
   Nachbarelement. Sobald ein echtes Foto vorliegt, greift die Regel darueber. */
.bewegung [data-parallax="bild"].bild--platzhalter > span,
.bewegung [data-parallax="bild"].bild--platzhalter::before {
  transform: translate3d(0, var(--parallax-versatz, 0px), 0);
  will-change: transform;
}

/* Die organisch angeschnittene Sandflaeche driftet etwas staerker als der
   Text daneben. Das erzeugt die Tiefe, ohne dass Inhalt verdeckt wird. */
.bewegung [data-parallax="form"] { will-change: transform; }

/* Sanftes Einblenden von Inhaltsbloecken beim Erreichen des Sichtbereichs.
   Ohne .bewegung ist alles von Anfang an sichtbar. */
.bewegung .einblenden {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
  transition: opacity 700ms cubic-bezier(0.22, 0.61, 0.36, 1),
              transform 700ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.bewegung .einblenden.sichtbar { opacity: 1; transform: none; }

/* Gestaffeltes Erscheinen innerhalb einer Gruppe – wirkt ruhiger als ein
   gleichzeitiges Aufblitzen aller Karten. */
.bewegung .einblenden[data-verzug="1"] { transition-delay: 90ms; }
.bewegung .einblenden[data-verzug="2"] { transition-delay: 180ms; }
.bewegung .einblenden[data-verzug="3"] { transition-delay: 270ms; }

/* Beim Drucken zaehlt nur der Inhalt: keine Bewegung, nichts Ausgeblendetes. */
@media print {
  .bewegung .einblenden {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .bewegung [data-parallax] > img,
  .bewegung [data-parallax] > span { transform: none !important; }
}

/* ---------------------------------------------------------------- Referenzen */

.referenzliste > * + * {
  margin-top: clamp(3rem, 2rem + 5vw, 6rem);
  padding-top: clamp(3rem, 2rem + 5vw, 6rem);
  border-top: 1px solid var(--linie);
}

.referenz { display: grid; gap: var(--abstand-7); scroll-margin-top: 120px; }

@media (min-width: 56rem) {
  .referenz { grid-template-columns: 0.9fr 1.1fr; gap: clamp(2.5rem, 1.5rem + 4vw, 4.5rem); align-items: start; }
  .referenz--bild-rechts .referenz__bild { order: 2; }
}

.referenz__bild { position: sticky; top: 120px; }

/* Ein unbeschnittenes Hochformat wird bei halber Fensterbreite sehr hoch: bei
   1440 px Fenster und 2:3 sind das rund 1080 px, mehr als ein Bildschirm. Die
   Obergrenze bindet die Hoehe an das Fenster statt an die Spaltenbreite; die
   Breite folgt dann dem Bild und nicht umgekehrt. Deshalb width: auto und
   margin-inline: auto - sonst bliebe die volle Spaltenbreite stehen und das
   Bild klebte am linken Rand.

   Kein Widerspruch zur Regel gegen aus der Hoehe gerechnete Breiten: Dort ging
   es um aspect-ratio: auto in einem gestreckten Raster, wo Hoehe und Breite
   sich gegenseitig hochschaukeln. Hier steht die Hoehe fest. */
/* Die Breite wird aus Hoehengrenze und Seitenverhaeltnis gerechnet, nicht auf
   auto gestellt. Grund: Das figure darunter schrumpft mit fit-content auf sein
   Kind - und ein Kind mit width: auto fragt umgekehrt das figure nach seiner
   Breite. Beide melden dann 0. Mit einer gerechneten Breite steht sie fest,
   und das figure hat etwas, worauf es schrumpfen kann.

   min(100%, ...) haelt das Bild in der Spalte: Ein Querformat fuellt sie wie
   bisher, ein Hochformat wird nur so breit, wie es bei der erlaubten Hoehe
   sein darf. Gemessen bei 1440 px Fenster: quer 599 px breit, hoch 507. */
.referenz__bild .bild--original {
  --hoehengrenze: min(80svh, 760px);
  width: calc(var(--hoehengrenze) * var(--seitenverhaeltnis, 3/2));
  max-width: 100%;
  max-height: var(--hoehengrenze);
  margin-inline: auto;
}

/* Die Bildunterschrift liegt im Bild und wird am figure ausgerichtet, das
   sonst die volle Spaltenbreite behaelt. Sobald das Bild schmaler ist als
   seine Spalte - bei einem Hochformat oder sobald die Hoehengrenze greift -
   stuenden Schriftzeile und Verlauf ueber das Bild hinaus, auf weissem Grund.
   Mit fit-content schrumpft das figure auf das Bild, und beide enden dort,
   wo auch das Foto endet. */
.referenz__bild figure:has(.bild--original) {
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
}

@media (max-width: 55.99rem) {
  .referenz__bild { position: static; }
}

/* Grosse Fassung fuer die Projektseiten. Das Bild steht wie sonst neben dem
   Text, reicht aber bis an den Fensterrand statt nur bis an die Huelle:
   gemessen 720 statt 500 px bei 1440 px Fenster. Es behaelt dabei sein
   Seitenverhaeltnis - "cover" ueber eine halbe Fensterbreite zoege jedes Foto
   in ein fremdes Format, und die Pflege waere aufwendiger, weil jedes Motiv
   einen eigenen Schwerpunkt braeuchte. */
/* Gleicher Abstand oben wie unten, aber knapper als --sektion-y: Das Bild
   reicht ohnehin bis an den Fensterrand, die Seite braucht daneben keine
   grosse Luft. Der Wert ist der von .sektion--knapp, damit hier keine dritte
   Abstandsgroesse entsteht - gemessen 80 statt 120 px bei 1440 px Fenster. */
.referenzliste--gross { padding-block: clamp(2.75rem, 1.8rem + 4vw, 5rem); }
.referenzliste--gross > * + * { margin-top: var(--sektion-y); }

.referenz--gross {
  display: grid;
  scroll-margin-top: 120px;
  /* Einmal benannt, weil die Bildhoehe daraus gerechnet wird. */
  --referenz-spalte: clamp(2rem, 1rem + 4vw, 4.5rem);
}

.referenz--gross > .referenz__text { padding-inline: var(--rand); }

/* Die Bildunterschrift steht im Bild, nicht darunter. Der Verlauf ist kein
   gestalterisches Element: Er sichert den Kontrast der weissen Schrift
   unabhaengig davon, wie hell das Foto an dieser Stelle ist - und die Fotos
   werden ausgetauscht. */
.referenz--gross .referenz__bild figure { position: relative; }

.referenz--gross .referenz__bild .bildunterschrift {
  position: absolute;
  inset: auto 0 0 0;
  margin: 0;
  padding: var(--abstand-6) var(--abstand-5) var(--abstand-4);
  color: var(--weiss);
  /* Bis 62 Prozent der Kastenhoehe bleibt die Deckung bei 0,70 - die Schrift
     steht darunter. Gemessen ueber einem reinweissen Foto, also im
     unguenstigsten Fall: 7,0:1. Ein linear auslaufender Verlauf haette an der
     Schriftkante nur 3,74:1 erreicht. */
  background: linear-gradient(to top,
              rgba(10, 9, 9, 0.74) 0%,
              rgba(10, 9, 9, 0.70) 62%,
              rgba(10, 9, 9, 0) 100%);
  pointer-events: none;
}

/* Umbruch bei 64rem wie beim Duo: Dazwischen wird die Textspalte so schmal,
   dass sie mehr Zeilen braucht als das Bild hoch ist. */
@media (min-width: 64rem) {
  .referenz--gross {
    grid-template-columns: 1fr 1fr;
    align-items: start;
    column-gap: var(--referenz-spalte);
  }

  /* Angeheftet, aber mittig im Fenster statt oben. Damit beginnt das Bild
     oben mit dem Text, steht beim Scrollen in der Fenstermitte und endet
     unten wieder mit ihm.

     Der Versatz muss die halbe Bildhoehe abziehen, und die ist berechenbar:
     Die Spalte ist die halbe Fensterbreite abzueglich des halben
     Spaltenabstands, das Bild darin 3:2. max() faengt den Fall ab, dass das
     Bild hoeher ist als das Fenster - dann klebte es sonst unter der
     Kopfzeile. */
  .referenz--gross > .referenz__bild {
    --bildhoehe: calc((100vw - var(--referenz-spalte)) / 3);
    position: sticky;
    top: max(7rem, calc((100vh - var(--bildhoehe)) / 2));
  }
  .referenz--gross > .referenz__text {
    padding-left: 0;
    padding-right: max(var(--rand), (100vw - var(--inhaltsbreite)) / 2);
  }
  .referenz--gross.referenz--bild-rechts > .referenz__bild { order: 2; }
  .referenz--gross.referenz--bild-rechts > .referenz__text {
    padding-right: 0;
    padding-left: max(var(--rand), (100vw - var(--inhaltsbreite)) / 2);
  }
}

.referenz h2 { margin-top: var(--abstand-2); }

.referenz__fakten {
  display: grid;
  gap: var(--abstand-4) var(--abstand-6);
  margin: var(--abstand-6) 0;
  padding: var(--abstand-5) 0;
  border-block: 1px solid var(--linie);
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
}

/* --------------------------------------------- Lange deutsche Komposita

   „Delegiertenversammlungen“ hat 24 Zeichen ohne Trennstelle,
   „Oberbürgermeisterkandidatur“ 27. In einer schmalen Rasterspalte passt so
   ein Wort in keine Zeile und ragt in die Nachbarspalte. Gemessen am
   7. August 2026 an zwei Stellen: in der Faktenzeile 219 px in einer
   160-px-Zelle, auf einer Projektkachel 301 px in 222 px.

   Drei Angaben zusammen lösen das, und alle drei werden gebraucht:

     min-width: 0    lässt die Rasterspalte überhaupt erst schmaler werden als
                     ihr längstes Wort – die Projektregel für Rasterkinder
     hyphens: auto   trennt deutsche Komposita an der richtigen Stelle; das
                     Dokument trägt lang="de", sonst wüsste der Browser die
                     Sprache nicht
     overflow-wrap   fängt ab, was auch getrennt nicht passt

   Bewusst nur an schmalen Stellen und nicht auf dem ganzen Dokument: In der
   großen Serifen-Display-Schrift der Überschriften sieht eine Trennung
   schlecht aus, und dort ist auch Platz. Kommt eine weitere schmale Spalte
   hinzu, gehört sie in diese Liste. */
.referenz__faktum,
.karte { min-width: 0; }

/* Getrennt wird überall dort, wo Fließtext in einer schmalen Spalte steht.
   Ohne Trennung bricht „Informationssicherheit“ mitten im Wort und ohne
   Bindestrich um – das ist schlechter lesbar als eine saubere Trennung.

   Wie stark getrennt wird, hängt von der Spaltenbreite ab – deshalb steht die
   Schwelle zweimal da. In Firefox gemessen, wie breit die Kachel **innen**
   ist und wie breit die längsten vorkommenden Wörter sind:

     Fenster   Kachel innen   „Regierungszentrum“ (17 Zeichen)
     1920      283 px         212 px   passt
     1280      290 px         212 px   passt
     1024      225 px         207 px   passt
      820      163 px         194 px   passt nicht
      375      261 px         167 px   passt

   Der Ausreißer ist die Bandbreite zwischen 48 und 64 rem: Dort schaltet
   .raster--3 bereits auf drei Spalten, und der Kachel bleiben innen 163 px.
   Nur dort muss auch ein mittellanges Wort getrennt werden.

   Überall sonst gilt die Regel, um die es geht: Ein Wort, das auf die nächste
   Zeile passt, rückt dorthin – getrennt wird nur, was sonst überliefe. Die
   Schwelle 23 ist gemessen: Alles bis 22 Zeichen passt, erst
   „Bürgermeisterkandidatur“ (23) und „Oberbürgermeisterkandidatur“ (27)
   passen nicht mehr.

   Die Metazeile bleibt ganz ausgenommen: Sie ist kurz, steht in gesperrten
   Versalien und lief nie über. Getrennt sah sie aus wie ein Fehler
   („EINZELPORTRÄT · OL-DENBURG“).

   Wer die Spaltenbreite oder die Schrift ändert, misst nach – und zwar in
   Firefox an der Innenbreite der Kachel, nicht an ihrer Außenbreite. Genau
   dieser Unterschied hat hier zuerst zu einer falschen Schwelle geführt. */
.referenz__faktum dd,
.karte h3,
.karte__text {
  hyphens: auto;
  hyphenate-limit-chars: 23 4 4;
}

/* Die eine Bandbreite, in der die Kachel auf 163 px innen zusammenfällt.
   Ohne großzügigere Trennung bräche dort „Regierungszentrum“ ohne
   Bindestrich mitten im Wort um. */
@media (min-width: 48rem) and (max-width: 63.999rem) {
  .karte h3,
  .karte__text { hyphenate-limit-chars: 8 4 4; }
}

.referenz__faktum dd,
.karte h3,
.karte__text,
.karte__meta { overflow-wrap: break-word; }

.referenz__faktum dt {
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--akzentrot);
  margin-bottom: var(--abstand-1);
}

.referenz__faktum dd { margin: 0; font-size: var(--gr-text); }

.referenz__bloecke { display: grid; gap: var(--abstand-6); }

.referenz__block h3 {
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  font-weight: 500;
  letter-spacing: var(--spatium-versal);
  text-transform: uppercase;
  margin-bottom: var(--abstand-3);
}

.referenz__block p { color: var(--text-gedaempft); }

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

.formular { display: grid; gap: var(--abstand-5); }

@media (min-width: 40rem) {
  .formular { grid-template-columns: repeat(2, 1fr); }
  .feld--breit { grid-column: 1 / -1; }
}

.feld { display: flex; flex-direction: column; gap: var(--abstand-2); }

.feld label {
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-gedaempft);
}

.feld .pflicht { color: var(--akzentrot); }

.feld input, .feld select, .feld textarea {
  width: 100%;
  padding: 0.85rem 1rem;
  background: var(--weiss);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  font-family: var(--schrift-text);
  font-size: var(--gr-text);
  color: var(--tinte);
  transition: border-color var(--uebergang);
}

.feld textarea { min-height: 9rem; resize: vertical; line-height: 1.6; }

.feld input:focus, .feld select:focus, .feld textarea:focus {
  border-color: var(--akzentrot);
  outline-offset: 1px;
}

.feld input[aria-invalid="true"],
.feld textarea[aria-invalid="true"] { border-color: var(--akzentrot); border-width: 2px; }

.feld__fehler {
  font-size: var(--gr-klein);
  color: var(--akzentrot);
  min-height: 1.2em;
}

.feld__hinweis { font-size: var(--gr-klein); color: var(--text-gedaempft); }

/* Honigtopf: fuer Menschen unsichtbar, fuer einfache Formular-Roboter attraktiv.
   Ersetzt ein Captcha und vermeidet damit eine Datenuebermittlung an Dritte. */
.honigtopf {
  position: absolute !important;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.zustimmung {
  display: flex;
  align-items: flex-start;
  gap: var(--abstand-3);
  grid-column: 1 / -1;
}

.zustimmung input {
  width: 1.15rem; height: 1.15rem;
  margin-top: 0.28rem;
  flex-shrink: 0;
  accent-color: var(--akzentrot);
}

.zustimmung label {
  font-family: var(--schrift-text);
  font-size: var(--gr-klein);
  line-height: 1.6;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-gedaempft);
}

.zustimmung a { color: var(--akzentrot); }

.formular__abschluss { grid-column: 1 / -1; display: flex; flex-direction: column; gap: var(--abstand-4); }

/* ---------------------------------------------------------------- Hinweiskasten */

.hinweis {
  padding: var(--abstand-5) var(--abstand-6);
  background: var(--sand-blass);
  border-left: 3px solid var(--sand);
  font-size: var(--gr-text);
}

.hinweis--salbei { background: var(--salbei); border-left-color: var(--salbei-tief); }
.hinweis p { margin-bottom: var(--abstand-3); }

/* ---------------------------------------------------------------- Fusszeile */

/* Der Fussbereich fuehrt zwei Schriftgrade, nicht drei. Bis zum 25. August
   standen die Linklisten und die Anschrift auf --gr-text (17 px), die
   Beschreibung daneben auf --gr-klein (13 px) - im selben Block, nebeneinander,
   ein Sprung von vier Punkt. Jetzt tragen alle laufenden Texte des Footers
   --fuss-text, und --gr-klein bleibt dem Kleingedruckten unten vorbehalten.
   Der Wert steht hier und nicht in tokens.css: Er gilt nur fuer diesen
   Baustein, und die Tokens stammen aus dem Design-Handoff. */
.fusszeile {
  --fuss-text: 0.9375rem;
  background: var(--sand);
  color: var(--text-auf-sand);
}

.fusszeile__haupt {
  display: grid;
  gap: var(--abstand-8);
  padding-block: clamp(3rem, 2rem + 4vw, 4.5rem);
}

/* Zwei Stufen, weil vier Bloecke daruntergehoeren: Marke, Leistungen,
   Unternehmen, Kontakt. Bis zum 25. August gab es nur drei Spalten fuer diese
   vier - der Kontaktblock fiel in eine zweite Zeile unter die Marke, und
   rechts daneben standen 686 x 216 px leere Sandflaeche. Gemessen bei 1400 px
   Fensterbreite. */
@media (min-width: 48rem) {
  .fusszeile__haupt { grid-template-columns: 1fr 1fr; gap: var(--abstand-7); }
}

/* ZWEI SPALTEN, WEIL ES ZWEI BLOECKE SIND. Auf der fertigen Website stehen
   hier vier (Marke, Leistungen, Unternehmen, Kontakt); diese Seite hat nur
   Marke und Kontakt. Mit dem Vierer-Raster standen sie beide links, und
   rechts blieben bei 1400 px gemessene 629 px leere Sandflaeche - derselbe
   Fehler wie am 25. August im Hauptprojekt, nur andersherum.

   Der Auftraggeber hat am 9. September aus drei Fassungen diese gewaehlt:
   Marke links, Kontakt rechts aussen. Die beiden Bloecke stehen damit an den
   Raendern der Huelle, und die Flaeche dazwischen ist Absicht statt Rest. */
@media (min-width: 64rem) {
  .fusszeile__haupt {
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1.05fr);
  }
}

@media (min-width: 48rem) {
  .fusszeile__haupt { justify-content: space-between; }
  .fusszeile__kontakt { justify-self: end; text-align: right; }
}

.fusszeile__marke img { width: 190px; margin-bottom: var(--abstand-5); }
.fusszeile__beschreibung {
  font-size: var(--fuss-text);
  line-height: 1.7;
  max-width: 34ch;
}

.fusszeile h2 {
  margin-bottom: var(--abstand-4);
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  font-weight: 500;
  letter-spacing: var(--spatium-weit);
  text-transform: uppercase;
}

.fusszeile ul { margin: 0; padding: 0; list-style: none; }
.fusszeile li { margin-bottom: 0; }

.fusszeile a { text-decoration: none; transition: opacity var(--uebergang); }
.fusszeile a:hover { text-decoration: underline; }

/* Die Linklisten im Fussbereich brauchen eine ausreichende Trefferflaeche.
   Die Innenabstaende ersetzen den frueheren Abstand zwischen den Punkten.
   Gerechnet: 15 px mal 1,75 Zeilenhoehe sind 26,25 px, dazu zweimal 0,6 rem
   Innenabstand ergibt 45,45 px. Wer den Schriftgrad oder die Zeilenhoehe
   anfasst, rechnet die 44 px neu nach - sie sind knapp. */
.fusszeile li > a {
  display: inline-block;
  font-size: var(--fuss-text);
  line-height: 1.75;
  padding-block: 0.6rem;
}

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

/* Telefon- und E-Mail-Link sind auf Mobilgeraeten die meistgenutzten Ziele
   im Fussbereich und brauchen deshalb eine grosszuegige Trefferflaeche.
   15 px mal 1,85 sind 27,75 px, dazu zweimal 0,55 rem ergibt 45,35 px. */
.fusszeile__kontakt a { display: inline-block; padding-block: 0.55rem; }

/* Die Trefferflaeche ist 44 px breit, das Zeichen darin 22 px - links bleiben
   also 11 px Luft, und die Reihe stuende gegenueber dem Text darueber um genau
   diese 11 px eingerueckt. Der negative Aussenabstand holt die optische Kante
   zurueck auf die Fluchtlinie der Spalte. */
.fusszeile__sozial {
  display: flex;
  gap: var(--abstand-2);
  margin-top: var(--abstand-5);
  margin-inline-start: -11px;
}

/* 44 px Trefferflaeche, damit die Symbole auf Mobilgeraeten sicher zu treffen sind. */
.fusszeile__sozial a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
}

/* Ein Unterstrich unter einer Zeichnung ist nicht zu sehen; die Regel weiter
   oben laeuft hier also ins Leere. Die Ueberblendung auf .fusszeile a stand
   schon da, nur hat nie etwas die Deckkraft geaendert. */
.fusszeile__sozial a:hover,
.fusszeile__sozial a:focus-visible { opacity: 0.62; }

.fusszeile__sozial svg { width: 22px; height: 22px; }

.fusszeile__abschluss {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--abstand-4);
  padding-block: var(--abstand-3);
  border-top: 1px solid rgba(58, 20, 24, 0.22);
  font-size: var(--gr-klein);
}

.fusszeile__rechtliches { display: flex; flex-wrap: wrap; gap: var(--abstand-5); }

/* Die drei Rechtslinks trugen bis zum 25. August 22 px Hoehe - die Haelfte der
   44 px, die dieses Projekt fuer sich festgelegt hat, und das auf jeder Breite.
   Uebersehen wurden sie, weil die Pruefung nur die Linklisten und die
   Kontaktdaten angesehen hat. 13 px mal 1,72 Zeilenhoehe sind 22,36 px, dazu
   zweimal 0,7 rem ergibt 44,76 px. Der Innenabstand des Bandes faellt dafuer
   von 1,5 rem auf 0,75 rem, sonst waechst das Band um 22 px. */
.fusszeile__rechtliches a { display: inline-block; padding-block: 0.7rem; }

/* ---------------------------------------------------------------- Sonstiges */

.gebietsband {
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  letter-spacing: var(--spatium-versal);
  text-transform: uppercase;
}

.trennzeichen { opacity: 0.5; padding-inline: var(--abstand-3); }

.kennzahl {
  font-family: var(--schrift-serif);
  font-size: var(--gr-h2);
  line-height: 1;
  color: var(--bordeaux);
}

.tabelle { width: 100%; border-collapse: collapse; font-size: var(--gr-text); }
.tabelle th, .tabelle td { padding: var(--abstand-3) var(--abstand-4); text-align: left; border-bottom: 1px solid var(--linie); vertical-align: top; }
.tabelle th { font-family: var(--schrift-versal); font-size: var(--gr-label); letter-spacing: 0.08em; text-transform: uppercase; }

/* ------------------------------------------------------------ Großansicht */

/* Der Auslöser ist ein Verweis auf die Bilddatei. Ohne JavaScript öffnet der
   Klick das Bild selbst; erst das Skript legt die Großansicht darüber. Der
   Verweis muss deshalb im Raster genauso liegen wie die Bildfläche vorher –
   sonst verschöbe sich das Layout, sobald ein Bild anklickbar wird. */
.bild-gross {
  display: block;
  min-width: 0;
  text-decoration: none;
  cursor: zoom-in;
}

/* Kein Rahmen, kein Schatten, keine Rundung – die Rückmeldung ist die
   Vergrößerung des Bildinhalts selbst, sonst nichts. */
.bild-gross .bild img { transition: transform var(--uebergang); }
.bild-gross:hover .bild img,
.bild-gross:focus-visible .bild img { transform: scale(1.02); }

/* „Bewegung reduzieren“: Die Klasse .bewegung setzt das Skript nur, wenn die
   Einstellung nicht aktiv ist. Ohne sie bleibt das Bild ruhig stehen. */
html:not(.bewegung) .bild-gross .bild img { transition: none; }
html:not(.bewegung) .bild-gross:hover .bild img { transform: none; }

.grossansicht {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: none;
  /* Flächig, kein Verlauf. --tinte mit Restdurchsicht, damit erkennbar
     bleibt, dass die Seite darunter weitersteht. */
  background: var(--schleier);
  padding: var(--abstand-4);
}

.grossansicht[data-offen="true"] { display: grid; grid-template-rows: auto 1fr auto; }

.grossansicht__bild {
  position: relative;
  min-height: 0;
}

/* Das Bild wird nie beschnitten und behält sein Format – quer wie hoch.

   Der Weg dorthin ist „position: absolute“ mit „inset: 0“ und „margin: auto“,
   nicht einfach „max-height: 100 %“ in einem Raster. Gemessen am 7. August
   2026 bei 1280 × 450: Die Bildfläche war 306 px hoch, das Querformat wurde
   trotzdem 811 px hoch gerechnet und ragte 505 px heraus, das Hochformat
   sogar 764 px. Der Grund ist, dass sich eine Prozentangabe für die Höhe im
   Raster nicht zuverlässig auflöst. Bei einem absolut gesetzten Element
   bezieht sie sich auf einen Rahmen mit fester Höhe und greift immer.

   „margin: auto“ zentriert dabei in beiden Richtungen. Die Bildfläche bleibt
   genau so groß wie das Bild – dadurch fallen Klicks neben das Bild an den
   Untergrund durch und schließen die Ansicht. */
.grossansicht__bild img {
  position: absolute;
  inset: 0;
  margin: auto;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}

.grossansicht__kopf,
.grossansicht__fuss {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--abstand-4);
  color: var(--weiss);
}

.grossansicht__fuss { padding-top: var(--abstand-4); }

/* Weiß auf dem Schleier: gemessen 15,16:1, siehe tokens.css. */
.grossansicht__unterschrift {
  font-size: var(--gr-text);
  margin: 0;
  max-width: 60ch;
}

/* Der Projektverweis in der Grossansicht. Er erscheint nur auf der
   Gesamtuebersicht: Dort besteht die Seite aus nichts als Fotos, und ohne
   diesen Verweis waere nicht zu erkennen, aus welchem Projekt eines stammt.
   Auf einer Projektseite traegt kein Bild die Angabe, das Element bleibt
   dort verborgen. */
.grossansicht__projekt {
  display: inline-block;
  margin-top: var(--abstand-2);
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  letter-spacing: 0.08em;
  color: var(--weiss);
  text-decoration: underline;
  text-underline-offset: 0.3em;
  /* 44 px Trefferflaeche wie ueberall, ohne die Zeile auseinanderzuziehen:
     Der Innenabstand wirkt nach oben und unten, der negative Aussenabstand
     nimmt ihn fuer das Layout wieder zuruck. */
  padding-block: 0.75rem;
  margin-block: calc(var(--abstand-2) - 0.75rem) -0.75rem;
}

.grossansicht__projekt:hover { text-decoration-thickness: 2px; }
.grossansicht__projekt[hidden] { display: none; }

.grossansicht__zaehler {
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  letter-spacing: var(--spatium-versal);
  text-transform: uppercase;
  white-space: nowrap;
}

/* Trefferfläche 48 px – die Vorgabe verlangt mindestens 44 px. */
.grossansicht__knopf {
  flex: none;
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.45);
  border-radius: 2px;
  background: none;
  color: var(--weiss);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}

.grossansicht__knopf:hover { border-color: var(--weiss); }
.grossansicht__knopf[disabled] { opacity: 0.35; cursor: default; }

/* Der Fokusring steht auf dunklem Grund und muss deshalb weiß sein –
   --akzentrot erreicht auf --tinte nur 2,8:1. */
.grossansicht :focus-visible { outline-color: var(--weiss); }

/* Die Blätterknöpfe stehen seitlich auf halber Höhe, wo sie bei einer
   Bildansicht erwartet werden – nicht unten in der Ecke. */
.grossansicht__knopf--blaettern {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  /* Sie liegen über dem Bild, und was hinter ihnen liegt, ist ein beliebiges
     Foto. Ein durchsichtiger Knopf wäre auf hellem Bildinhalt nicht mehr zu
     erkennen. Die eigene Fläche macht den Kontrast unabhängig vom Motiv:
     Weiß darauf 15,16:1, der Rahmen 4,27:1. */
  background: var(--schleier);
}

.grossansicht__knopf--zurueck { left: var(--abstand-4); }
.grossansicht__knopf--weiter { right: var(--abstand-4); }

/* ------------------------------------------------- Galerie nach Terminen

   Fasst ein Projekt mehrere Veranstaltungen zusammen, tragen die Bilder eine
   „gruppe“. Wo sich der Wert ändert, beginnt ein eigener Block mit
   Überschrift. Bei dreißig Bildern aus drei Terminen ist das der Unterschied
   zwischen einer Wand aus Kacheln und etwas, das man überfliegen kann.

   Der Abstand zwischen den Blöcken ist größer als der zwischen den Bildern
   (4 rem gegen 1,5 rem) – erst dadurch liest sich die Überschrift als Anfang
   des Folgenden und nicht als Nachtrag zum Vorigen. */
.galeriegruppe + .galeriegruppe { margin-top: var(--abstand-9); }

/* Dieselbe Sprache wie die Augenbraue: Versalien, weit gesperrt, Akzentrot.
   Eine Haarlinie darunter trennt, ohne einen Kasten zu bauen. */
.galeriegruppe__titel {
  margin: 0 0 var(--abstand-5);
  padding-bottom: var(--abstand-3);
  border-bottom: 1px solid var(--linie);
  font-family: var(--schrift-versal);
  font-size: var(--gr-label);
  font-weight: 500;
  letter-spacing: var(--spatium-weit);
  text-transform: uppercase;
  color: var(--akzentrot);
}

.flaeche--blau .galeriegruppe__titel,
.flaeche--bordeaux .galeriegruppe__titel,
.flaeche--tinte .galeriegruppe__titel {
  color: rgba(255, 255, 255, 0.82);
  border-bottom-color: rgba(255, 255, 255, 0.28);
}

.flaeche--sand .galeriegruppe__titel { color: var(--bordeaux); }
