/* Erzeugt von scripts/fetch-fonts.mjs — nicht von Hand aendern.
   Poppins liegt in den Schnitten 400/500/600 lokal im Projekt.
   Lizenz: SIL Open Font License 1.1, siehe LIZENZ.txt */

/* Poppins 400 — Fliesstext (latin-ext) */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/poppins-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Poppins 400 — Fliesstext (latin) */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/poppins-400-latin.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;
}
/* Poppins 500 — Labels, Chips, Navigation (latin-ext) */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/poppins-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Poppins 500 — Labels, Chips, Navigation (latin) */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/poppins-500-latin.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;
}
/* Poppins 600 — Ueberschriften (latin-ext) */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/poppins-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Poppins 600 — Ueberschriften (latin) */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fonts/poppins-600-latin.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;
}

/* ===========================================================================
   Design-Tokens — die einzige Quelle für Farbe, Schrift, Maß und Kante.
   Nichts weiter unten im CSS darf einen Hex-Wert oder eine feste Pixelgröße
   erfinden. Wer eine Farbe braucht, nimmt eine von hier.

   Beschlossen in docs/redesign-konzept.md, Abschnitt 4.
   Richtung: industriell-modern. Anthrazit trägt, Stahlblau ist der einzige
   Akzent — logotreu, damit das Logo unangetastet bleiben kann.

   Woher die Werte kommen: Primary #1C244B, Body #324A6D und Accent #609FBC
   standen als Marken-Tokens im Elementor-Kit der alten Seite. Sie sind aus dem
   Logo abgeleitet und werden übernommen. Was NICHT übernommen wird, sind die
   sechs Farbwerte, die im Bestand zusätzlich in Gebrauch waren, ohne in der
   eigenen Palette zu stehen — allen voran reines Schwarz, mit 34 Vorkommen die
   häufigste Textfarbe der Seite.
   =========================================================================== */

:root {
  /* --- Tinte: Text und dunkle Flächen ----------------------------------- */
  --c-ink: #1c2022; /* Fließtext — 15,9:1 auf Weiß */
  --c-ink-muted: #4a5157; /* Sekundärtext — 8,1:1 auf Weiß */
  --c-steel-900: #21252b; /* Hero-Overlay, dunkle Bänder */
  --c-navy: #1c244b; /* Überschriften (aus dem Kit) — 13,9:1 */

  /* --- Stahlblau: der eine Akzent ---------------------------------------
     ⚠️ DREI WERTE, UND SIE SIND NICHT AUSTAUSCHBAR.

     --c-accent (#609FBC) erreicht gegen Weiß nur 2,9:1.

     ⚠️ ENTSCHEIDUNG TOM, 2026-08-05 — BITTE NICHT "REPARIEREN".
     Die Akzent-Schaltflächen tragen seitdem WEISSE Schrift auf #609FBC. Das
     sind besagte 2,9:1 und damit unter WCAG AA (4,5:1) und auch unter
     AA-Large (3:1). Es ist eine bewusste Designentscheidung nach Vorlage der
     Kontrastwerte, keine Nachlässigkeit:

         "Ich möchte immer, dass wenn du einen hellblauen Button verwendest,
          dass die Schrift die du dort verwendest da nicht dunkelblau ist
          sondern weiß."

     Der Alternativvorschlag — Fläche auf --c-accent-deep (5,6:1 mit Weiß) —
     lag vor und wurde abgelehnt.

     Der Preis, am 05.08.2026 mit Lighthouse mobil GEMESSEN und nicht
     geschätzt: Accessibility 100 → 96, eine Beanstandung (color-contrast,
     zwei Elemente, gemeldeter Wert 2,92). Best Practices und SEO bleiben bei
     100. Das Projektziel ≥95 wird damit weiterhin gehalten — die Sorge, es
     zu verfehlen, hat sich nicht bestätigt.

     Wer diese Regel zurückdreht, überschreibt eine Kundenentscheidung.
     Vorher fragen.

     Regel:  --c-accent      → Fläche (mit weißer Schrift), Linie, aktiver Zustand
             --c-accent-deep → Text, Links, Icons auf hellem Grund (5,6:1),
                               außerdem Hover-Fläche der Akzent-Schaltflächen
             --c-accent-hell → Links auf dunklem Grund (7,4:1 auf steel-900) */
  --c-accent: #609fbc;
  --c-accent-deep: #2f6e8c;
  --c-accent-hell: #9ac9de;

  /* --- Die vier Ringe unter "Warum wir?" ---------------------------------
     Abstufungen derselben Palette, dunkel nach hell. Es kommt keine neue
     Farbe hinzu — --c-ring-1 ist --c-navy, --ring-2 ist --c-accent-deep,
     --ring-3 ist --c-accent, --ring-4 ist --c-accent-hell.

     Sie stehen trotzdem als eigene Namen da, weil index.njk sie ueber den
     Schleifenindex anspricht (--c-ring-1 … --c-ring-4). Wer einen Wert
     aendert, aendert nur den Ring, nicht die Schaltflaechen. Die Ringe sind
     Zierde und tragen keinen Text — fuer sie gilt keine Kontrastgrenze. */
  --c-ring-1: #1c244b;
  --c-ring-2: #2f6e8c;
  --c-ring-3: #609fbc;
  --c-ring-4: #9ac9de;

  /* --- Neutrale Flächen -------------------------------------------------- */
  --c-paper: #f3f5f8; /* helle Sektionsfläche */
  --c-line: #d8dee4; /* Trennlinien */
  --c-line-dunkel: #3a4149; /* Trennlinien auf dunklem Grund */
  --c-white: #ffffff;

  /* --- Deckschichten über Fotos ------------------------------------------
     Der Hero legt Text über ein Foto. Ohne Deckschicht hängt der Kontrast
     davon ab, was zufällig hinter dem Buchstaben liegt — bei Gelaender-37
     wechselt das auf wenigen Zentimetern von anthrazitfarbenem Glas zu weißer
     Hauswand. Der Verlauf ist unten am dichtesten, weil dort der Text steht.

     ⚠️ Diese Werte sind keine Geschmacksfrage. Weiße Schrift auf --overlay-unten
     über einem hellen Foto kommt auf über 7:1; wer den Wert senkt, um "mehr
     vom Bild zu sehen", kippt genau die Accessibility-Messung, an der im
     Bestand alle drei geprüften Seiten scheitern. */
  --c-overlay-oben: rgb(28 32 34 / 0.3);
  --c-overlay-unten: rgb(28 32 34 / 0.86);
  --c-backdrop: rgb(28 32 34 / 0.94); /* hinter dem Lichtkasten */

  /* Fehlermeldung im Formular. Der einzige Rotton der Seite und bewusst
     gedeckt statt signalrot — 6,5:1 auf Weiß. Er sagt "hier stimmt etwas
     nicht", ohne die Palette zu sprengen. */
  --c-warn: #8c3a2f;

  /* --- Schrift -----------------------------------------------------------
     Poppins bleibt. Das Typo-Problem des Bestands war nie die Schrift,
     sondern 14 px in Gewicht 300 (mit 30 Vorkommen die häufigste Kombination
     auf dem Handy, an einer Stelle sogar 12 px) und 11 statt 5 Stile.

     Geladen werden nur 400, 500 und 600. Gewicht 300 fällt ersatzlos weg. */
  --f-sans:
    'Poppins', system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;

  /* --- Typo-Skala: 6 Stufen statt 11 -------------------------------------
     Im Bestand waren h2 und h3 beide 65 px und unterschieden sich nur in der
     Farbe — es gab visuell keine zweite Überschriftenebene. Die Skala hier
     stellt sie her. Fließtext startet bei 17 px, nicht 14. */
  --t-display: clamp(2.5rem, 7vw, 4.5rem); /* Hero-h1, Gewicht 600, Zeilenhöhe 1,05 */
  --t-h2: clamp(1.75rem, 4vw, 2.75rem); /* Sektionsüberschriften */
  --t-h3: clamp(1.25rem, 2.2vw, 1.5rem); /* Karten- und Abschnittstitel */
  --t-lead: clamp(1.125rem, 2vw, 1.375rem); /* Einleitungssätze */
  --t-body: clamp(1.0625rem, 0.9vw + 0.85rem, 1.125rem); /* Fließtext: 17 → 18 px */
  --t-meta: 0.9375rem; /* Labels, Chips, Fußzeile */

  --lh-display: 1.05;
  --lh-h2: 1.15;
  --lh-h3: 1.25;
  --lh-lead: 1.5;
  --lh-body: 1.6;

  --fw-normal: 400;
  --fw-mittel: 500;
  --fw-halbfett: 600;

  /* --- Abstand -----------------------------------------------------------
     ⚠️ DIE EINE REGEL, DIE DEN UNTERSCHIED MACHT:
        Abstand hängt an der Sektion, nie im Inhalt.

     Im Bestand sind sämtliche 11 Sektions-Paddings der Startseite 0px, und
     der gesamte vertikale Rhythmus entsteht aus leeren Spacer-Widgets:
     815 px auf dem Desktop, 1.251 px auf dem Handy. Auf dem Handy ist der
     Leerraum also GRÖSSER als auf dem Desktop — das Gegenteil des Sinnvollen.
     38 der 108 Widgets über alle fünf Seiten sind Spacer.

     Im Neubau: null. Wer hier ein leeres <div> zum Abstandhalten einfügt,
     baut den Fehler nach, wegen dem diese Seite neu gebaut wird. */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-6: 24px;
  --s-8: 32px;
  --s-12: 48px;
  --s-16: 64px;
  --s-24: 96px;
  --s-32: 128px;

  --pad-section: clamp(48px, 8vw, 96px); /* oben + unten je Sektion */
  --pad-rand: clamp(16px, 4vw, 32px); /* linker/rechter Seitenrand */

  /* --- Form --------------------------------------------------------------
     Ein Radius statt vier (im Bestand: 5px, 50%, 3px, 100px) und eine
     Inhaltsbreite statt vier (1368 / 1405 / 1425 / 1000 px). */
  --r: 4px;
  --r-full: 999px;
  --w-content: 1200px;
  --w-text: 68ch; /* Lesebreite für Fließtext und Rechtstexte */
  --tap-min: 44px; /* Mindest-Tippziel — im Bestand 9 Elemente darunter,
                      die Menü-Schaltfläche misst 36×30 px */

  --kopf-hoehe: 64px;

  /* Keine Schatten. Das ist im Bestand schon so und bleibt: klare Kanten
     passen zu "industriell". Tiefe entsteht über Flächenfarbe, nicht Weichheit. */
  --fokus-breite: 2px;
  --fokus-abstand: 2px;

  --uebergang: 160ms ease;
}

