/* ==========================================================================
   EMSAS — Stile fuer alle Seiten
   Eine Farbfamilie: Weiss und ein tiefes Gruen. Kein Dunkelmodus.
   Farben stehen NUR in dieser Datei, nie im Markup.

   Farbwechsel: nur die sechs Werte im :root-Block unten tauschen.
   Marine-Variante (falls zurueckgewechselt werden soll):
     --ink-rgb: 22 35 61;  --ink-deep-rgb: 14 23 40;  --paper-2-rgb: 244 246 249;
     --muted-rgb: 92 104 128;  --on-ink-muted-rgb: 163 176 199;  --accent-rgb: 46 76 135;
   ========================================================================== */

:root {
  /* Flaechen */
  --paper-rgb:      255 255 255;   /* #FFFFFF  Grundflaeche */
  --paper-2-rgb:    242 246 243;   /* #F2F6F3  leicht gruenlich abgesetzt */
  --ink-rgb:         20  64  47;   /* #14402F  Waldgruen: Text, Flaechen, Knoepfe */
  --ink-deep-rgb:    12  44  32;   /* #0C2C20  tieferes Gruen, NUR Flaechen wie der Footer */
  --schleier-rgb:    17  17  17;   /* #111111  neutrales Dunkel. Liegt auf FOTOS.
                                      Bewusst nicht gruen: sonst bekommt jedes Foto
                                      einen Gruenstich, auch die Haut der Personen. */

  /* Text */
  --muted-rgb:       90 107  98;   /* #5A6B62  auf Weiss, Kontrast 5.7:1 */
  --on-ink-muted-rgb: 168 195 181; /* #A8C3B5  auf Gruen, Kontrast 6.2:1 */

  /* Interaktion */
  --accent-rgb:      30 107  75;   /* #1E6B4B  Links, Hover, Fokus */

  /* Illustrationen. Die EINZIGEN zwei Farben ausserhalb der Gruenfamilie.
     Sie gelten nur fuer Kleidung in Zeichnungen: nie fuer Text, nie fuer
     Knoepfe, nie als Sektionsflaeche. Sonst zerfaellt die Farbfamilie. */
  --ill-1-rgb:      194 112  61;   /* #C2703D  Ocker */
  --ill-2-rgb:      140  58  52;   /* #8C3A34  Ziegelrot */

  --line-a: 0.12;

  --shadow-1: 0 1px 2px rgb(12 44 32 / .05), 0 8px 24px rgb(12 44 32 / .07);
  --shadow-2: 0 2px 4px rgb(12 44 32 / .07), 0 20px 48px rgb(12 44 32 / .12);

  --ease-out: cubic-bezier(.22, 1, .36, 1);
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1);

  --header-h: 72px;
}

/* --------------------------------------------------------------------------
   Vorab-Reset
   WARUM DAS HIER STEHT, obwohl Tailwind dasselbe mitbringt:

   Tailwinds Preflight entsteht erst, wenn cdn.tailwindcss.com geladen UND
   ausgefuehrt ist. Das ist ein Skript, kein Stylesheet. In der Lucke davor
   gibt es kein `img { max-width: 100% }`, und ein <img width="1600"> ist dann
   wirklich 1600 Pixel breit. Auf einem 390 Pixel breiten Handy zoomt der
   Browser darum erst heraus, um die Seite zu zeigen, und springt wieder
   hinein, sobald Tailwind da ist. Genau dieses Zoomen hat Emery am 6.9.2026
   auf dem Handy gemeldet.

   Diese Datei ist ein normales <link>-Stylesheet und damit VOR dem ersten
   Zeichnen da. Der Reset hier schliesst die Luecke. Sobald Tailwind laedt,
   setzt Preflight dieselben Werte noch einmal, das stoert nicht.

   NICHT loeschen, solange Tailwind ueber das CDN kommt.
   -------------------------------------------------------------------------- */
*, ::before, ::after { box-sizing: border-box; }
body { margin: 0; }
h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd { margin: 0; }
ol, ul { list-style: none; margin: 0; padding: 0; }
img, svg, video, canvas, picture { display: block; }
img, video { max-width: 100%; height: auto; }
button, input, select, textarea { font: inherit; color: inherit; margin: 0; }

html { scroll-behavior: smooth; }

body {
  font-family: 'Manrope', sans-serif;
  background: rgb(var(--paper-rgb));
  color: rgb(var(--ink-rgb));
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection { background: rgb(var(--ink-rgb)); color: rgb(var(--paper-rgb)); }

/* --------------------------------------------------------------------------
   Farbige Sektionen
   Alles darin dreht die Farben automatisch um. Deshalb funktionieren
   dieselben Klassen (.muted-text, .btn-primary, .border-hair) auf beiden
   Flaechen, ohne dass im Markup je eine Farbe steht.
   -------------------------------------------------------------------------- */
.section-ink {
  background: rgb(var(--ink-rgb));
  color: rgb(var(--paper-rgb));
}
.section-ink-deep {
  background: rgb(var(--ink-deep-rgb));
  color: rgb(var(--paper-rgb));
}
.section-paper2 { background: rgb(var(--paper-2-rgb)); }

/* --------------------------------------------------------------------------
   Marke — reine Wortmarke, kein Zeichen
   -------------------------------------------------------------------------- */
.wordmark {
  font-family: 'Archivo', sans-serif;
  font-weight: 700;
  letter-spacing: -0.045em;
  line-height: 1;
  display: inline-block;
}

/* --------------------------------------------------------------------------
   Typografie
   -------------------------------------------------------------------------- */
.display-tight { letter-spacing: -0.032em; line-height: 0.98; }
.lead { line-height: 1.7; }

.label {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(var(--muted-rgb));
}
.section-ink .label,
.section-ink-deep .label { color: rgb(var(--on-ink-muted-rgb)); }

.muted-text { color: rgb(var(--muted-rgb)); }
.section-ink .muted-text,
.section-ink-deep .muted-text { color: rgb(var(--on-ink-muted-rgb)); }

/* Haarlinien */
.border-hair { border-color: rgb(var(--ink-rgb) / var(--line-a)); }
.section-ink .border-hair,
.section-ink-deep .border-hair { border-color: rgb(var(--paper-rgb) / 0.16); }

/* --------------------------------------------------------------------------
   Knoepfe — Kanten statt Pillen, das wirkt auf Weiss ruhiger
   -------------------------------------------------------------------------- */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .6rem;
  font-weight: 600;
  border-radius: 8px;
  min-height: 44px;
}
.btn:focus-visible {
  outline: 3px solid rgb(var(--accent-rgb) / .55);
  outline-offset: 3px;
}
.section-ink .btn:focus-visible,
.section-ink-deep .btn:focus-visible {
  outline-color: rgb(var(--paper-rgb) / .7);
}

