/* ============================================================
   HANS BAUR GMBH — RELAUNCH 2026
   base.css — Token, Schrift, Grundelemente, Navigation, Footer
   Grundlage: DESIGN.md. Schwarz/Weiß trägt, Blau gesetzt, Rot Akzent.
   ============================================================ */

/* ---------- Schriften: lokal gehostet, kein Google-Aufruf ---------- */
@font-face {
  font-family: 'Big Shoulders';
  src: url('../fonts/big-shoulders-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Outfit';
  src: url('../fonts/outfit-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Geist Mono';
  src: url('../fonts/geist-mono-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Crimson Pro';
  src: url('../fonts/crimson-pro-italic-var.woff2') format('woff2-variations');
  font-weight: 200 900;
  font-style: italic;
  font-display: swap;
}
/* Bis Crimson Pro geladen ist, steht Georgia da – ungebremst 22 % breiter.
   Der Leitsatz der Leistungsseiten brach dann in zwei Zeilen um und der
   ganze Titel sprang (Google misst das als Layout-Verschiebung).
   Auf die Laufweite von Crimson Pro verkleinert (gemessen 0,82). */
@font-face {
  font-family: 'Crimson Ersatz';
  src: local('Georgia Italic'), local('Georgia-Italic');
  font-style: italic;
  size-adjust: 82%;
}

/* ============================================================
   HELLER GRUND (Aug 2026)

   Die Seite lief auf fast schwarzem Grund. Der Grundton ist jetzt
   warmes Papier — alles andere bleibt, wie es war: dieselbe
   Typografie, dieselben Abstände, dasselbe Blau und Rot als Akzent.

   WARUM DAS MIT SO WENIGEN ZEILEN GEHT:
   Die Token waren nie nach Farbe benannt, sondern nach ROLLE.
   `--paper` hiess nie „weiss", sondern „die lesbare Schrift auf dem
   Grund" (17 der 21 Fundstellen sind `color:`). `--void` hiess nie
   „schwarz", sondern „die Fläche der Seite" (alle 11 Fundstellen sind
   Hintergründe). Beide Rollen tauschen beim Grundwechsel einfach die
   Farbe. Deshalb stehen unten zwei ehrliche Namen, und die alten
   Namen zeigen darauf — so bleiben rund 400 Fundstellen unberührt.

   DIE AUSNAHME, die man von Hand nachziehen muss: Schrift, die auf
   einer BLAUEN, ROTEN oder dunklen Fläche sitzt (Knöpfe, Auswahl,
   Aufklappfelder). Die bleibt hell, sonst stünde Dunkel auf Dunkel.
   Dafür gibt es `--auf-farbe`. Es sind acht Stellen, alle einzeln
   geprüft.

   KONTRASTE GERECHNET, nicht geschätzt, gegen #FAF8F4:
     Tinte #0A0E18 18,18:1 · Blau #0846A8 8,07:1 · Rot #D2201C 4,99:1
   Die alten Varianten für dunklen Grund fallen auf Papier durch
   (--blue-text #5A8EE7 nur 3,07:1, --red-text #E7514E nur 3,48:1) —
   sie zeigen deshalb jetzt auf die vollen Markenfarben.
   ============================================================ */

/* ---------- Token ---------- */
:root {
  /* Die zwei Rollen, ehrlich benannt */
  --grund:      #FAF8F4;   /* Fläche der Seite — warmes Papier */
  --tinte:      #0A0E18;   /* Schrift und Linien darauf (18,18:1) */
  --auf-farbe:  #FAF8F4;   /* Schrift auf blauen/roten/dunklen Flächen */

  /* Flächen — alte Namen, neue Bedeutung */
  --void:       var(--grund);
  --ink:        #FFFFFF;   /* abgesetzte Karten: reines Weiss auf Papier.
                              Der Abstand ist nur 1,06:1 — solche Karten
                              brauchen IMMER Linie oder Schatten dazu. */
  --paper:      var(--tinte);

  /* Identität — unverändert, das ist der Kern der Marke */
  --blue:       #0846A8;   /* Fläche; Schrift darauf --auf-farbe (8,07:1) */
  --blue-lift:  #0B52C4;   /* Linien, Icons, große Schrift (6,56:1) */
  --blue-text:  #0846A8;   /* kleine Schrift auf Papier (8,07:1) */
  --red:        #D2201C;   /* Akzent: Fläche, Rand, Signal */
  --red-text:   #D2201C;   /* kleine rote Schrift auf Papier (4,99:1) */

  /* Linien und Glas — dieselben Deckkräfte, nur dunkle statt heller
     Tinte. Auf Papier trägt schon weniger, darum je eine Stufe feiner. */
  --grey-line:  rgba(10,14,24,0.14);
  --grey-line-2:rgba(10,14,24,0.24);
  --glass:      rgba(10,14,24,0.045);
  --glass-2:    rgba(10,14,24,0.08);

  /* Schrift auf dem Grund */
  /* Papier braucht mehr Tinte als Schwarz Licht braucht: die auf
     Dunkel gültigen Deckkräfte (0,62 / 0,42) ergaben auf Papier nur
     3,15:1 — gemessen an elf Stellen (Fußzeilen-Überschriften, „01 / 04").
     Gerechnet: 0,575 ist das Minimum für 4,5:1. */
  --text:       var(--tinte);
  --text-mute:  rgba(10,14,24,0.70);   /* 7,12:1 */
  --text-dim:   rgba(10,14,24,0.58);   /* 4,65:1 — AA für kleine Schrift */

  /* Abgesetzte Ebene: auf hellem Grund ist das Auffällige das DUNKLE.
     Die Preislisten-Kachel war das einzige Papier auf dunkler Seite;
     jetzt ist sie umgekehrt die einzige dunkle Fläche auf Papier. */
  --doc-bg:     #101624;
  --doc-text:   #FAF8F4;
  --doc-mute:   rgba(250,248,244,0.64);
  --doc-line:   rgba(250,248,244,0.16);

  /* Tinte der Canvas-Effekte — von js/fx/fx-core.js ausgelesen.
     Ohne Klammern als „R,G,B", weil die Module die Deckkraft selbst
     anhängen. Das ist der EINE Schalter für den Grundton der Effekte. */
  --fx-tinte:       10,14,24;
  --fx-tinte-blau:  11,82,196;
  --fx-tinte-rot:   210,32,28;
  /* Fläche der Seite für die gebauten Szenen: ferne Flächen blenden
     dagegen aus (Luftperspektive), der Dunst am Horizont ebenso. */
  --fx-grund:       250,248,244;

  /* Schriftfamilien */
  --f-display:  'Big Shoulders', 'Arial Narrow', sans-serif;
  --f-body:     'Outfit', system-ui, -apple-system, sans-serif;
  --f-mono:     'Geist Mono', ui-monospace, 'SF Mono', monospace;
  --f-accent:   'Crimson Pro', 'Crimson Ersatz', Georgia, serif;

  /* Bewegung — eine Sprache für die ganze Seite */
  --ease:       cubic-bezier(0.16, 1, 0.3, 1);
  --t-hover:    180ms var(--ease);
  --t-layout:   560ms var(--ease);

  /* Maße */
  --wrap:       1280px;
  --gutter:     clamp(20px, 5vw, 64px);
  --nav-h:      72px;
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Die Menüleiste steht fest über der Seite. Ohne diese Zeile
     landet jedes Sprungziel exakt ihre Höhe zu weit oben und
     verschwindet darunter — beim Kontaktformular war das das
     erste Eingabefeld. Am Scrollbehälter gesetzt gilt es für
     alle Sprünge auf einmal: Anker im Quelltext, Links von
     anderen Seiten und scrollIntoView. */
  scroll-padding-top: calc(var(--nav-h, 72px) + 22px);
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  background: var(--void);
  color: var(--text);
  font-family: var(--f-body);
  font-size: 1rem;
  line-height: 1.7;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img, svg, canvas { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
ul { list-style: none; }

::selection { background: var(--red); color: var(--auf-farbe); }

:focus-visible {
  outline: 2px solid var(--blue-text);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ---------- Typografie ---------- */
.display-xl, .display-l, .display-m, h1, h2, h3 {
  font-family: var(--f-display);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -0.015em;
  line-height: 0.9;
  text-wrap: balance;
}

.display-xl {
  /* Untergrenze bewusst niedrig: „Transportbeton" ist das längste Wort und
     muss auch auf 360 px Breite in eine Zeile passen. */
  font-size: clamp(2.3rem, 10.2vw, 10.5rem);
  line-height: 0.86;
  letter-spacing: -0.025em;
}
.display-l {
  font-size: clamp(2.2rem, 6vw, 4.6rem);
  line-height: 0.92;
}
.display-m {
  font-size: clamp(1.3rem, 2.4vw, 1.9rem);
  line-height: 1;
  letter-spacing: 0;
}

.lead {
  font-size: clamp(1.02rem, 1.3vw, 1.15rem);
  line-height: 1.65;
  color: var(--text-mute);
  max-width: 58ch;
  text-wrap: pretty;
}
p { text-wrap: pretty; }

/* Mono — das inhaltliche Bindeglied */
.mono {
  font-family: var(--f-mono);
  font-weight: 700;
  font-size: 0.7rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--blue-text);
}
.mono-data {
  font-family: var(--f-mono);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* Akzent — sparsam, maximal dreimal pro Seite */
.accent {
  font-family: var(--f-accent);
  font-style: italic;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
}

/* ---------- Layout ---------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  position: relative;
  padding-block: clamp(72px, 11vh, 140px);
}

/* Sektionskopf — einheitlich über die ganze Seite */
.sec-head { margin-bottom: clamp(40px, 6vh, 72px); }
.sec-head .mono { display: block; margin-bottom: 18px; }
.sec-head .display-l { margin-bottom: 20px; }
.sec-head.center { text-align: center; }
.sec-head.center .lead { margin-inline: auto; }

/* Haarlinie */
.hairline { height: 1px; background: var(--grey-line); border: 0; }

/* ---------- Buttons ----------
   Blau = Identität (Ruhezustand), Rot = Interaktion (Hover).
   Damit ist Rot konsequent das Signal für „hier passiert etwas". */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 15px 30px;
  font-family: var(--f-mono);
  font-weight: 700;
  font-size: 0.74rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  border: 1px solid transparent;
  transition: background var(--t-hover), border-color var(--t-hover),
              color var(--t-hover), transform var(--t-hover);
}
.btn:hover { transform: translateY(-2px); }

.btn-primary {
  background: var(--blue);
  color: var(--auf-farbe);   /* helle Schrift auf blauer Fläche */
  border-color: var(--blue);
}
.btn-primary:hover {
  background: var(--red);
  border-color: var(--red);
}

.btn-ghost {
  background: transparent;
  color: var(--text);
  border-color: var(--grey-line-2);
}
.btn-ghost:hover {
  border-color: var(--red);
  color: var(--red-text);
}

.btn-arrow::after {
  content: '→';
  font-size: 1em;
  transition: transform var(--t-hover);
}
.btn:hover .btn-arrow::after,
.btn-arrow:hover::after { transform: translateX(4px); }

/* ---------- Skip-Link ---------- */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  padding: 14px 22px;
  background: var(--blue);
  color: var(--auf-farbe);   /* helle Schrift auf blauer Fläche */
  font-family: var(--f-mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.skip-link:focus { left: 12px; top: 12px; }

/* ============================================================
   EBENE 4 — Navigation, fest
   ============================================================ */
.nav {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 100;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  background: rgba(250,248,244,0.62);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  backdrop-filter: blur(16px) saturate(140%);
  border-bottom: 1px solid transparent;
  transition: background var(--t-layout), border-color var(--t-layout);
}
.nav.is-scrolled {
  background: rgba(250,248,244,0.88);
  border-bottom-color: var(--grey-line);
}
.nav .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  /* Blendet mit dem Hero zusammen ein. `backwards` statt `both`:
     nach dem Lauf gilt wieder der normale, sichtbare Zustand —
     die Navigation kann also nicht unsichtbar hängenbleiben. */
  animation: navIn 760ms var(--ease) 80ms backwards;
}
@keyframes navIn {
  from { opacity: 0; transform: translateY(-14px); }
  to   { opacity: 1; transform: none; }
}
/* Die Leiste selbst wird erst beim Scrollen zur Fläche — vorher
   schwebt sie ohne eigenen Grund über der Bühne. */
.nav:not(.is-scrolled) {
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
@media (prefers-reduced-motion: reduce) {
  .nav .wrap { animation: none; }
}

.brand {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: 1.35rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--paper);
  white-space: nowrap;
}
.brand span {
  font-family: var(--f-mono);
  font-weight: 700;
  font-size: 0.6rem;
  letter-spacing: 0.2em;
  color: var(--blue-text);
  margin-left: 6px;
  vertical-align: 0.35em;
}

.nav-list {
  display: flex;
  align-items: center;
  gap: 4px;
}
.nav-list > li > a {
  display: block;
  padding: 9px 15px;
  font-family: var(--f-mono);
  font-weight: 700;
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
  position: relative;
  transition: color var(--t-hover);
}
.nav-list > li > a::after {
  content: '';
  position: absolute;
  left: 15px; right: 15px; bottom: 2px;
  height: 1px;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t-hover);
}
.nav-list > li > a:hover { color: var(--paper); }
.nav-list > li > a:hover::after { transform: scaleX(1); }
.nav-list > li > a.is-active { color: var(--paper); }
.nav-list > li > a.is-active::after { transform: scaleX(1); }

/* Dropdown */
.has-sub { position: relative; }
.sub {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  min-width: 232px;
  padding: 8px;
  background: rgba(255,255,255,0.96);
  -webkit-backdrop-filter: blur(20px);
  box-shadow: 0 18px 40px -18px rgba(10,14,24,0.35);
  backdrop-filter: blur(20px);
  border: 1px solid var(--grey-line);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity var(--t-hover), transform var(--t-hover), visibility var(--t-hover);
}
.has-sub:hover .sub,
.has-sub:focus-within .sub {
  opacity: 1;
  visibility: visible;
  transform: none;
}
.sub a {
  display: block;
  padding: 10px 14px;
  font-size: 0.86rem;
  color: var(--text-mute);
  transition: color var(--t-hover), background var(--t-hover);
}
.sub a:hover { color: var(--paper); background: var(--glass); }

/* Menü-Schalter */
.nav-toggle {
  display: none;
  width: 40px; height: 40px;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  padding: 0 8px;
}
.nav-toggle span {
  display: block;
  height: 1.5px;
  background: var(--paper);
  transition: transform var(--t-hover), opacity var(--t-hover);
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

@media (max-width: 900px) {
  .nav-toggle { display: flex; }
  /* Der Knopf gehört nach ganz rechts oben — er stand aber mittig.
     Grund: .wrap verteilt mit space-between DREI Kinder. Auf dem
     Handy wird .nav-list zu position:fixed, das umgebende <nav>
     behält also Breite 0, zählt als Flex-Element aber weiter mit
     und besetzt den rechten Platz. Gemessen bei 383 px Fenster:
     Knopf 237–277, rechte Inhaltskante 363 — 86 px daneben.
     Also kein space-between mehr, sondern der Knopf schiebt sich
     selbst nach rechts. Das leere <nav> bleibt im Baum, damit die
     Landmarke für Screenreader erhalten bleibt. */
  .nav .wrap { justify-content: flex-start; gap: 0; }
  /* −8 px hebt das eigene Innenmaß des Knopfes auf, damit die drei
     Striche bündig mit der Inhaltskante stehen — dieselbe Kante,
     an der links der Schriftzug beginnt. Die 40 px Tippfläche
     bleiben unverändert. */
  .nav-toggle { margin-left: auto; margin-right: -8px; }
  /* Das aufgeklappte Menüfeld (.nav-list, position: fixed) lag ÜBER
     Schriftzug und Schalter: beide waren verdeckt, und das Menü ließ
     sich nicht wieder schließen. Beide liegen jetzt eine Ebene höher. */
  .brand, .nav-toggle { position: relative; z-index: 2; }
  .nav-list {
    position: fixed;
    /* Von der Oberkante aus schieben, sonst ragt der untere Rand des
       eingeklappten Menüs über die Leiste hinaus ins Bild. */
    inset: 0 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0 var(--gutter) 28px;
    /* Das Feld ist mit allen Unterpunkten rund 730 px hoch. Auf kleinen
       Handys und im Querformat lagen die letzten Punkte unter dem
       Bildschirmrand und waren nicht erreichbar – jetzt wird das Feld
       höchstens bildschirmhoch und lässt sich in sich scrollen. */
    max-height: 100vh;
    max-height: 100dvh;
    overflow-y: auto;
    overscroll-behavior: contain;
    background: rgba(250,248,244,0.98);
    -webkit-backdrop-filter: blur(20px);
    backdrop-filter: blur(20px);
    border-bottom: 1px solid var(--grey-line);
    transform: translateY(-102%);
    transition: transform var(--t-layout);
  }
  /* Kopfstreifen in Höhe der Leiste: bleibt beim Scrollen oben stehen,
     damit die Menüpunkte nicht unter Schriftzug und Schalter durchlaufen.
     Er ersetzt den bisherigen Abstand oben (Leistenhöhe + 12 px). */
  .nav-list::before {
    content: ''; flex: none;
    position: sticky; top: 0; z-index: 1;
    height: var(--nav-h);
    margin: 0 calc(-1 * var(--gutter)) 12px;
    background: var(--grund);
  }
  .nav-list.is-open { transform: none; }
  .nav-list > li > a { padding: 14px 0; font-size: 0.8rem; }
  .nav-list > li > a::after { left: 0; right: auto; width: 26px; }
  .sub {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    background: none;
    border: 0;
    border-left: 1px solid var(--grey-line);
    margin: 0 0 10px 2px;
    padding: 0 0 0 14px;
  }
  .sub a { padding: 8px 0; font-size: 0.82rem; }
}

/* ============================================================
   FOOTER
   ============================================================ */
.foot {
  border-top: 1px solid var(--grey-line);
  padding-block: clamp(56px, 8vh, 90px) 0;
}
.foot-grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1.3fr;
  gap: clamp(28px, 4vw, 56px);
  padding-bottom: 56px;
}
@media (max-width: 900px) { .foot-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .foot-grid { grid-template-columns: 1fr; } }

.foot h4 {
  font-family: var(--f-mono);
  font-weight: 700;
  font-size: 0.66rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 18px;
}
.foot-brand p {
  font-size: 0.9rem;
  color: var(--text-mute);
  margin-top: 14px;
  max-width: 30ch;
}
.foot li { margin-bottom: 9px; }
.foot li a, .foot-contact a, .foot-contact p {
  font-size: 0.9rem;
  color: var(--text-mute);
  transition: color var(--t-hover);
}
.foot li a:hover, .foot-contact a:hover { color: var(--paper); }
.foot-contact p { margin-bottom: 9px; }

.foot-mark {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: clamp(3rem, 13vw, 11.5rem);
  line-height: 0.8;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  text-align: center;
  white-space: nowrap;
  color: transparent;
  /* Kontur war auf 0,20 — bei der Groesse las sich die Wortmarke wie
     ein Versehen statt wie ein Gestaltungsmittel, und die untere
     Haelfte des Footers wirkte dadurch leer. 0,35 macht sie zum
     bewussten Element, ohne dass sie den Links davor Konkurrenz
     macht: sie ist reine Kontur ohne Fuellung und steht in einer
     eigenen Zeile. Deutlich hoeher sollte sie nicht werden, sonst
     zieht sie den Blick vom Kontaktblock weg. */
  -webkit-text-stroke: 1px rgba(10,14,24,0.35);
  user-select: none;
  pointer-events: none;
}
.foot-data {
  font-family: var(--f-mono);
  font-weight: 700;
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  text-align: center;
  color: var(--text-dim);
  margin: 22px 0 28px;
}
.foot-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 20px 0 26px;
  border-top: 1px solid var(--grey-line);
}
.foot-bottom p, .foot-bottom a {
  font-family: var(--f-mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  color: var(--text-dim);
}
.foot-links { display: flex; gap: 22px; }
.foot-bottom a:hover { color: var(--text-mute); }

/* Nur für Screenreader und Suchmaschinen sichtbar */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ============================================================
   SEKTIONS-FLUSS — ein Abschnitt fällt in den nächsten
   `--enter` (0→1 beim Hereinkommen) und `--leave` (0→1 beim
   Hinausgehen) setzt js/site.js laufend beim Scrollen.
   Dadurch ist die Bewegung durchgehend statt schrittweise.
   Grundzustand ohne JS: voll sichtbar, nichts hängt.
   ============================================================ */
/* Jeder Abschnitt ist eine eigene, undurchsichtige Ebene. Er
   überlappt den vorigen, hat eine abgerundete Oberkante und wirft
   nach oben Schatten — dadurch sieht man beim Scrollen, wie eine
   Platte über die andere schiebt. */
[data-flow] {
  --enter: 1;
  --leave: 0;
  position: relative;
  background: var(--void);
  margin-top: -54px;
  padding-top: 54px;
  /* Der nächste Abschnitt schiebt sich 54 px über diesen. Ohne
     denselben Fußraum verschluckt er die letzte Textzeile. */
  padding-bottom: 54px;
  border-radius: 30px 30px 0 0;
  box-shadow: 0 -22px 52px rgba(10,14,24,0.13);
  /* z-index setzt js/site.js aufsteigend, damit später = oben */
}
/* ---------- Das Ereignis an der Kante ----------
   Ein Lichtband ZIEHT beim Eintreten quer über die Oberkante.
   Das ist der sichtbare Moment des Übergangs: hell, breit, und es
   passiert genau dann, wenn der Abschnitt ins Bild kommt. */
[data-flow]::after {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 3px;
  z-index: 3;
  pointer-events: none;
  transform: scaleX(var(--enter));
  transform-origin: 0 50%;
  background: linear-gradient(90deg,
    rgba(46,111,224,0) 0%,
    var(--blue-lift) 18%,
    var(--paper) 52%,
    var(--red) 86%,
    rgba(210,32,28,0) 100%);
  box-shadow: 0 0 26px rgba(46,111,224,0.85), 0 0 60px rgba(46,111,224,0.35);
  /* Wenn der Abschnitt sitzt, verglimmt das Band wieder */
  opacity: calc((1 - var(--leave)) * (1 - var(--enter) * 0.55) + 0.45);
}

/* Lichtschleier: der Abschnitt tritt aus einem Leuchten hervor
   und klart auf, während er sich setzt. */
[data-flow]::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 240px;
  z-index: 2;
  pointer-events: none;
  border-radius: 30px 30px 0 0;
  background: linear-gradient(180deg,
    rgba(46,111,224, calc(0.30 * (1 - var(--enter)))) 0%,
    rgba(46,111,224, calc(0.10 * (1 - var(--enter)))) 38%,
    transparent 100%);
}

/* Der Inhalt kommt spürbar von unten herein und weicht beim
   Hinausgehen deutlich zurück. */
[data-flow] > * {
  opacity: calc(0.05 + 0.95 * var(--enter) - 0.80 * var(--leave));
  transform:
    translate3d(0, calc((1 - var(--enter)) * 90px - var(--leave) * 64px), 0)
    scale(calc(1 - (1 - var(--enter)) * 0.045 - var(--leave) * 0.07));
  transform-origin: 50% 0;
  will-change: transform, opacity;
}

@media (prefers-reduced-motion: reduce) {
  [data-flow] { margin-top: 0; padding-top: 0; padding-bottom: 0; box-shadow: none; }
  [data-flow] > * { opacity: 1; transform: none; }
  [data-flow]::before { opacity: 1; }
}

/* ============================================================
   BEWEGUNG — Einblenden beim Scrollen (kostenlos, kein Canvas)
   ============================================================ */
.js .rise {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity var(--t-layout), transform var(--t-layout);
}
.rise.is-in { opacity: 1; transform: none; }
.rise-2 { transition-delay: 90ms; }
.rise-3 { transition-delay: 180ms; }
.rise-4 { transition-delay: 270ms; }

/* Die Klasse .js davor ist kein Schoenheitsfehler, sondern die
   Absicherung: Ohne JavaScript greift die Regel nicht, und der Inhalt
   ist von Anfang an sichtbar. Gemessen waren 179 Elemente betroffen —
   faellt site.js aus, waeren sie sonst unsichtbar geblieben.

   Zeilenweise gestaffelt */
.js .stagger > * {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--t-layout), transform var(--t-layout);
}
.stagger.is-in > * { opacity: 1; transform: none; }
.stagger.is-in > *:nth-child(2) { transition-delay: 70ms; }
.stagger.is-in > *:nth-child(3) { transition-delay: 140ms; }
.stagger.is-in > *:nth-child(4) { transition-delay: 210ms; }
.stagger.is-in > *:nth-child(5) { transition-delay: 280ms; }
.stagger.is-in > *:nth-child(6) { transition-delay: 350ms; }

/* ---------- Reduzierte Bewegung: Pflicht laut DESIGN.md ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 90ms !important;
  }
  .rise, .stagger > * { opacity: 1 !important; transform: none !important; }
  canvas[data-fx] { display: none !important; }
  [data-fx-fallback] { display: block !important; }
}

/* Fallback-Standbild liegt hinter jedem Canvas und wird nur sichtbar,
   wenn der Canvas fehlt oder abgeschaltet ist. */
[data-fx-fallback] { display: none; }

/* Und es muss die Bühne FÜLLEN, nicht in seiner natürlichen Größe darin
   liegen. Diese Regel stand nur in home.css. Die Leistungsseiten laden
   home.css nicht — dort lag das Standbild bei eingeschalteter Bewegungs-
   reduzierung als 845x315-Kachel in einer 845x1487-Bühne und deckte 21 %
   davon ab; der Rest war leer. Gemessen am 30.08.2026 auf /tiefbau/. */
.hero-fx img[data-fx-fallback] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.35;
}

/* ---------- Ohne backdrop-filter ---------- */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .nav { background: rgba(250,248,244,0.96); }
  .sub { background: #FFFFFF; }
}

/* ============================================================
   RÜCKMELDUNG DER FORMULARE  (js/formular.js)

   Steht in base.css und nicht in kontakt.css: Denselben Kasten
   braucht auch das Bewerbungsformular auf der Jobseite, und die
   lädt leistung.css. Zwei Fassungen desselben Kastens wären zwei
   Stellen zum Vergessen.

   Der Kasten ist von Anfang an im Quelltext (leer und hidden),
   damit aria-live ihn ansagen kann, sobald Text erscheint. Erst
   nachträglich eingefügte Meldungen werden überlesen.
   ============================================================ */
.form-status {
  margin: 16px 0 0;
  padding: 12px 16px;
  border-left: 3px solid var(--grey-line-2);
  background: var(--glass);
  font-size: 0.95rem;
  line-height: 1.45;
}
.form-status.ist-gut     { border-left-color: var(--blue); }
.form-status.ist-schlecht{ border-left-color: var(--red); }

/* ============================================================
   SPAM-FALLE DER FORMULARE

   Ein Feld, das kein Mensch sieht und kein Mensch ausfüllt —
   wer es ausfüllt, ist ein Programm (js/formular.js und
   formular.php werten das aus).

   Steht hier und nicht in kontakt.css, weil beide Formulare der
   Website sie tragen und die Jobseite kontakt.css nicht lädt.
   Genau daran hing ein sichtbarer leerer Kasten neben „Vorname".

   Aus dem Bild geschoben statt display:none: Manche Massenversender
   überspringen Felder, die auf display:none stehen, und dann fängt
   die Falle nichts mehr.
   ============================================================ */
.kt-falle {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

/* ═══════════════════════════════════════════════════════════
   ANRUFLEISTE — nur am Handy

   Die Startseite ist mobil ueber 10.000 Pixel hoch, und keiner
   der sieben Telefonlinks steht im ersten Bild. Wer anrufen
   will, muesste suchen. Diese Leiste haelt die beiden einzigen
   Wege zum Auftrag — anrufen und anfragen — dauerhaft einen
   Daumen entfernt.

   Sie steht im Quelltext jeder Seite und nicht im Skript: ein
   Weg zum Auftrag darf nicht davon abhaengen, dass JavaScript
   laedt. Am Rechner ist sie ausgeblendet, dort liegt die
   Nummer ohnehin in der Kopf- und Fusszeile.
   ═══════════════════════════════════════════════════════════ */
.rufleiste { display: none; }

@media (max-width: 900px) {
  .rufleiste {
    display: grid;
    grid-template-columns: 1fr 1fr;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 90;                      /* unter dem geoeffneten Menue */
    background: var(--doc-bg);
    border-top: 1px solid var(--doc-line);
    /* Auf iPhones mit Streifen am unteren Rand liegt sonst die
       Haelfte des Knopfes unter der Systemgeste. */
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }

  .rufleiste a {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    /* 56 statt der geforderten 44 Pixel — hier tippt jemand mit
       Arbeitshandschuhen auf der Baustelle. */
    min-height: 56px;
    padding: 0 10px;
    font-family: var(--f-mono);
    font-weight: 700;
    font-size: 0.72rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    text-decoration: none;
    color: var(--doc-text);
    background: transparent;
    transition: background var(--t-hover);
  }

  .rufleiste a + a {
    background: var(--blue);
    color: var(--auf-farbe);
  }

  .rufleiste a:active { background: var(--red); color: var(--auf-farbe); }

  .rufleiste svg {
    width: 15px;
    height: 15px;
    flex: 0 0 auto;
  }

  /* Platz schaffen, damit die Leiste nichts verdeckt. */
  body { padding-bottom: calc(56px + env(safe-area-inset-bottom, 0px)); }
}

/* Beim Drucken hat eine Schaltflaeche nichts verloren. */
@media print { .rufleiste { display: none !important; } }


/* ═══════════════════════════════════════════════════════════
   ZURÜCK NACH OBEN

   Lag vorher in home.css und stand deshalb nur auf der
   Startseite — auf den anderen 15 Seiten, die zwischen 7.000
   und 10.000 Pixel lang sind, gab es keinen Weg zurueck.
   Jetzt in base.css, also ueberall.
   ═══════════════════════════════════════════════════════════ */
.to-top {
  position: fixed;
  right: 22px; bottom: 22px;
  z-index: 80;                    /* unter der Anrufleiste (90) */
  width: 44px; height: 44px;
  display: grid;
  place-items: center;
  border: 1px solid var(--grey-line-2);
  background: rgba(250,248,244,0.7);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  color: var(--text-mute);
  cursor: pointer;
  opacity: 0;
  transform: translateY(12px);
  pointer-events: none;
  transition: opacity var(--t-hover), transform var(--t-hover),
              border-color var(--t-hover), color var(--t-hover);
}
.to-top.is-on { opacity: 1; transform: none; pointer-events: auto; }
.to-top:hover { border-color: var(--red); color: var(--red-text); }

@media (max-width: 900px) {
  /* Ueber der Anrufleiste, sonst laege er darunter. */
  .to-top { bottom: calc(56px + env(safe-area-inset-bottom, 0px) + 14px); }
}

@media (prefers-reduced-motion: reduce) {
  .to-top { transition: opacity 0.01ms; transform: none; }
}

@media print { .to-top { display: none !important; } }

/* ═══════════════════════════════════════════════════════════
   BESTELLKNOPF — Transportbeton, Sand und Kies, Blocksteine

   Gleiche Ecke und gleiche Form wie der Knopf nach oben, nur
   rot: Wer bestellen will, soll ihn sehen, ohne ihn zu suchen.
   Er sitzt unten, der Knopf nach oben rueckt darueber — das
   regelt die Geschwisterregel weiter unten ganz ohne Skript.

   Beim Darueberfahren faehrt die Nummer heraus. Im Ruhezustand
   bleibt es ein Symbol, damit er nicht dauerhaft Inhalt
   verdeckt.

   Am Handy erscheint er nicht: dort steht unten schon die
   Anrufleiste, und auf diesen drei Seiten fuehrt ihr linkes
   Feld direkt zur Bestellannahme.
   ═══════════════════════════════════════════════════════════ */
.bestellknopf {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 81;                      /* knapp ueber dem Knopf nach oben */
  display: inline-flex;
  align-items: center;
  height: 52px;
  padding: 0 15px;
  max-width: 52px;                  /* nur das Symbol */
  overflow: hidden;
  gap: 0;
  background: var(--red);
  color: var(--auf-farbe);
  text-decoration: none;
  border: 1px solid var(--red);
  white-space: nowrap;
  box-shadow: 0 10px 24px -12px rgba(10,14,24,0.55);
  transition: max-width var(--t-layout), gap var(--t-hover),
              padding var(--t-hover), background var(--t-hover);
}

.bestellknopf svg { width: 22px; height: 22px; flex: 0 0 auto; }

.bestellknopf-text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  opacity: 0;
  transition: opacity var(--t-hover);
}
.bestellknopf-was {
  font-family: var(--f-mono);
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.8;
}
.bestellknopf-nr {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: 0.01em;
}

.bestellknopf:hover,
.bestellknopf:focus-visible {
  max-width: 320px;
  gap: 13px;
  padding-right: 20px;
  background: var(--blue);
  border-color: var(--blue);
}
.bestellknopf:hover .bestellknopf-text,
.bestellknopf:focus-visible .bestellknopf-text { opacity: 1; }

/* Steht ein Bestellknopf auf der Seite, rueckt der Knopf nach
   oben darueber statt sich mit ihm zu ueberlagern. */
.bestellknopf ~ .to-top { bottom: calc(22px + 52px + 12px); }

@media (max-width: 900px) {
  .bestellknopf { display: none; }
  /* Ohne Bestellknopf gilt wieder der normale Abstand ueber der
     Anrufleiste. */
  .bestellknopf ~ .to-top { bottom: calc(56px + env(safe-area-inset-bottom, 0px) + 14px); }
}

@media (prefers-reduced-motion: reduce) {
  .bestellknopf { transition: background 0.01ms; }
}

@media print { .bestellknopf { display: none !important; } }

/* ---------- Tippflächen am Handy ----------
   Pfadzeile, Herunterladen, Telefon und E-Mail sind nur 16–20 px hoch, zu
   wenig für den Finger. Eine unsichtbare Fläche über dem Link vergrößert
   das Ziel, ohne dass sich etwas verschiebt (Polster täten das). */
@media (pointer: coarse) {
  .lcrumb a,
  .dl-tat,
  main a[href^="tel:"],
  main a[href^="mailto:"] { position: relative; }
  .lcrumb a::before,
  .dl-tat::before,
  main a[href^="tel:"]::before,
  main a[href^="mailto:"]::before { content: ""; position: absolute; inset: -6px 0; }
}