/* Wer eine Bewegung abbestellt hat, bekommt keine. Betrifft hier vor allem
   den Lichtkasten und das aufklappende Menü. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --uebergang: 1ms;
  }
}

/* ===========================================================================
   Basis — Reset, Grundtypografie, Grundlayout.
   Nur Element-Selektoren. Alles mit Klasse steht in 30-komponenten.css.
   =========================================================================== */

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

* {
  margin: 0;
}

html {
  /* Kein `scroll-behavior: smooth` global: Es macht Sprungmarken auf langen
     Rechtsseiten zu einer Reise und ist bei Tastaturbedienung lästig. */
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: calc(var(--kopf-hoehe) + var(--s-4));
}

body {
  font-family: var(--f-sans);
  font-size: var(--t-body);
  font-weight: var(--fw-normal);
  line-height: var(--lh-body);
  color: var(--c-ink);
  background: var(--c-white);
  -webkit-font-smoothing: antialiased;

  /* Der Bestand hat keinen horizontalen Overflow (geprüft bei 390 px) — das
     soll so bleiben. `overflow-x: hidden` wäre hier die bequeme Lösung und
     genau die falsche: Sie versteckt den Fehler, statt ihn zu verhindern.
     Im Bestand hält `overflow: hidden` acht aus dem Viewport ragende Elemente
     des Rezensions-Karussells zusammen. */
}

/* --- Überschriften ---------------------------------------------------------
   ⚠️ Kein `text-transform: capitalize`. Im Bestand steht es auf allen
   Überschriften und erzeugt "Was Wir Ihnen Bieten" und "Glas- Und
   Metallgeländer" — eine Regel aus dem englischen Vorlagen-Kit, im Deutschen
   schlicht falsch. */

h1,
h2,
h3,
h4 {
  font-weight: var(--fw-halbfett);
  color: var(--c-navy);
  text-wrap: balance;

  /* ⚠️ Deutsche Komposita in Displaygröße sprengen das Handy.
     Gemessen am Staging: Die h1 "Datenschutzerklärung" ist bei 390 px und
     40 px Schriftgröße 455 px breit und ragte 80 px aus der Seite — der
     einzige horizontale Überlauf im ganzen Projekt. Ein einzelnes Wort kann
     nicht umbrechen, `text-wrap: balance` hilft dagegen nicht.

     `hyphens: auto` greift, weil <html lang="de"> gesetzt ist, und trennt
     sauber nach Sprechsilben. `overflow-wrap` ist das Netz darunter, falls
     einem Browser das Trennmuster fehlt: dann bricht das Wort hart, statt die
     Seite seitlich verschiebbar zu machen.

     Beides wirkt nur, wenn eine Zeile sonst nicht passt — auf dem Desktop
     ändert sich nichts. */
  hyphens: auto;
  overflow-wrap: break-word;
}

h1 {
  font-size: var(--t-display);
  line-height: var(--lh-display);
  letter-spacing: -0.02em;
}

h2 {
  font-size: var(--t-h2);
  line-height: var(--lh-h2);
  letter-spacing: -0.01em;
}

h3 {
  font-size: var(--t-h3);
  line-height: var(--lh-h3);
}

h4 {
  font-size: var(--t-body);
  line-height: var(--lh-h3);
}

p,
li {
  text-wrap: pretty;
}

/* Fließtext bekommt eine Lesebreite. Ohne sie laufen Zeilen auf einem
   1440-px-Schirm über 120 Zeichen — messbar schlechter lesbar. */
p {
  max-width: var(--w-text);
}

/* --- Links -----------------------------------------------------------------
   --c-accent-deep, nicht --c-accent: Letzteres erreicht auf Weiß nur 2,9:1.
   Siehe die Warnung in 10-tokens.css. */

a {
  color: var(--c-accent-deep);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  transition: color var(--uebergang);
}

a:hover {
  color: var(--c-navy);
}

/* --- Fokus -----------------------------------------------------------------
   Im Bestand gibt es KEINEN sichtbaren Fokusring. Wer die Seite mit der
   Tastatur bedient, sieht nicht, wo er steht. Das hier ist die Korrektur, und
   sie gilt global — nicht pro Komponente, sonst wird sie irgendwo vergessen. */

:focus-visible {
  outline: var(--fokus-breite) solid var(--c-accent-deep);
  outline-offset: var(--fokus-abstand);
  border-radius: var(--r);
}

/* Auf dunklen Bändern trägt der dunkle Ring nicht. Der Hero gehört dazu, ist
   aber bewusst kein .band-dunkel — Begründung bei .hero .knopf-rahmen. */
.band-dunkel :focus-visible,
.hero :focus-visible {
  outline-color: var(--c-accent-hell);
}