.btn-primary {
  background: rgb(var(--ink-rgb));
  color: rgb(var(--paper-rgb));
  box-shadow: 0 1px 2px rgb(12 44 32 / .12), 0 8px 20px rgb(12 44 32 / .16);
  transition: transform .22s var(--ease-spring), box-shadow .22s ease, background-color .22s ease;
}
.btn-primary:hover {
  transform: translateY(-2px);
  background: rgb(var(--accent-rgb));
  box-shadow: 0 2px 4px rgb(12 44 32 / .16), 0 14px 30px rgb(12 44 32 / .22);
}
.btn-primary:active { transform: translateY(0); }

.btn-ghost {
  border: 1px solid rgb(var(--ink-rgb) / .22);
  color: rgb(var(--ink-rgb));
  transition: transform .22s var(--ease-spring), background-color .22s ease, border-color .22s ease;
}
.btn-ghost:hover {
  transform: translateY(-2px);
  background: rgb(var(--ink-rgb) / .04);
  border-color: rgb(var(--ink-rgb) / .45);
}
.btn-ghost:active { transform: translateY(0); }

/* Auf farbigem Grund dreht sich beides um */
.section-ink .btn-primary,
.section-ink-deep .btn-primary {
  background: rgb(var(--paper-rgb));
  color: rgb(var(--ink-rgb));
  box-shadow: 0 1px 2px rgb(0 0 0 / .18), 0 10px 24px rgb(0 0 0 / .22);
}
.section-ink .btn-primary:hover,
.section-ink-deep .btn-primary:hover {
  background: rgb(var(--paper-2-rgb));
}
.section-ink .btn-ghost,
.section-ink-deep .btn-ghost {
  border-color: rgb(var(--paper-rgb) / .38);
  color: rgb(var(--paper-rgb));
  backdrop-filter: blur(2px);
}
.section-ink .btn-ghost:hover,
.section-ink-deep .btn-ghost:hover {
  background: rgb(var(--paper-rgb) / .12);
  border-color: rgb(var(--paper-rgb) / .7);
}

/* --------------------------------------------------------------------------
   Navigation
   -------------------------------------------------------------------------- */
.navlink { position: relative; transition: color .2s ease; }
.navlink::after {
  content: '';
  position: absolute; left: 0; bottom: -6px;
  height: 2px; width: 100%;
  background: rgb(var(--ink-rgb));
  transform: scaleX(0); transform-origin: left;
  transition: transform .28s var(--ease-out);
}
.navlink:hover::after,
.navlink:focus-visible::after,
.navlink-active::after { transform: scaleX(1); }
.navlink:focus-visible { outline: none; color: rgb(var(--accent-rgb)); }

/* .tap vergroessert die Trefferflaeche am Handy, die Linie bleibt am Text */
.tap:hover .navlink::after,
.tap:focus-visible .navlink::after { transform: scaleX(1); }
.tap:focus-visible { outline: none; }

.section-ink .navlink::after,
.section-ink-deep .navlink::after { background: rgb(var(--paper-rgb)); }

/* Fliesstext-Links */
.textlink {
  color: rgb(var(--accent-rgb));
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.textlink:hover { text-decoration-thickness: 2px; }
.section-ink .textlink,
.section-ink-deep .textlink { color: rgb(var(--paper-rgb)); }

/* --------------------------------------------------------------------------
   Karten
   -------------------------------------------------------------------------- */
.card {
  background: rgb(var(--paper-rgb));
  border: 1px solid rgb(var(--ink-rgb) / var(--line-a));
  border-radius: 12px;
  box-shadow: var(--shadow-1);
}
.card-quiet {
  background: rgb(var(--paper-2-rgb));
  border: 1px solid rgb(var(--ink-rgb) / .09);
  border-radius: 12px;
}
.card-hover {
  transition: transform .28s var(--ease-out), box-shadow .28s ease, border-color .28s ease;
}
.card-hover:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-2);
  border-color: rgb(var(--ink-rgb) / .28);
}

/* --------------------------------------------------------------------------
   Referenzkachel
   Zeigt eine fertige Kundenseite. Das Bild ist ein Screenshot der echten
   Seite, deshalb 4:3 und OBEN ausgerichtet: bei einer Website ist der Kopf
   das Wichtigste, nicht die Mitte.

   .ref-bild-leer ist der Zustand, solange noch kein Screenshot da ist. Es ist
   bewusst eine gestaltete Flaeche in Markenfarbe und KEIN gestrichelter
   Platzhalterkasten: ein leerer Rahmen mit dem Wort PLATZHALTER sieht aus wie
   ein Fehler, eine gefuellte Flaeche sieht nach Absicht aus.
   -------------------------------------------------------------------------- */
/* Das Seitenverhaeltnis MUSS zu den Screenshots passen, sonst schneidet
   object-fit: cover etwas weg. Aufgenommen wird bei 1440x900, das sind 16:10.
   Vorher stand hier 4:3, dadurch wurden links und rechts Streifen abgesaegt
   und bei beiden Kunden fiel das Logo oben links dem Beschnitt zum Opfer.
   Wer die Aufnahmegroesse aendert, muss diesen Wert mitaendern. */
.ref-bild {
  aspect-ratio: 16 / 10;
  border-radius: 12px;
  overflow: hidden;
  background: rgb(var(--ink-rgb));
}
.ref-bild img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 0;   /* Kopf der Seite, nicht die Mitte */
  display: block;
}
/* Bild der kompletten Kundenseite auf einer Fallseite. Es ist sehr hoch,
   deshalb ein Rahmen mit Schatten: so liest man es als Abbild eines
   Bildschirms und nicht als endlos langes Foto. */
.ref-ganz {
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid rgb(var(--ink-rgb) / .12);
  box-shadow: var(--shadow-2);
  max-width: 860px;
}
.ref-ganz img { width: 100%; height: auto; display: block; }

.ref-bild-leer {
  display: grid;
  place-items: center;
  padding: 1.5rem;
  text-align: center;
  color: rgb(var(--on-ink-muted-rgb));
}

/* Beim Ueberfahren zoomt NUR das Bild, sonst nichts.
   Bis 6.9.2026 trug die Kachel zusaetzlich .card-hover. Das war ein Fehler:
   .card-hover setzt Schatten und Rahmenfarbe, die Kachel hat aber weder
   Hintergrund noch Rahmen. Sichtbar wurde davon nur der Schatten, also ein
   blasses Rechteck, das beim Ueberfahren um Bild UND Text herum aufleuchtete
   und heller wirkte als das Papier dahinter. Wer eine Referenz anschaut, soll
   die Arbeit sehen und nicht den Kasten darum.
   Die Bewegung sitzt auf dem Bild, der Ausschnitt haelt sie fest. */
.ref-karte:hover .ref-bild img { transform: scale(1.03); }
.ref-bild img { transition: transform .4s var(--ease-out); }
@media (prefers-reduced-motion: reduce) {
  .ref-bild img { transition: none; }
  .ref-karte:hover .ref-bild img { transform: none; }
}

/* --------------------------------------------------------------------------
   Leistungsblock
   EIN Muster fuer alle Leistungen auf leistungen.html. Frueher hatte dort
   jeder Abschnitt sein eigenes Layout, dadurch fand das Auge kein Muster und
   die Seite wirkte unsortiert. Jetzt sieht jeder Block gleich aus, nur die
   Seiten tauschen.

   Am Handy steht der Text IMMER zuerst, egal ob gedreht: die Reihenfolge im
   HTML ist Text, dann Bild. Erst ab Desktop greift die Spaltenzuweisung, und
   nur dort kann .ist-gedreht die Seiten tauschen. So bleibt die Drehung eine
   reine Frage der Darstellung und nicht der Dokumentstruktur.
   -------------------------------------------------------------------------- */
.leistung-block { display: grid; gap: 2.5rem; align-items: center; }

@media (min-width: 1024px) {
  .leistung-block { grid-template-columns: 1fr 1fr; gap: 4.5rem; }
  .leistung-block > .leistung-text { grid-column: 1; grid-row: 1; }
  .leistung-block > .leistung-bild { grid-column: 2; grid-row: 1; }
  .leistung-block.ist-gedreht > .leistung-text { grid-column: 2; }
  .leistung-block.ist-gedreht > .leistung-bild { grid-column: 1; }
}

/* Die Nummer traegt den Block optisch. Sie ist Schmuck und wird deshalb
   nicht vorgelesen, die Reihenfolge steht schon in der Ueberschriftenfolge. */
.leistung-nr {
  font-family: Archivo, Helvetica, Arial, sans-serif;   /* wie .font-display in tailwind.config.js */
  font-weight: 700;
  font-size: 3.2rem;
  line-height: 1;
  letter-spacing: -0.04em;
  color: rgb(var(--ink-rgb) / .18);
  margin-bottom: .75rem;
}

/* Platz fuer die Zeichnung. Solange keine da ist, steht hier eine ruhige
   Flaeche statt eines leeren Rahmens. */
.leistung-bild {
  aspect-ratio: 4 / 3;
  border-radius: 12px;
  overflow: hidden;
  background: rgb(var(--paper-2-rgb));
  border: 1px solid rgb(var(--ink-rgb) / .09);
}
.leistung-bild img { width: 100%; height: 100%; object-fit: contain; display: block; }
.leistung-bild-leer {
  display: grid;
  place-items: center;
  padding: 1.5rem;
  text-align: center;
  color: rgb(var(--muted-rgb));
}
/* Auf hellgruenem Grund muss die Flaeche andersherum abgesetzt werden,
   sonst verschwindet sie im Hintergrund. */
.section-paper2 .leistung-bild { background: rgb(var(--paper-rgb)); }

/* --------------------------------------------------------------------------
   Fotobaender
   Grosse Bildflaechen, die den Fliesstext unterbrechen. Eingefuehrt am
   30.8.2026: die Startseite hatte nach der Animation fuenf Bildschirme Text
   und genau EIN Foto, und niemand liest das. Die Baender sind deshalb kein
   Schmuck, sondern der Rhythmus der Seite.
   -------------------------------------------------------------------------- */
.foto {
  position: relative;
  overflow: hidden;
  background: rgb(var(--paper-2-rgb));
}
.foto > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Der Ausschnitt muss sich zwischen quer und hochkant unterscheiden: beim
     breiten Band wird oben und unten weggeschnitten, beim hochkanten links
     und rechts. Deshalb zwei Werte, gesetzt am Bild selbst. */
  object-position: var(--pos, 50% 50%);
}
@media (max-width: 639px) {
  .foto > img { object-position: var(--pos-handy, var(--pos, 50% 50%)); }
}