/* --- Bilder ----------------------------------------------------------------
   width/height stehen im Markup (der {% bild %}-Shortcode setzt sie), damit
   der Browser den Platz vor dem Laden kennt und nichts springt. Hier wird das
   Seitenverhältnis daraus wieder freigegeben. */

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

img {
  height: auto;
}

/* ⚠️ Muss NACH dem Block darüber stehen und braucht das !important.
   Das `svg { display: block }` oben stammt aus dem Autoren-Stylesheet und
   schlägt damit das `[hidden] { display: none }` des Browsers — die Kaskade
   entscheidet zuerst nach Ursprung, erst danach nach Spezifität.

   Gebraucht wird das u. a. vom Menü-Schalter, der bis zum Laden von seite.js
   `hidden` trägt. */
[hidden] {
  display: none !important;
}

/* Die <symbol>-Ablage für das Logo, ganz oben im <body>.

   🔴 Sie darf NICHT über `hidden` bzw. `display: none` verschwinden. In einem
   display:none-Teilbaum baut Chrome aus <linearGradient> keine Paint-Server
   mehr. Die vier Glasscheiben des Logos füllen sich per fill="url(#logo-glas-N)"
   aus genau diesen Verläufen — sie wurden dadurch gar nicht gezeichnet, das
   Geländer blieb weiß. (Feedback Tom, 05.08.2026.)

   Stattdessen aus dem Fluss genommen und auf 0×0 gesetzt: Das hält den Kopf
   ebenso zuverlässig an Ort und Stelle wie display:none — ohne eine der beiden
   Maßnahmen wird die Ablage wegen `svg { display: block }` zu einem
   300×150-px-Kasten und schiebt jede Seite um 150 px nach unten —, lässt die
   Verläufe aber leben. Für Screenreader sorgt aria-hidden am Element selbst. */
.svg-ablage {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* --- Formulare -------------------------------------------------------------
   Eingabefelder erben die Schrift nicht von selbst. Und: 16 px ist die Grenze,
   unter der iOS beim Fokussieren in ein Feld hineinzoomt. */

input,
textarea,
select,
button {
  font: inherit;
  color: inherit;
}

input,
textarea,
select {
  font-size: max(16px, var(--t-meta));
}

/* --- Anschriften -----------------------------------------------------------
   Browser setzen <address> kursiv. Das ist eine Vorgabe aus der Frühzeit des
   Webs und sieht in einer serifenlosen Schrift nach Zitat aus. Die Auszeichnung
   selbst ist richtig und bleibt — sie sagt Vorlesesoftware und Suchmaschinen,
   dass hier eine Anschrift steht. Nur die Schrägstellung fällt weg, und zwar
   global: Im Fuß stand die Regel schon, auf der Kontaktseite fehlte sie, und
   dieselbe Adresse sah auf zwei Seiten verschieden aus. */
address {
  font-style: normal;
}

/* --- Tabellen (Rechtstexte) ------------------------------------------------ */

table {
  border-collapse: collapse;
  width: 100%;
}

/* --- Sprunglink ------------------------------------------------------------
   Im Bestand misst der "Zum Inhalt springen"-Link 1×1 px — er existiert, ist
   aber unbenutzbar. Hier ist er außerhalb des Bildes geparkt und kommt beim
   Fokussieren in voller Größe herein. */

.sprunglink {
  position: absolute;
  top: var(--s-2);
  left: var(--s-2);
  z-index: 100;
  transform: translateY(-200%);
  padding: var(--s-3) var(--s-4);
  min-height: var(--tap-min);
  display: inline-flex;
  align-items: center;
  background: var(--c-navy);
  color: var(--c-white);
  text-decoration: none;
  border-radius: var(--r);
  transition: transform var(--uebergang);
}

.sprunglink:focus-visible {
  transform: translateY(0);
  color: var(--c-white);
}

/* --- Nur für Hilfsmittel --------------------------------------------------- */

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

/* ===========================================================================
   Komponenten — Bausteine, die auf mehreren Seiten vorkommen.

   Stand Roadmap-Schritt 3 (Fundament): Layout, Sektionen, Knöpfe, Karten,
   Chips, Kopf und Fuß. Die seitenspezifischen Teile (Hero, Galerie-Raster,
   Lichtkasten, Formular) kommen in Schritt 4 dazu.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   Layout

   Zwei Container statt vier verschiedener Inhaltsbreiten wie im Bestand
   (1368 / 1405 / 1425 / 1000 px — vier Werte, keine Absicht dahinter).
   --------------------------------------------------------------------------- */

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

.huelle-schmal {
  width: 100%;
  max-width: 800px;
  margin-inline: auto;
  padding-inline: var(--pad-rand);
}

/* Der vertikale Rhythmus. Steht hier und NUR hier — nie als leeres Element
   im Markup. Siehe die Warnung bei --pad-section in 10-tokens.css. */
.sektion {
  padding-block: var(--pad-section);
}

.sektion-hell {
  background: var(--c-paper);
}

.band-dunkel {
  background: var(--c-steel-900);
  color: var(--c-white);
}

.band-dunkel h1,
.band-dunkel h2,
.band-dunkel h3,
.band-dunkel h4 {
  color: var(--c-white);
}

/* ⚠️ .lead trägt --c-ink-muted (#4A5157). Auf steel-900 sind das 1,3:1 — der
   Text wäre praktisch unsichtbar. Dieselbe Fehlerart, an der im Bestand alle
   drei gemessenen Seiten scheitern, nur andersherum. */
.band-dunkel .lead {
  color: var(--c-white);
}

/* ⚠️ `:not(.knopf)` ist hier keine Feinheit, sondern eine Kontrastkorrektur.
   `.band-dunkel a` hat Spezifitaet (0,1,1) und schlaegt damit `.knopf-akzent`
   (0,1,0). Ohne den Ausschluss traegt der Knopf "Kontaktieren Sie uns" im
   CTA-Band --c-accent-hell auf --c-accent — hellblaue Schrift auf hellblauer
   Flaeche. Lighthouse hat genau das am Staging als einzige
   Accessibility-Beanstandung gemeldet (96 statt 100).

   Knoepfe bringen ihre Farben selbst mit und brauchen die Link-Regel nicht. */
.band-dunkel a:not(.knopf) {
  color: var(--c-accent-hell);
}

.band-dunkel a:not(.knopf):hover {
  color: var(--c-white);
}

/* Abstand zwischen Geschwistern in einer Sektion — als Klasse am Container,
   nicht als Rand am einzelnen Kind. So bleibt Abstand eine Eigenschaft der
   Anordnung. */
.stapel > * + * {
  margin-block-start: var(--s-4);
}

.stapel-weit > * + * {
  margin-block-start: var(--s-8);
}

.sektionskopf {
  margin-block-end: var(--s-12);
}

.lead {
  font-size: var(--t-lead);
  line-height: var(--lh-lead);
  color: var(--c-ink-muted);
  max-width: var(--w-text);
}

/* Der Einleitungssatz klebte an seiner Überschrift: `* { margin: 0 }` aus
   20-basis.css raeumt alle Raender ab, und die Klassen darueber setzen Abstand
   nur ZWISCHEN Bloecken (`.stapel*`) oder UNTER dem ganzen Kopf
   (`.sektionskopf`) — nie INNERHALB, zwischen Ueberschrift und Text. Der
   Abstand war dort exakt 0 px (Tom, 05.08.2026).

   24 px, nicht die 32 px von `.zweispalt h1, .zweispalt h2` weiter unten: Dort
   steht eine Ueberschrift ueber einem ganzen Inhaltsblock, hier ueber ihrem
   eigenen Einleitungssatz. Der soll Luft bekommen, aber zugehoerig bleiben. */
h1 + .lead,
h2 + .lead {
  margin-block-start: var(--s-6);
}

/* Kleine Überzeile über einer Überschrift. Ersetzt die h6-Elemente, die im
   Bestand als Gestaltungsmittel missbraucht wurden (Galerie: h1 → h6 → h2). */
.ueberzeile {
  font-size: var(--t-meta);
  font-weight: var(--fw-mittel);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-accent-deep);
  margin-block-end: var(--s-2);
}