/* Volle Fensterbreite. AM HANDY HOCHKANT, das ist der Punkt: ein Querfoto auf
   einem hochkant gehaltenen Telefon laesst die Person winzig in der Mitte
   stehen, oben und unten bleibt leere Flaeche. Zwei Varianten, weil es davon
   abhaengt, wie viel Breite das Motiv braucht:
     .foto-band-hoch   eine Person   -> 3:4, sie steht gross im Bild
     .foto-band-breit  zwei Personen -> 1:1, enger geht nicht, sonst faellt
                                        eine Person aus dem Ausschnitt
   Ab 640px sind beide gleich, dort ist die Breite kein Problem mehr.
   Die Seitenverhaeltnisse sind bewusst gestuft, nicht fluid. */
.foto-band-hoch  { aspect-ratio: 3 / 4; }
.foto-band-breit { aspect-ratio: 1 / 1; }
@media (min-width: 640px) {
  .foto-band-hoch,
  .foto-band-breit { aspect-ratio: 16 / 9; }
}
@media (min-width: 1024px) {
  .foto-band-hoch,
  .foto-band-breit { aspect-ratio: 2.4 / 1; }
}

/* Bild in einer Spalte neben Text */
.foto-halb { aspect-ratio: 3 / 4; border-radius: 12px; }
@media (min-width: 640px)  { .foto-halb { aspect-ratio: 4 / 3; } }
@media (min-width: 1024px) { .foto-halb { aspect-ratio: 5 / 4; } }

.foto-hoch { aspect-ratio: 4 / 5; border-radius: 12px; }

/* Schleier, damit heller Text auf dem Foto lesbar bleibt. Gilt fuer JEDES
   Foto, auch ein helles, deshalb nicht schwaecher stellen.
   Farbe ist --schleier-rgb, also neutral und NICHT gruen. Sonst kippt jedes
   Foto in einen Gruenstich, am sichtbarsten in Gesichtern. */
.foto-schleier::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
              rgb(var(--schleier-rgb) / .74) 0%,
              rgb(var(--schleier-rgb) / .34) 40%,
              rgb(var(--schleier-rgb) / 0) 72%);
}
.foto-text {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 1;
  color: rgb(var(--paper-rgb));
}
/* .label und .muted-text drehen sonst nur in .section-ink um */
.foto-text .label   { color: rgb(var(--on-ink-muted-rgb)); }
.foto-text .muted-text { color: rgb(var(--on-ink-muted-rgb)); }

/* Platzhalter, solange ein Foto noch fehlt. Faellt bewusst auf. */
.foto-leer {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed rgb(var(--ink-rgb) / .3);
  border-radius: 12px;
  background: rgb(var(--paper-2-rgb));
  color: rgb(var(--muted-rgb));
  text-align: center;
  padding: 2rem;
}

/* --------------------------------------------------------------------------
   Parallax-Band
   Ein Foto liegt HINTER einem Abschnitt. Beim Scrollen wandert es langsamer
   als die Seite, dadurch wirkt es wie hinter einem Fenster.

   Aufbau, drei Ebenen uebereinander:
     .parallax-bild      das Foto, absichtlich um --reise hoeher als das Band,
                         damit es Weg zum Wandern hat und oben oder unten nie
                         eine Kante auftaucht
     .parallax-schleier  neutrales Dunkel, damit die weisse Schrift lesbar ist
     .parallax-inhalt    Ueberschrift, Text, Knopf

   Bewegt wird ausschliesslich transform, also nichts, was ein Neuzeichnen
   der Seite ausloest.

   OHNE JS und bei reduzierter Bewegung: das Bild steht still und ist sofort
   sichtbar. Der Abschnitt sieht dann normal aus, er bewegt sich nur nicht.
   -------------------------------------------------------------------------- */
/* IMMER zusammen mit .section-ink verwenden:
     <section class="section-ink parallax-band">
   Von dort kommen Textfarbe und alle Knopf-Varianten fuer dunkle Flaechen
   (weisser .btn-primary, sichtbarer .btn-ghost). Von dort kommt auch die
   gruene Flaeche, die als Rueckfall stehen bleibt, falls das Foto nicht
   laedt. Deshalb setzt .parallax-band selbst KEINE Farbe. */
.parallax-band {
  --reise: 120px;
  position: relative;
  overflow: hidden;
}

.parallax-bild {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--reise) / -2);
  height: calc(100% + var(--reise));
  z-index: 0;
  transform: translate3d(0, var(--py, 0px), 0);
}
.parallax-bild picture { display: block; height: 100%; }
.parallax-bild img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;   /* zeigt Muenstertuerme plus ruhigen Himmel darueber */
  display: block;
}

/* Auftauchen: erst wenn das Band ins Bild kommt, wird das Foto eingeblendet.
   Startwert ist 1, damit ohne JS sofort etwas dasteht. Erst .hat-js schaltet
   auf 0 zurueck, sonst bliebe die Flaeche bei einem Skriptfehler leer. */
.hat-js .parallax-bild { opacity: 0; transition: opacity 1.1s var(--ease-out); }
.hat-js .parallax-band.is-sichtbar .parallax-bild { opacity: 1; }

.parallax-schleier {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* Zwei Lagen: eine gleichmaessige von oben nach unten, und eine weiche
     Ellipse genau dort, wo Ueberschrift und Absatz stehen. Ohne die Ellipse
     laeuft der helle Absatztext ueber die Hausfassaden und wird unlesbar. */
  background:
    radial-gradient(ellipse 62% 68% at 50% 48%,
      rgb(var(--schleier-rgb) / .40) 0%,
      rgb(var(--schleier-rgb) / 0) 78%),
    linear-gradient(to bottom,
      rgb(var(--schleier-rgb) / .70) 0%,
      rgb(var(--schleier-rgb) / .56) 45%,
      rgb(var(--schleier-rgb) / .80) 100%);
}