.band-dunkel .ueberzeile {
  color: var(--c-accent-hell);
}

/* ---------------------------------------------------------------------------
   Knöpfe

   ⚠️ Ein Knopf ist ein <a> mit href oder ein <button>. Nie ein <a> ohne href:
   Im Bestand sind die drei prominentesten Schaltflächen der Startseite
   ("Planung / Fertigung / Montage") genau das — sie sehen aus wie Knöpfe und
   führen nirgendwohin.
   --------------------------------------------------------------------------- */

.knopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: var(--tap-min);
  padding: var(--s-3) var(--s-6);
  border: 2px solid transparent;
  border-radius: var(--r);
  font-size: var(--t-body);
  font-weight: var(--fw-mittel);
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--uebergang),
    border-color var(--uebergang),
    color var(--uebergang);
}

/* Gefüllt mit Stahlblau, weiße Schrift darauf.

   ⚠️ ENTSCHEIDUNG TOM, 2026-08-05 — NICHT ZURÜCKDREHEN.
   Weiß auf #609FBC sind 2,9:1 und damit unter WCAG AA. Bis zum 05.08. stand
   hier --c-navy (5,6:1). Der Wechsel ist gewollt, die Kontrastzahlen lagen
   bei der Entscheidung vor. Vollständige Begründung und der gemessene Preis
   (Lighthouse Accessibility 96 statt 100) stehen in 10-tokens.css. */
.knopf-akzent {
  background: var(--c-accent);
  border-color: var(--c-accent);
  color: var(--c-white);
}

.knopf-akzent:hover {
  background: var(--c-accent-deep);
  border-color: var(--c-accent-deep);
  color: var(--c-white);
}

.knopf-fest {
  background: var(--c-navy);
  border-color: var(--c-navy);
  color: var(--c-white);
}

.knopf-fest:hover {
  background: var(--c-steel-900);
  border-color: var(--c-steel-900);
  color: var(--c-white);
}

.knopf-rahmen {
  background: transparent;
  border-color: currentColor;
  color: var(--c-navy);
}

.knopf-rahmen:hover {
  background: var(--c-navy);
  color: var(--c-white);
}

/* ⚠️ Der Hero muss hier mitgenannt werden, obwohl er kein .band-dunkel ist.
   Er darf es auch nicht sein: `.band-dunkel a { color: --c-accent-hell }` haette
   hoehere Spezifitaet als `.knopf-akzent` und faerbte die Schrift auf der
   Akzentflaeche hellblau — hellblau auf hellblau.

   Ohne diese Zeile steht "Kontakt aufnehmen" in --c-navy auf dem dunklen Foto
   und ist praktisch unsichtbar. Genau so stand es beim ersten Staging-Lauf. */
.band-dunkel .knopf-rahmen,
.hero .knopf-rahmen {
  color: var(--c-white);
}

.band-dunkel .knopf-rahmen:hover,
.hero .knopf-rahmen:hover {
  background: var(--c-white);
  color: var(--c-navy);
}

/* Auf dem Handy stehen Knöpfe untereinander und über die volle Breite.

   Warum nicht einfach nebeneinander umbrechen lassen: Dann bestimmt der
   Textinhalt die Breite, und "Galerie ansehen" wird schmaler als "Kontakt
   aufnehmen" darunter. Bei drei Knopfreihen auf der Startseite ergibt das
   sechs verschiedene Breiten — unruhig, ohne dass ein Grund erkennbar wäre.
   Volle Breite ist die einzige Breite, die überall dieselbe ist.

   Die Grenze liegt bei 600 px, nicht bei den 900 px des Kopfes: Ab dort
   passen zwei Knöpfe nebeneinander, ohne dass die Zeile gedrängt wirkt. */
.knopf-reihe {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--s-3);
}

@media (min-width: 600px) {
  .knopf-reihe {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }
}

/* Zwei gleich breite Knöpfe nebeneinander — im Hero und im Schlussband, wo
   zwei gleichrangige Wege angeboten werden. `flex-basis: 0` statt `auto`:
   sonst geht der längere Text als Startbreite ein und die Knöpfe werden
   wieder ungleich. */
@media (min-width: 600px) {
  .knopf-reihe-gleich > .knopf {
    flex: 1 1 0;
  }
}

/* Eine Knopfreihe, die einen Block abschliesst — etwa "Alle Arbeiten ansehen"
   unter dem Galerie-Teaser. Der Abstand haengt an der Reihe, nicht an einem
   leeren Element davor. */
.aktionszeile {
  margin-block-start: var(--s-8);
}

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

.karte {
  display: flex;
  flex-direction: column;
  background: var(--c-white);
  border: 1px solid var(--c-line);
  border-radius: var(--r);
  overflow: hidden;
}

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

.karte-text {
  padding: var(--s-6);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  flex: 1;
}

/* Karten stehen im Raster gleich hoch — durch das Grid, nicht durch
   gleich lange Texte. Im Bestand ist Spalte 3 mehr als doppelt so lang
   wie Spalte 1. */
.raster {
  display: grid;
  gap: var(--s-6);
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .raster-3 {
    grid-template-columns: repeat(3, 1fr);
  }

  .raster-2 {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ---------------------------------------------------------------------------
   Chips — Filter in der Galerie, Aufzählungen auf Leistungskarten
   --------------------------------------------------------------------------- */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: var(--tap-min);
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--c-line);
  border-radius: var(--r-full);
  background: var(--c-white);
  font-size: var(--t-meta);
  font-weight: var(--fw-mittel);
  color: var(--c-ink);
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--uebergang),
    border-color var(--uebergang);
}

.chip:hover {
  border-color: var(--c-accent-deep);
  color: var(--c-ink);
}

.chip[aria-pressed='true'],
.chip[aria-current='true'] {
  background: var(--c-accent);
  border-color: var(--c-accent);
  color: var(--c-navy);
}

/* Die Anzahl je Filter.
   ⚠️ KEIN `opacity` zum Absetzen. Ein erster Versuch mit opacity: 0.7 hat den
   Kontrast unter den Grenzwert gedrückt und war Lighthouses einzige
   Accessibility-Beanstandung — dieselbe Fehlerart, an der im Bestand alle drei
   gemessenen Seiten scheitern. Abgesetzt wird über das Schriftgewicht, das
   kostet keinen Kontrast. */
.chip-zahl {
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-normal);
}

.chip-reihe {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}

/* Statische Aufzählung ohne Interaktion — z. B. die sieben Einträge unter
   "Reparatur und Extras", die im Bestand als lange Liste die Spalte sprengen. */
.merkmalsliste {
  list-style: none;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}

.merkmalsliste li {
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-full);
  background: var(--c-paper);
  font-size: var(--t-meta);
  color: var(--c-ink-muted);
}

/* ---------------------------------------------------------------------------
   Kopf

   Sticky, 64 px, alle Tippziele ≥ 44 px. Im Bestand misst die
   Menü-Schaltfläche 36×30 px und ist damit eines von neun zu kleinen Zielen.

   Der Telefonknopf ist neu und begründet: Der Betrieb lebt laut Linda über
   Mundpropaganda — wer eine Empfehlung bekommt und googelt, will die Nummer,
   nicht ein Formular.
   --------------------------------------------------------------------------- */

.kopf {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--c-white);
  border-block-end: 1px solid transparent;
  transition: border-color var(--uebergang);
}

/* Die Kante erscheint erst, wenn Inhalt darunter durchläuft. Gesetzt von
   seite.js über einen Fühler ganz oben im Dokument. */
.kopf[data-geschoben='true'] {
  border-block-end-color: var(--c-line);
}

.kopf-innen {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: var(--kopf-hoehe);
}

/* Das Logo ist 40 px hoch, der Link darum aber mindestens 44 — die
   Zielflaeche zaehlt, nicht die Grafik. Ohne das misst der Link 76x40 und
   liegt damit unter der Mindestgroesse, die diese Seite ueberall einhaelt. */
.kopf-logo {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  min-height: var(--tap-min);
}

.kopf-logo svg {
  height: 40px;
  width: auto;
}

.kopf-rechts {
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

.kopf-menue {
  display: none;
}

.kopf-menue ul {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  list-style: none;
  padding: 0;
}

.kopf-menue a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  padding-inline: var(--s-3);
  color: var(--c-ink);
  text-decoration: none;
  font-weight: var(--fw-mittel);
  border-radius: var(--r);
}

.kopf-menue a:hover {
  color: var(--c-accent-deep);
}

.kopf-menue a[aria-current='page'] {
  color: var(--c-accent-deep);
  box-shadow: inset 0 -2px 0 var(--c-accent);
}

/* Icon-Schaltflächen: quadratisch, mindestens 44 px. */
.icon-knopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  height: var(--tap-min);
  border: 1px solid var(--c-line);
  border-radius: var(--r);
  background: var(--c-white);
  color: var(--c-navy);
  cursor: pointer;
}

.icon-knopf:hover {
  border-color: var(--c-accent-deep);
  color: var(--c-accent-deep);
}

.symbol {
  width: 1.25em;
  height: 1.25em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Mobil: Telefonknopf als Icon, Menü als Overlay. */
.menue-schalter {
  display: inline-flex;
}

/* Der Anrufknopf ist mobil ein reines Icon und bekommt deshalb exakt die
   Geometrie des Menüknopfs daneben: 44 x 44, keine Innenabstände.

   Vorher erbte er das Knopf-Padding von 24 px und stand als ~74 x 44 breite
   Pille neben einem 44 x 44 großen Quadrat — zwei Nachbarn, die dasselbe
   Gewicht haben sollen und verschieden groß waren. Die Fläche bleibt
   akzentblau, damit der Anruf der auffälligere der beiden Wege ist. */
.kopf-telefon {
  width: var(--tap-min);
  height: var(--tap-min);
  min-height: 0;
  padding: 0;
  flex-shrink: 0;
}

@media (min-width: 900px) {
  .kopf-menue {
    display: block;
  }

  .menue-schalter {
    display: none;
  }

  /* Ab hier verschwindet der Anrufknopf ganz statt nur seinen Text zu
     verlieren: Auf einem Rechner führt ein tel:-Link ins Leere oder öffnet
     ein Programm, das niemand erwartet. Die Nummer steht auf der
     Kontaktseite und im Fuß jeder Seite.

     ⚠️ Der umgebende .kopf-rechts muss MIT verschwinden. Bleibt er stehen,
     ist er zwar leer und 0 px breit, zählt für `space-between` aber als
     drittes Element — und die Textnavigation landet in der Bildschirmmitte
     statt rechts. Genau so stand sie beim ersten Lauf am 05.08. */
  .kopf-telefon,
  .kopf-rechts {
    display: none;
  }
}

/* Das aufklappende Menü. Ohne JavaScript bleibt es geschlossen — deshalb
   stehen alle fünf Adressen zusätzlich im Fuß, der immer offen ist. */
.menue-overlay {
  position: fixed;
  inset: var(--kopf-hoehe) 0 0 0;
  z-index: 40;
  background: var(--c-white);
  padding: var(--s-6) 0;
  overflow-y: auto;
}

.menue-overlay[hidden] {
  display: none;
}

.menue-overlay ul {
  list-style: none;
  padding: 0;
}

.menue-overlay a {
  display: flex;
  align-items: center;
  min-height: 56px;
  padding-inline: var(--pad-rand);
  border-block-end: 1px solid var(--c-line);
  color: var(--c-ink);
  text-decoration: none;
  font-size: var(--t-h3);
  font-weight: var(--fw-mittel);
}

/* ---------------------------------------------------------------------------
   Fuß

   Auf JEDER Seite identisch und mit allen fünf Adressen. Im Bestand fehlt das
   Fußmenü auf Impressum und Datenschutz komplett (gemessen: 0 Einträge) — von
   der einen Rechtsseite kommt man nicht zur anderen.

   Mobil NICHT eingeklappt: Im Fuß ist Platz, und der 33×33-px-Schalter des
   Bestands ist ein Bedienhindernis ohne Nutzen.
   --------------------------------------------------------------------------- */

.fuss {
  background: var(--c-steel-900);
  color: var(--c-white);
  padding-block: var(--s-16) var(--s-8);
}

.fuss-raster {
  display: grid;
  gap: var(--s-12);
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .fuss-raster {
    grid-template-columns: 1.2fr 1fr 1fr;
    gap: var(--s-8);
  }
}

.fuss h2,
.fuss h3 {
  font-size: var(--t-meta);
  font-weight: var(--fw-halbfett);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-accent-hell);
  margin-block-end: var(--s-4);
}

.fuss-logo svg {
  height: 48px;
  width: auto;
  margin-block-end: var(--s-4);
}

.fuss ul {
  list-style: none;
  padding: 0;
}

.fuss li + li {
  margin-block-start: var(--s-1);
}

.fuss a,
.fuss p {
  color: var(--c-white);
}

/* min-width zusaetzlich zu min-height: Kurze Eintraege wie "Start" sind sonst
   nur 40 px breit. Die Links stehen linksbuendig untereinander, die zusaetzliche
   Flaeche ist also unsichtbar — aber traegt den Daumen. */
.fuss a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  min-width: var(--tap-min);
  text-decoration: none;
}

.fuss a:hover {
  color: var(--c-accent-hell);
  text-decoration: underline;
}

.fuss-schluss {
  margin-block-start: var(--s-12);
  padding-block-start: var(--s-6);
  border-block-start: 1px solid var(--c-line-dunkel);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-6);
  justify-content: space-between;
  font-size: var(--t-meta);
  color: var(--c-accent-hell);
}

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

   Ein Foto über die volle Breite, Text darüber. Im Bestand steht hier eine
   Architekturaufnahme des Wohnhauses Wismer mit "30 Zone"-Schild und parkenden
   Autos — ein Metallbauer führt mit einem Foto, das seine Leistung nicht zeigt.
   Und darunter dreimal die Wortmarke plus drei Schaltflächen OHNE href.

   Die Höhe steht in svh, nicht vh: Auf dem Handy meint vh die Fensterhöhe OHNE
   die Adressleiste. Der Hero wäre beim Laden höher als der Bildschirm und
   schrumpfte beim ersten Scrollen — genau die Art Sprung, die CLS misst.
   --------------------------------------------------------------------------- */

.hero {
  position: relative;
  isolation: isolate; /* hält die beiden z-index unten in dieser Sektion */
  display: flex;
  align-items: flex-end;
  min-height: 70svh;
  padding-block: var(--s-24) var(--s-12);
  background: var(--c-steel-900); /* steht, bevor das Foto da ist */
  color: var(--c-white);
}

.hero-bild {
  position: absolute;
  inset: 0;
  z-index: -2;
}