.parallax-inhalt { position: relative; z-index: 2; }

/* Der Knopf dreht auf dem Foto ZURUECK auf Gruen.
   .section-ink macht ihn sonst weiss, was auf einer gruenen Flaeche richtig
   ist, auf diesem Foto aber nicht: die weisse Flaeche liegt teils vor hellem
   Himmel und hellen Fassaden und verliert dort ihre Kante. Gruen setzt sich
   ab und ist ausserdem dieselbe Farbe wie derselbe Knopf im Seitenkopf.
   Der Schatten bleibt kraeftig, damit der Knopf vom Bild abhebt. */
.parallax-band .btn-primary {
  background: rgb(var(--ink-rgb));
  color: rgb(var(--paper-rgb));
  box-shadow: 0 2px 6px rgb(0 0 0 / .35), 0 12px 28px rgb(0 0 0 / .30);
}
.parallax-band .btn-primary:hover {
  background: rgb(var(--accent-rgb));
}

/* Text auf dem Foto braucht MEHR Kontrast als auf der gruenen Flaeche.
   Das gedaempfte Gruen (--on-ink-muted-rgb) reicht hier nicht: es liegt teils
   ueber hellen Hausfassaden und verschwindet darin. Deshalb fast weiss. */
.parallax-band .muted-text { color: rgb(255 255 255 / .90); }

@media (prefers-reduced-motion: reduce) {
  .parallax-bild { transform: none; }
}

/* --------------------------------------------------------------------------
   Hero ueber die ganze Seite
   Bild fuellt den Bereich, der Text liegt als Vordergrund darauf.
   Der Schleier sorgt dafuer, dass der Text auf JEDEM Foto lesbar bleibt,
   auch auf einem hellen. Deshalb nicht schwaecher stellen.
   -------------------------------------------------------------------------- */
.hero-full {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  min-height: calc(100vh - var(--header-h));
  min-height: calc(100svh - var(--header-h));
  background: rgb(var(--schleier-rgb));
}

/* Ab Tablet folgt die Hoehe dem Bild (16:9), damit oben und unten nichts
   weggeschnitten wird. Nur wenn das Fenster flacher ist, wird begrenzt. */
@media (min-width: 768px) {
  .hero-full {
    min-height: 0;
    height: min(calc(100svh - var(--header-h)), 56.25vw);
  }
}

.slider-media { position: absolute; inset: 0; }
.slider-media img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Der Schleier liegt bewusst NUR dort, wo der Text steht: unten und links.
   Oben rechts bleibt das Foto unberührt, sonst wirkt jedes Bild grün überzogen. */
.hero-scrim {
  position: absolute; inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to top,
      rgb(var(--schleier-rgb) / .86) 0%,
      rgb(var(--schleier-rgb) / .58) 24%,
      rgb(var(--schleier-rgb) / .18) 52%,
      transparent 74%),
    linear-gradient(to right,
      rgb(var(--schleier-rgb) / .52) 0%,
      rgb(var(--schleier-rgb) / .14) 40%,
      transparent 62%);
}

/* Am Handy liegt der Text NICHT auf dem Foto, sondern darunter.
   Ein Querformat wird auf einem hochkant-Bildschirm sonst zu einem schmalen
   Streifen, und die Schrift landet zwangsläufig im Gesicht.
   Muss NACH .slider-media und .hero-scrim stehen, sonst greift es nicht. */
@media (max-width: 767px) {
  .hero-full { display: block; min-height: 0; }
  .slider-media { position: relative; inset: auto; aspect-ratio: 4 / 3; }
  .hero-scrim {
    background: linear-gradient(to top, rgb(var(--schleier-rgb) / .4), transparent 55%);
  }
}

/* --------------------------------------------------------------------------
   Bilder-Wechsler
   Ueberblendung laeuft nur ueber opacity, damit nichts ruckelt.
   -------------------------------------------------------------------------- */
.slide {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity .9s var(--ease-out);
  pointer-events: none;
}
.slide.is-active { opacity: 1; pointer-events: auto; }

/* Der Punkt ist klein, die Trefferflaeche 44px. Deshalb Button plus Innenpunkt. */
.dot {
  display: grid; place-items: center;
  width: 44px; height: 44px;
  background: none; border: 0; cursor: pointer;
}
.dot::before {
  content: '';
  width: 9px; height: 9px;
  border-radius: 999px;
  border: 1.5px solid rgb(var(--ink-rgb) / .38);
  background: transparent;
  transition: background-color .25s ease, border-color .25s ease, transform .25s var(--ease-spring);
}
.dot:hover::before { border-color: rgb(var(--ink-rgb) / .7); transform: scale(1.2); }
.dot[aria-current="true"]::before {
  background: rgb(var(--ink-rgb));
  border-color: rgb(var(--ink-rgb));
  transform: scale(1.2);
}
.dot:focus-visible { outline: 3px solid rgb(var(--accent-rgb) / .55); outline-offset: -6px; border-radius: 999px; }

/* Pfeile links und rechts im Bild.
   Zurueckhaltend: nur die duenne Spitze, keine Flaeche im Ruhezustand.
   Erst beim Zeigen legt sich eine dunkle Scheibe darunter. Trefferflaeche 48px. */
.slider-nav {
  position: absolute;
  top: 50%;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 48px; height: 48px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  color: rgb(var(--paper-rgb) / .82);
  background: rgb(var(--schleier-rgb) / 0);
  transform: translateY(-50%);
  transition: color .25s ease, background-color .25s ease, opacity .25s var(--ease-out);
}

/* Gleiche Aufgabe wie der Schleier beim Text: der Pfeil muss auch auf einem
   hellen Foto sichtbar bleiben, ohne dass eine Flaeche darunter liegt. */