.hero-bild picture,
.hero-bild img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Auf dem Handy ist der Hero hoeher als breit. `cover` skaliert dann auf die
   HOEHE und schneidet seitlich ab — sichtbar bleiben rund 44 % der Bildbreite.
   Zentriert liegt dieser Streifen auf der weissen Hauswand; bei 72 % faellt
   die dunkle Ecke des Gelaenders mit dem Handlauf hinein, also das Produkt.

   Auf dem Desktop ist der Hero breiter als hoch, dort wird oben und unten
   beschnitten und dieser Wert aendert nichts. */
.hero-bild img {
  object-position: 72% center;
}

.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to top, var(--c-overlay-unten), var(--c-overlay-oben));
}

/* Die Hero-Überschrift bricht fest in zwei Zeilen (siehe index.njk) und
   braucht deshalb eine eigene Größe.

   --t-display beginnt bei 2,5 rem = 40 px. Bei dieser Größe ist "Alles aus
   einer Hand" auf einem 390-px-Gerät breiter als der verfügbare Platz und
   die Überschrift würde dreizeilig — der Umbruch, den die festen Zeilen
   gerade verhindern sollen.

   Die Werte sind gemessen, nicht geschätzt. "Alles aus einer Hand" ist die
   längere der beiden Zeilen und misst in Poppins 600 bei 30 px genau 298 px.
   Daraus folgt die Bedingung, die für JEDE Bildschirmbreite gelten muss:

       Schriftgröße < 30 px * (Breite − 32 px Seitenrand) / 298 px

   Mit `8vw` ist sie ab 156 px Breite erfüllt, also überall. Bei 360 px, dem
   schmalsten Gerät, mit dem zu rechnen ist, greift der Mindestwert 30 px und
   lässt 15 px Rest; bei 390 px sind es 31,2 px. Der Deckel von 4,5 rem
   greift ab 562 px — dort ist die Grenze bei 53 px, also unkritisch.

   Wer den Text der Überschrift ändert, muss diese Rechnung neu machen: Ein
   längeres Wort verschiebt die 298 px und damit alles Weitere.

   `max-width` entfällt: Sie steuerte den automatischen Umbruch, den es hier
   nicht mehr gibt, und würde jetzt nur noch stören. */
.hero h1 {
  color: var(--c-white);
}

.hero-titel {
  font-size: clamp(1.875rem, 8vw, 4.5rem);
}

.hero-titel span {
  display: block;
}

.hero-inhalt > * + * {
  margin-block-start: var(--s-6);
}

/* Planung · Fertigung · Montage. Im Bestand sind das drei Schaltflächen ohne
   Ziel — die prominenteste Fläche der Startseite führt nirgendwohin. Als
   Aussage sind die drei Wörter richtig, als Knopf eine Lüge. Also eine Zeile.

   Die Trennpunkte sind Pseudo-Elemente: So stehen sie nicht im Textinhalt und
   werden nicht vorgelesen. */
.hero-schritte {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-3);
  list-style: none;
  padding: 0;
  font-size: var(--t-meta);
  font-weight: var(--fw-mittel);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-white);
}

.hero-schritte li + li::before {
  content: '·';
  margin-inline-end: var(--s-3);
  color: var(--c-accent);
}

@media (min-width: 900px) {
  .hero {
    min-height: 78svh;
  }

  /* Der Text bleibt unten, aber auf der linken Hälfte. Unten ist die
     Deckschicht am dichtesten; ein vertikal zentrierter Textblock stünde bei
     wechselnden Fotos irgendwann auf einer hellen Hauswand. */
  .hero-inhalt {
    max-width: 46rem;
  }
}

/* ---------------------------------------------------------------------------
   Medienband — Text und Foto nebeneinander ("Über uns")

   Im Bestand steht dieser Text ganz unten, rechtsbündig, mit leerer linker
   Bildschirmhälfte. Inhaltlich ist er das Stärkste der Seite.
   --------------------------------------------------------------------------- */

.medienband {
  display: grid;
  gap: var(--s-8);
  align-items: center;
}

.medienband img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--r);
}

.medienband-text > * + * {
  margin-block-start: var(--s-4);
}

@media (min-width: 900px) {
  .medienband {
    grid-template-columns: 1fr 1fr;
    gap: var(--s-16);
  }
}

/* ---------------------------------------------------------------------------
   "Warum wir?" — vier Werte mit Ring

   Steht an der Stelle, an der im Bestand drei erfundene Rezensionen liefen
   ("Sabine Keller", "Jan Fischer", "Lisa Müller" — keine Spur im Netz,
   § 5 UWG). Kommen echte Kundenstimmen von Linda, wandern sie hierher.

   Bis 05.08.2026 stand unter den Werten eine Faktenzeile (HRB · Birstein ·
   Alles aus einer Hand). Sie ist entfallen: Alle drei Angaben stehen ohnehin
   auf derselben Seite — die Registernummer im Fuß, Birstein und "Alles aus
   einer Hand" in der neuen Hero-Überschrift.
   --------------------------------------------------------------------------- */

.werte {
  display: grid;
  gap: var(--s-8);
  grid-template-columns: 1fr;
}

.wert h3 {
  margin-block-end: var(--s-2);
}

.wert p {
  color: var(--c-ink-muted);
}

@media (min-width: 640px) {
  .werte {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1000px) {
  .werte {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* --- Der Ring --------------------------------------------------------------
   Zwei deckungsgleiche Kreise: eine graue Bahn und darüber die farbige
   Füllung. Gefüllt wird über `stroke-dasharray`/`-dashoffset` — ein Strich
   von der Länge des vollen Umfangs, der um denselben Betrag verschoben und
   dann zurückgeholt wird.

   ⚠️ --ring-umfang gehört zu r=54 in index.njk (2 * pi * 54 = 339,29).
   Wer den Radius im Markup ändert, muss diese Zahl mitändern, sonst füllt
   der Ring zu weit oder bleibt offen.

   Der Ausgangszustand ist VOLL. Erst das Skript setzt ihn auf leer und lässt
   ihn laufen — ohne JavaScript steht also der Endzustand da, nicht ein
   leerer Ring. Dasselbe Prinzip wie bei der Galerie: Inhalt zuerst,
   Bewegung danach. */
.wert-ring {
  --ring-umfang: 339.29;
  position: relative;
  display: grid;
  place-items: center;
  width: 104px;
  height: 104px;
  margin-block-end: var(--s-4);
}

.wert-ring svg {
  width: 100%;
  height: 100%;
  /* Der Kreis beginnt in SVG bei 3 Uhr. Gedreht startet er oben. */
  transform: rotate(-90deg);
}

.wert-ring circle {
  fill: none;
  stroke-width: 8;
  stroke-linecap: round;
}

.wert-ring-bahn {
  stroke: var(--c-line);
}

.wert-ring-fuellung {
  stroke: var(--ring-farbe, var(--c-accent));
  stroke-dasharray: var(--ring-umfang);
  stroke-dashoffset: 0;
}

/* Nur wenn das Skript übernommen hat, wird überhaupt animiert. */
[data-ring='laeuft'] .wert-ring-fuellung {
  transition: stroke-dashoffset 1.4s cubic-bezier(0.22, 0.61, 0.36, 1);
}

.wert-zahl {
  position: absolute;
  font-size: var(--t-h3);
  font-weight: var(--fw-halbfett);
  color: var(--c-navy);
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  [data-ring='laeuft'] .wert-ring-fuellung {
    transition: none;
  }
}

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

   ⚠️ DER KERN-BUG DES BESTANDS. Dort setzt Elementor die Bilder per JavaScript
   als CSS-Hintergrund auf .e-gallery-image: Beim Aufruf stehen 0 von 27 Fotos
   da, auch nach drei Sekunden, und acht Kacheln laden nie. Zweite Folge: Die
   Seite enthält genau zwei <img> — das ganze Portfolio ist für Suchmaschinen
   unsichtbar.

   Hier ist jede Kachel ein <a> auf das volle Bild mit einem echten <img>
   darin. Der Lichtkasten hängt sich per JavaScript davor; fällt es aus, bleibt
   ein funktionierender Link.

   Alle Kacheln haben dasselbe Seitenverhältnis, und das ist Absicht: Ein
   Raster mit zeilenübergreifenden Kacheln reißt Löcher, sobald der Filter
   Einträge ausblendet.
   --------------------------------------------------------------------------- */

.galerie-raster {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: repeat(2, 1fr);
}

@media (min-width: 700px) {
  .galerie-raster {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s-4);
  }
}

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

/* `grid` statt `block`, und das Bild als Block-Element.

   Vorher lag unter dem Bild der Grundlinien-Spalt, den ein inline-Element in
   seiner Zeilenbox erzeugt, und in der doppelt hohen Teaser-Kachel füllte
   `height: 100%` die gestreckte Rasterzelle nicht aus. Beides legte den
   hellgrauen Hintergrund (--c-paper) als Streifen unter dem Foto frei. Als
   Rasterzelle deckt das Bild seinen Kasten vollständig ab.

   Der Hintergrund bleibt trotzdem stehen: Er ist die Fläche, die man sieht,
   solange das Foto noch lädt. */
.galerie-kachel {
  display: grid;
  overflow: hidden;
  border-radius: var(--r);
  background: var(--c-paper);
}

.galerie-kachel img {
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform var(--uebergang);
}

/* Hochformate verlieren im 4:3-Ausschnitt am meisten. Der Schwerpunkt liegt
   bei Treppen und Detailaufnahmen oben, nicht in der Mitte. */
.galerie-kachel-hoch img {
  object-position: center 35%;
}

.galerie-kachel:hover img {
  transform: scale(1.03);
}

@media (prefers-reduced-motion: reduce) {
  .galerie-kachel:hover img {
    transform: none;
  }
}

/* ---------------------------------------------------------------------------
   Galerie-Teaser der Startseite

   Sechs Fotos statt des blauen Bandes mit der Flip-Box, die mobil 315×280 px
   misst und nichts transportiert. Warum an dieser Stelle: Die Leistungen
   darüber sagen, WAS gemacht wird — die Fotos beweisen es.

   Ab 900 px führt die erste Kachel doppelt breit und doppelt hoch. 3 Spalten ×
   3 Zeilen = 9 Zellen, die große nimmt 4, die fünf kleinen 5. Es geht genau
   auf, und jede Zeile enthält mindestens eine kleine Kachel, die ihre Höhe
   bestimmt.
   --------------------------------------------------------------------------- */

@media (min-width: 900px) {
  .teaser-raster {
    grid-template-columns: repeat(3, 1fr);
  }

  .teaser-raster > :first-child {
    grid-column: span 2;
    grid-row: span 2;
  }

  .teaser-raster > :first-child img {
    height: 100%;
    aspect-ratio: auto;
  }
}

/* ---------------------------------------------------------------------------
   Lichtkasten

   Als <dialog>: Fokusfalle, Escape und das Stilllegen des Hintergrunds bringt
   der Browser mit — keine Bibliothek, kein Dritt-Request. Beschriftet wird mit
   dem Alt-Text; im Bestand steht dort der rohe Dateiname "Geländer-05".
   --------------------------------------------------------------------------- */

.lichtkasten {
  width: 100%;
  max-width: 100%;
  height: 100%;
  max-height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--c-white);
  overflow: hidden;
}

.lichtkasten::backdrop {
  background: var(--c-backdrop);
}

.lichtkasten-buehne {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: var(--s-16) var(--s-4) var(--s-24);
}

.lichtkasten-buehne img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  object-fit: contain;
}

.lichtkasten-text {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  padding: var(--s-4) var(--s-6);
  margin: 0;
  max-width: none;
  text-align: center;
  font-size: var(--t-meta);
  color: var(--c-white);
}

.lichtkasten-knopf {
  position: absolute;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  height: var(--tap-min);
  border: 1px solid var(--c-line-dunkel);
  border-radius: var(--r);
  background: var(--c-steel-900);
  color: var(--c-white);
  cursor: pointer;
}

.lichtkasten-knopf:hover {
  border-color: var(--c-accent);
  color: var(--c-accent-hell);
}

.lichtkasten-schliessen {
  top: var(--s-4);
  right: var(--s-4);
}

.lichtkasten-zurueck {
  top: 50%;
  left: var(--s-4);
  transform: translateY(-50%);
}

.lichtkasten-vor {
  top: 50%;
  right: var(--s-4);
  transform: translateY(-50%);
}

.lichtkasten-zurueck .symbol {
  transform: rotate(180deg);
}

/* ---------------------------------------------------------------------------
   Kontaktseite
   --------------------------------------------------------------------------- */

/* "So erreichen Sie uns" trägt das <h1> der Seite, seit der eigene Seitenkopf
   entfallen ist (Tom, 05.08.2026). Es soll aber weiter wie die Überschrift
   daneben aussehen — beide Spalten sind gleichrangig. Also h1 in der Rolle,
   h2 in der Erscheinung. */
.wie-h2 {
  font-size: var(--t-h2);
  line-height: var(--lh-h2);
}

/* Der Abstand zwischen einer Abschnittsüberschrift und dem, was sie
   überschreibt.

   Vorher klebten "So erreichen Sie uns" und die Telefonzeile darunter
   aneinander: `.stapel-weit > * + *` greift zwischen den Blöcken, nicht
   INNERHALB eines Blocks zwischen Überschrift und Inhalt. Hier steht der
   Wert einmal und gilt für alle drei Abschnitte der Seite. */
.zweispalt h1,
.zweispalt h2 {
  margin-block-end: var(--s-8);
}

.kontaktzeilen {
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--s-4);
}

.kontaktzeile {
  display: flex;
  align-items: flex-start;
  gap: var(--s-4);
}

.kontaktzeile .symbol {
  flex-shrink: 0;
  margin-block-start: 0.2em;
  color: var(--c-accent-deep);
  width: 1.5em;
  height: 1.5em;
}

.kontaktzeile-titel {
  display: block;
  font-size: var(--t-meta);
  font-weight: var(--fw-mittel);
  color: var(--c-ink-muted);
}

/* Die Zielfläche zählt, nicht der Text: "Telefon" allein wäre unter 44 px. */
.kontaktzeile a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
}

/* ---------------------------------------------------------------------------
   Landkarte

   Statt des Google-Maps-iframes, der im Bestand beim Seitenaufruf
   maps.google.com, www.google.com und www.gstatic.com lädt — ohne Einwilligung
   und ohne Consent-Tool.

   Der Marker liegt als eigenes Element über dem Bild: Er bleibt damit auf
   jedem Bildschirm scharf und trägt die Akzentfarbe. scripts/karte.mjs
   schneidet den Ausschnitt so, dass die Betriebsadresse exakt in der Bildmitte
   liegt — deshalb steht er bei 50 % / 50 %.

   Seit 05.08.2026 liegt unten auf der Karte ein Knopf, der auf Klick den
   Google-Rahmen einsetzt. Bis dahin bleibt die Seite frei von fremden
   Requests. Das Seitenverhältnis wird deshalb hier festgehalten, nicht vom
   Bild bestimmt: Sonst springt das Layout in dem Moment, in dem das Bild dem
   iframe weicht.
   --------------------------------------------------------------------------- */

.landkarte {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--r);
  overflow: hidden;
  background: var(--c-paper);
}

.landkarte img,
.landkarte-rahmen {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

.landkarte img {
  object-fit: cover;
}

.landkarte-marke {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--s-6);
  height: var(--s-6);
  transform: translate(-50%, -50%);
  border-radius: var(--r-full);
  background: var(--c-accent-deep);
  border: 3px solid var(--c-white);
}