.slider-nav svg { filter: drop-shadow(0 1px 3px rgb(var(--schleier-rgb) / .6)); }
.slider-nav-prev { left: .5rem; }
.slider-nav-next { right: .5rem; }

.slider-nav:hover {
  color: rgb(var(--paper-rgb));
  background: rgb(var(--schleier-rgb) / .42);
}
.slider-nav:active { color: rgb(var(--paper-rgb)); opacity: .75; }
.slider-nav:focus-visible {
  outline: 3px solid rgb(var(--paper-rgb) / .8);
  outline-offset: 2px;
  color: rgb(var(--paper-rgb));
}

@media (min-width: 768px) {
  .slider-nav-prev { left: 1rem; }
  .slider-nav-next { right: 1rem; }
}

/* Punkte auf dem Foto */
.section-ink .dot::before { border-color: rgb(var(--paper-rgb) / .6); }
.section-ink .dot:hover::before { border-color: rgb(var(--paper-rgb)); }
.section-ink .dot[aria-current="true"]::before {
  background: rgb(var(--paper-rgb));
  border-color: rgb(var(--paper-rgb));
}
.section-ink .dot:focus-visible { outline-color: rgb(var(--paper-rgb) / .8); }

/* --------------------------------------------------------------------------
   Reveal beim Scrollen — nur opacity und transform
   -------------------------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .7s var(--ease-out), transform .7s var(--ease-out);
}
.reveal.visible { opacity: 1; transform: none; }
.d1 { transition-delay: .08s }
.d2 { transition-delay: .16s }
.d3 { transition-delay: .24s }
.d4 { transition-delay: .32s }

/* --------------------------------------------------------------------------
   Formularfelder
   -------------------------------------------------------------------------- */
.field {
  width: 100%;
  background: rgb(var(--paper-rgb));
  border: 1px solid rgb(var(--ink-rgb) / .18);
  border-radius: 8px;
  padding: .8rem 1rem;
  color: rgb(var(--ink-rgb));
  font-family: inherit;
  font-size: 1rem;
  min-height: 48px;
  transition: border-color .2s ease, box-shadow .2s ease;
}
.field::placeholder { color: rgb(var(--muted-rgb) / .75); }
.field:focus {
  outline: none;
  border-color: rgb(var(--accent-rgb));
  box-shadow: 0 0 0 3px rgb(var(--accent-rgb) / .16);
}
select.field { appearance: none; background-image: none; }

.field-hint { font-size: .8rem; color: rgb(var(--muted-rgb)); margin-top: .4rem; line-height: 1.5; }

/* --------------------------------------------------------------------------
   FAQ (leistungen.html)
   -------------------------------------------------------------------------- */
.faq-plus { transition: transform .3s var(--ease-out); }
details[open] .faq-plus { transform: rotate(45deg); }
summary::-webkit-details-marker { display: none; }
summary { list-style: none; cursor: pointer; }
summary:focus-visible { outline: 3px solid rgb(var(--accent-rgb) / .55); outline-offset: 4px; border-radius: 6px; }

/* --------------------------------------------------------------------------
   Illustrationen
   Flache Vektorzeichnung, kein Foto. Verbindliche Regeln:
   Kontur in Ink, gleichbleibende Strichstaerke, runde Enden und Ecken.
   Haut und Haende sind weiss, nie eine Hautfarbe. Jedes Kleidungsstueck
   bekommt GENAU eine Volltonfarbe, keine Verlaeufe, keine Schattierung.
   Koepfe haben KEIN Gesicht: keine Augen, kein Mund, keine Nase.

   Farben stehen auch hier nie im Markup. Im SVG nur diese Klassen setzen.
   -------------------------------------------------------------------------- */