/* Der Knopf liegt auf dem Bild, unten mittig. Zwei Zeilen: die Handlung und
   darunter, kleiner, was sie auslöst. Der Hinweis gehört an den Knopf und
   nicht in ein Banner — wer klickt, soll im selben Blick lesen können, was
   passiert. */
.landkarte-schalter {
  position: absolute;
  left: 50%;
  bottom: var(--s-4);
  transform: translateX(-50%);
  flex-direction: column;
  gap: 2px;
  max-width: calc(100% - var(--s-8));
  box-shadow: 0 2px 12px rgb(28 32 34 / 0.35);
}

.landkarte-schalter-hinweis {
  font-size: var(--t-meta);
  font-weight: var(--fw-normal);
  opacity: 0.9;
}

/* Ist die Karte geladen, ist der Knopf mit ihr verschwunden (das Skript
   ersetzt den Inhalt) — hier bleibt nur die Fläche zu neutralisieren. */
.landkarte[data-karte='geladen'] {
  background: none;
}

.landkarte-quelle {
  margin-block-start: var(--s-2);
  font-size: var(--t-meta);
  color: var(--c-ink-muted);
}

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

   Sichtbare Feldbeschriftungen über den Feldern — im Bestand sind sie
   vorhanden und gut, das bleibt. Was fehlt, ist alles andere: `aria-required`
   an keinem Feld, kein sichtbarer Fokus.

   Kein reCAPTCHA. Nicht aus Prinzip, sondern gemessen: 26 von 34 Einsendungen
   des Bestands sind Werbung, 18 davon von bekannten SEO-Spam-Domains — alles
   durch das aktive Captcha hindurch. Der Verzicht kostet nachweislich nichts.
   --------------------------------------------------------------------------- */

.formular {
  display: grid;
  gap: var(--s-4);
}

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

.feld label {
  font-size: var(--t-meta);
  font-weight: var(--fw-mittel);
}

.feld input,
.feld textarea {
  padding: var(--s-3) var(--s-4);
  min-height: var(--tap-min);
  border: 1px solid var(--c-line);
  border-radius: var(--r);
  background: var(--c-white);
}

.feld input:hover,
.feld textarea:hover {
  border-color: var(--c-ink-muted);
}

.feld textarea {
  resize: vertical;
}

/* :user-invalid statt :invalid — sonst ist jedes Pflichtfeld rot, bevor
   überhaupt jemand hineingeklickt hat. */
.feld input:user-invalid,
.feld textarea:user-invalid {
  border-color: var(--c-warn);
}

/* Gedämpft, nicht betont: Ein Hinweis darauf, dass etwas WEGgelassen werden
   darf, soll die Beschriftung nicht überstrahlen. (Vorher stand hier
   `.feld-pflicht` im Akzentton an drei von vier Feldern.) */
.feld-optional {
  color: var(--c-ink-muted);
  font-weight: var(--fw-normal);
}

.feld-paar {
  display: grid;
  gap: var(--s-4);
}

@media (min-width: 600px) {
  .feld-paar {
    grid-template-columns: 1fr 1fr;
  }
}

.einwilligung {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
}

.einwilligung input {
  flex-shrink: 0;
  width: var(--s-6);
  height: var(--s-6);
  margin-block-start: 0.15em;
  accent-color: var(--c-accent-deep);
}

.einwilligung label {
  font-size: var(--t-meta);
}

/* ⚠️ Die Falle ist absichtlich weder `hidden` noch `display: none`: Beides ist
   das Erste, worauf ein Formular-Bot prüft. Aus dem Bild geschoben sieht sie
   für ihn aus wie jedes andere Feld. */
.formular-falle {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.formular-aktion {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4);
}

.formular-meldung {
  border-inline-start: 3px solid var(--c-accent);
  border-radius: 0 var(--r) var(--r) 0;
  background: var(--c-paper);
  padding: var(--s-6);
}

.formular-meldung h3 {
  margin-block-end: var(--s-2);
}

.formular-meldung-fehler {
  border-inline-start-color: var(--c-warn);
}

/* Der Weg OHNE JavaScript.
   Beide Kästen stehen `hidden` im HTML; mit Skript nimmt seite.js das Attribut
   weg. Ohne Skript schickt der Browser das Formular selbst ab, und der
   Formular-Dienst leitet im Fehlerfall auf /kontakt?fehler=1#kontakt-fehler
   zurück. Dann muss der Kasten sichtbar werden — sonst landet jemand auf einer
   Seite, die aussieht, als sei nichts passiert.

   Das `!important` ist hier Pflicht und kein Zufall: Die Basisregel
   `[hidden] { display: none !important }` in 20-basis.css ist selbst
   `!important` (sie muss das eigene `svg { display: block }` schlagen). Ohne
   Gegengewicht bliebe der Kasten verborgen. Der Ausnahmefall ist eng: genau
   ein Element, das der Server ausdrücklich über den Anker angesteuert hat. */
.formular-meldung[hidden]:target {
  display: block !important;
  margin-block-start: var(--s-6);
}

/* ---------------------------------------------------------------------------
   Rechtstexte

   Im Bestand steht der gesamte Text jeweils in EINEM Block ohne Gliederung,
   und drei von fünf Widgets der Seite sind leere Abstandhalter. Vom Impressum
   kommt man nicht zur Datenschutzerklärung und umgekehrt — das Fußmenü fehlt
   auf beiden Seiten vollständig (gemessen: 0 Einträge).
   --------------------------------------------------------------------------- */

.rechtstext {
  max-width: var(--w-text);
}

.rechtstext > * + * {
  margin-block-start: var(--s-4);
}

.rechtstext h2 {
  margin-block-start: var(--s-12);
  font-size: var(--t-h3);
}

.rechtstext h2:first-child {
  margin-block-start: 0;
}

.rechtstext ul {
  padding-inline-start: var(--s-6);
}

.rechtstext li + li {
  margin-block-start: var(--s-2);
}

.rechtstext-stand {
  font-size: var(--t-meta);
  color: var(--c-ink-muted);
}

.rechtstext-block {
  background: var(--c-paper);
  border-radius: var(--r);
  padding: var(--s-6);
}

.rechtstext-quelle {
  font-size: var(--t-meta);
  color: var(--c-ink-muted);
}

/* ---------------------------------------------------------------------------
   Zwei Spalten — Kontaktdaten neben Formular
   --------------------------------------------------------------------------- */

.zweispalt {
  display: grid;
  gap: var(--s-12);
}

@media (min-width: 900px) {
  .zweispalt {
    grid-template-columns: 1fr 1fr;
    gap: var(--s-16);
    align-items: start;
  }
}

/* ---------------------------------------------------------------------------
   Styleguide — nur für die interne Seite /styleguide, nicht für Kundenseiten
   --------------------------------------------------------------------------- */

.sg-probe {
  border: 1px solid var(--c-line);
  border-radius: var(--r);
  padding: var(--s-6);
}

.sg-farbe {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-3);
  border: 1px solid var(--c-line);
  border-radius: var(--r);
}

.sg-farbe-klecks {
  width: 56px;
  height: 56px;
  border-radius: var(--r);
  border: 1px solid var(--c-line);
  flex-shrink: 0;
}

.sg-farbe code {
  font-size: var(--t-meta);
  color: var(--c-ink-muted);
}

.sg-notiz {
  border-inline-start: 3px solid var(--c-accent);
  padding: var(--s-3) var(--s-4);
  background: var(--c-paper);
  font-size: var(--t-meta);
  color: var(--c-ink-muted);
  border-radius: 0 var(--r) var(--r) 0;
}

.sg-notiz p {
  max-width: none;
}

.sg-raster-fein {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}