.ill {
  stroke: rgb(var(--ink-rgb));
  fill: none;
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ill-paper { fill: rgb(var(--paper-rgb)); }   /* Kopf, Haende, Radflaechen */
.ill-ink   { fill: rgb(var(--ink-rgb)); }     /* Haare, Schuhe, dunkle Kleider */
.ill-1     { fill: rgb(var(--ill-1-rgb)); }   /* Ocker */
.ill-2     { fill: rgb(var(--ill-2-rgb)); }   /* Ziegelrot */
.ill-none  { stroke: none; }                  /* Flaechen ohne eigene Kontur */

/* Arme und Beine sind KEINE Vielecke, sondern zwei uebereinanderliegende
   Linien: erst die dicke Kontur in Ink, darueber die duennere Fuellfarbe.
   Das gibt runde Gelenke und einen fliessenden Strich, den ein Polygon
   nie hinbekommt. Immer paarweise verwenden, Kontur zuerst. */
.ill-leg-out { stroke-width: 27; }
.ill-leg-in  { stroke-width: 20; }
.ill-arm-out { stroke-width: 20; }
.ill-arm-in  { stroke-width: 13; }

.ill-s-ink   { stroke: rgb(var(--ink-rgb)); }
.ill-s-paper { stroke: rgb(var(--paper-rgb)); }
.ill-s1      { stroke: rgb(var(--ill-1-rgb)); }
.ill-s2      { stroke: rgb(var(--ill-2-rgb)); }

/* Tiefe entsteht ueber die Strichstaerke, nicht ueber Graustufen:
   je weiter hinten, desto duenner und blasser. Die Figur vorne traegt
   die volle Kontur und die dunklen Flaechen, deshalb liest man sie zuerst. */
.ill-net  { stroke: rgb(var(--ink-rgb) / .28); stroke-width: 1.4; }
.ill-far  { stroke: rgb(var(--ink-rgb) / .5);  stroke-width: 2; }
.ill-bike { stroke-width: 3; }

.ill-net-dot { fill: rgb(var(--ink-rgb) / .45); stroke: none; }

/* --------------------------------------------------------------------------
   Barrierefreiheit
   -------------------------------------------------------------------------- */
.skip-link {
  position: absolute; left: -9999px; top: 0;
  z-index: 100;
  background: rgb(var(--ink-rgb)); color: rgb(var(--paper-rgb));
  padding: .8rem 1.2rem; border-radius: 0 0 8px 0;
}
.skip-link:focus { left: 0; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  .slide { transition: none; }
  * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}

/* --------------------------------------------------------------------------
   Hero-Scroll-Geschichte

   Aufbau: eine hohe Bahn (.story-track), darin klebt die Buehne
   (.story-stage) fest, waehrend man an ihr vorbeiscrollt. main.js rechnet
   den Scrollweg in --s um, einen Wert von 0 bis 3, und setzt daraus fuer
   jedes Element ein eigenes --o (Deckkraft) und --y (Versatz).

   Bewegt werden ausschliesslich opacity und transform, nichts anderes.

   WICHTIG: ohne die Klasse .is-on ist das hier die Schluss-Szene, also Bild 3
   plus Motto, ganz ohne Bewegung. Startet das Skript nicht, sieht der
   Besucher trotzdem eine fertige Seite. Erst JS setzt .is-on.
   -------------------------------------------------------------------------- */
.story-track { position: relative; background: rgb(var(--paper-rgb)); }

.story-stage {
  position: relative;
  overflow: hidden;
  min-height: calc(100vh - var(--header-h));
  min-height: calc(100svh - var(--header-h));
}

/* Die Bilder liegen deckungsgleich uebereinander und sitzen UNTEN auf.
   Der graue Streifen im Bild trifft so die Unterkante des Bildschirms,
   und der weisse Himmel oben geht nahtlos in die Seite ueber. */
.story-figure {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overflow: hidden;
}
/* Kein object-fit, das braucht es hier nicht und es wuerde nur stoeren:
   Das Bild ist immer volle Breite und sitzt unten auf. Ist die Buehne
   niedriger als das Bild, laeuft oben der leere Himmel ueber und wird
   abgeschnitten. Ist sie hoeher, bleibt oben Weiss, das nahtlos in die
   weisse Seite uebergeht. So beruehrt der graue Streifen in jedem
   Fensterformat alle drei Kanten. */
.story-figure img {
  width: 100%;
  height: auto;
  display: block;
}
.story-figure[data-scene="1"] { z-index: 1; }
.story-figure[data-scene="2"] { z-index: 2; }
.story-figure[data-scene="3"] { z-index: 3; }

/* Text liegt oben im Bild, dort ist bei allen drei Szenen leerer Himmel */
.story-copy {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding-top: clamp(2rem, 7vh, 5rem);
  pointer-events: none;
}

.story-line,
.story-outro {
  grid-area: 1 / 1;
  max-width: 34rem;
}
/* Alle Textbausteine liegen aufeinander, damit keiner den anderen schiebt */
.story-copy > * { position: absolute; left: 1.5rem; right: 1.5rem; top: clamp(2rem, 7vh, 5rem); }
@media (min-width: 1024px) { .story-copy > * { left: 2.5rem; right: 2.5rem; } }

.story-line {
  font-family: 'Archivo', sans-serif;
  font-weight: 700;
  letter-spacing: -0.032em;
  line-height: 1.08;
  font-size: clamp(1.7rem, 4.6vw, 3.1rem);
  max-width: 22ch;
  color: rgb(var(--ink-rgb));
}

/* Kein ch hier: die Einheit rechnet mit der geerbten 16px-Schrift, waehrend
   die Wortmarke darin bis 83px gross wird. Der Kasten waere zu schmal und
   das clip-path wuerde das letzte S abschneiden. */
.story-outro { max-width: 34rem; }
.story-mark {
  display: block;
  font-size: clamp(2.6rem, 8vw, 5.2rem);
  line-height: 1;
  color: rgb(var(--ink-rgb));
}
.story-motto {
  margin-top: .6rem;
  font-family: 'Manrope', sans-serif;
  font-weight: 500;
  font-size: clamp(1.05rem, 2.4vw, 1.5rem);
  letter-spacing: 0;
  color: rgb(var(--muted-rgb));
}

/* Video und Rueckfallbild liegen deckungsgleich uebereinander und sitzen unten
   auf. Sie stecken seit 30.8.2026 in .story-bild.

   Warum der Container: vorher war das Video schlicht 100% breit. Auf einem
   breiten flachen Fenster (etwa 1920x800) wurde es dadurch hoeher als die
   Buehne, und der Ueberschuss oben fiel weg. Genau dort stehen aber Turm und
   Turmspitzen, die waren dann angeschnitten. Jetzt begrenzt max-width die
   Breite so, dass die volle Hoehe immer hineinpasst: 1.7493 ist das
   Seitenverhaeltnis der Clips (1270:726).

   Damit bleibt auf einem breiten flachen Fenster links und rechts Platz frei.
   Die Zeichnung steht dort als Flaeche in der Mitte. Das ist bewusst so: der
   frueher hier verlaengerte Strassenstreifen hat mehr kaputt gemacht als er
   half, siehe die Notiz weiter unten. */
.story-bild {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 100%;
  max-width: min(calc((100svh - var(--header-h)) * 1.7493), 1780px);
}
/* Die 1780px sind eine Schaerfe-Grenze, keine Design-Grenze: die Clips sind
   1270 breit, mehr als das 1.4-fache hochskaliert wird eine Strichzeichnung
   sichtbar weich. Auf einem 27-Zoll-Schirm steht der Hero dann etwas schmaler
   in der Mitte.

   HIER STAND BIS 30.8.2026 ein ::before, das den Strassenstreifen ueber die
   volle Fensterbreite verlaengert hat. Es ist ersatzlos entfernt und darf
   NICHT zurueckkommen: die Strasse steht im Video nicht still. Die Kamera
   zoomt und faehrt, damit wandert die Strasse im Bild auf und ab. Ein fest
   positionierter Streifen kann deshalb hoechstens in einem einzigen Frame
   passen. Gemessen auf 1900x1000: der Streifen sass rund 130 Pixel zu hoch,
   sichtbar als zwei graue Bloecke links und rechts, die sich nicht bewegten.
   Die Prozentwerte stammten aus hero-ende.webp, also aus dem LETZTEN Frame,
   und galten damit fuer keinen anderen. */
.story-fallback {
  position: relative;
  z-index: 1;
  width: 100%;
  height: auto;
  display: block;
}
.story-video {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  display: block;
}

/* --- Ruhezustand ohne JS: das Bild der Schluss-Szene und das Motto -------- */
.story-video,
.story-line { opacity: 0; }
.story-fallback,
.story-outro { opacity: 1; }

/* --- Mit JS: die Bahn wird hoch, die Buehne klebt, JS spult das Video ----- */
.story-track.is-on .story-video { opacity: 1; }
.story-track.is-on .story-fallback { opacity: 0; }

/* Fuenf Akte, 24.21 Sekunden Video. Der Scrollweg ist bewusst proportional zur
   Laufzeit: frueher 420svh auf 20.17s, das sind 20.8svh je Sekunde. Bei 24.21s
   ergibt das 505svh, und das Scrolltempo bleibt gleich wie vorher. */
.story-track.is-on { height: 505svh; }
.story-track.is-on .story-stage {
  position: sticky;
  top: var(--header-h);
  height: calc(100svh - var(--header-h));
  min-height: 0;
}
/* Die frueheren drei Standbilder sind am 29.8.2026 durch EIN Video ersetzt
   worden, das der Scroll durchspult. Damit gibt es keinen Szenenwechsel mehr
   zu kaschieren: die Bewegung laeuft durchgehend. */

/* Der Text wird von links nach rechts aufgedeckt und wandert dabei leicht
   mit. Das Aufdecken macht clip-path, bewusst als Ausnahme von der
   Hausregel "nur transform und opacity": ein reiner Versatz waere ein
   Verschieben, kein Aufdecken. Nur drei kurze Zeilen, das traegt. */
.story-track.is-on .story-line,
.story-track.is-on .story-outro {
  opacity: var(--o, 0);
  transform: translate3d(var(--x, 0), 0, 0);
  clip-path: inset(0 calc((1 - var(--rv, 0)) * 100%) 0 0);
  will-change: opacity, transform, clip-path;
}

/* HOCHKANT gehaltenes Geraet, Telefon wie Tablet.
   Hier laeuft eine eigene HOCHFORMAT-Fassung (3:4), gebaut aus dem gleichen
   Material mit tools/hochformat-bauen.py. Vorher lief das Quer-Video auf 120
   Prozent Breite gezogen und fuellte damit nur 35 Prozent der Buehne, der Rest
   war leere weisse Flaeche.

   Groesser ziehen ist NICHT die Loesung und war es nie: in der Sprungszene
   steht das Handy ganz links und das Velo ganz rechts, ab etwa 130 Prozent
   faellt eines von beiden aus dem Bild. Deshalb faehrt die Kamera im Video
   selbst mit und zoomt genau an diesen zwei Stellen heraus.

   Die Grenze ist 1023px und NICHT 767px: ein hochkant gehaltenes Tablet ist
   768 breit und fiel damit auf die Desktop-Regeln, wo das Quer-Video nur 46
   Prozent der Buehne fuellte. Die Bedingung `orientation: portrait` ist
   ebenso wichtig, ohne sie bekaeme ein quer gedrehtes Telefon die 3:4-Box mit
   dem Quer-Video darin und das Bild waere zerrissen. */
@media (max-width: 1023px) and (orientation: portrait) {
  .story-track.is-on { height: 456svh; }

  .story-bild {
    max-width: none;
    width: 100%;
  }
  .story-video,
  .story-fallback {
    width: 100%;
    max-width: none;
    left: 0;
    right: 0;
    aspect-ratio: 3 / 4;
    height: auto;
  }
  .story-video { position: absolute; bottom: 0; top: auto; }

}

/* Nur am TELEFON rueckt der Text hinunter, damit er direkt ueber der Zeichnung
   steht. Dort ist das Hochformat-Video 520 von 772 Pixeln hoch, oben bleiben
   also gut 250 Pixel leer, und der Text stuende sonst weit weg vom Bild.
   Auf einem hochkanten Tablet ist das anders: dort fuellt die Zeichnung die
   Buehne ganz aus, es gibt keine Luecke, und der Text gehoert nach oben in den
   Himmel der Zeichnung. */
@media (max-width: 639px) and (orientation: portrait) {
  .story-copy > * { top: 20svh; }
}

/* QUER gedrehtes Telefon. Dort passt der hochkante Ausschnitt nicht, also
   laeuft weiter das Quer-Video, etwas groesser gezogen. Nur 120 Prozent: in
   der Sprungszene steht das Handy ganz links und das Velo ganz rechts, bei
   120 Prozent bleibt der Bildinhalt von 8 bis 92 Prozent sichtbar. */
@media (max-width: 767px) and (orientation: landscape) {
  .story-track.is-on { height: 456svh; }
  .story-video,
  .story-fallback { width: 120%; max-width: none; left: -10%; right: -10%; }
}

/* Wer im Betriebssystem weniger Bewegung eingestellt hat, bekommt kein
   scrollendes Video, sondern das Standbild der Schluss-Szene mit dem Motto.
   main.js steigt bei dieser Einstellung ohnehin vorher aus. */
@media (prefers-reduced-motion: reduce) {
  .story-track.is-on { height: auto; }
  .story-track.is-on .story-stage { position: relative; top: auto; }
  .story-track.is-on .story-video { opacity: 0; }
  .story-track.is-on .story-fallback,
  .story-track.is-on .story-outro { opacity: 1; transform: none; }
  .story-track.is-on .story-line { opacity: 0; }
}
