/*
Theme Name:  Fundament
Theme URI:   https://github.com/restitutio777/wordpress-blueprint
Author:      Marlen Zumstein Blueprint
Description: Schlankes Block-Theme fuer Websites kleiner Dienstleister. Kein
             Page-Builder, kein Build-Schritt, ein Stylesheet, ein Skript.
             Abgeleitet aus dem statischen Fundament in site/.
Version:     1.0.2
Requires at least: 6.5
Tested up to: 6.8
Requires PHP: 7.4
License:     GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: fundament
Tags:        blog, one-column, block-patterns, full-site-editing, accessibility-ready
*/

/* =============================================================
   Dieses Stylesheet ist das Fundament aus site/assets/style.css,
   unveraendert bis auf drei Stellen:

   1. Der @font-face-Block ist entfallen. Die Schriften registriert
      theme.json, dadurch laedt WordPress sie mit Preload und Cache-Headern.
   2. Rohtoene, Schriftfamilien, Schriftgroessen und Abstaende zeigen jetzt
      auf die Presets aus theme.json, jeweils mit dem alten Wert als
      Rueckfall. Damit kennt der Editor die Werte, und das Regelwerk
      funktioniert unveraendert weiter.
   3. Die Rollenebene (--color-bg, --color-text, ...) bleibt hier. Nur so
      bleibt die Palette im Editor kurz und verstaendlich.

   Wer Werte aendert, aendert sie in theme.json, nicht hier.
   ============================================================= */

/* =============================================================
   FUNDAMENT — Basis-Stylesheet für Websites kleiner Dienstleister
   Alles Branchenspezifische steckt in den Custom Properties unten.
   Für ein Kundenprojekt werden nur diese Werte getauscht; die
   Struktur (Blöcke, Grid, Rhythmus) bleibt unberührt.
   Die Reihenfolge und Benennung entspricht der späteren theme.json.
   ============================================================= */

/* =============================================================
   Schriften. Self-hosted, nur WOFF2, latin-Schnitt (enthält alle
   Umlaute und ß). Beide unter der SIL Open Font License, siehe
   site/assets/fonts/LIZENZEN.md. Keine Google-Fonts-Einbindung.
   ============================================================= */
:root {
  /* ---------- 1. Farben: Rohtöne (theme.json → settings.color.palette) ----------
     Warme Greige-Familie: ein Papierton, zwei Flächentöne, ein dunkler
     Grund, ein Akzent. Alle Kontraste sind gemessen, nicht geschätzt.     */
  --c-paper:       var(--wp--preset--color--paper, #f3f0ea); /* Papier – warmes Off-White                          */
  --c-linen:       var(--wp--preset--color--linen, #e6e1d8); /* Leinen – zweite Fläche, ruhige Abschnitte          */
  --c-sand:        var(--wp--preset--color--sand, #d3cabc); /* Sand – dritte Fläche, Bildrahmen                   */
  --c-stone:       var(--wp--preset--color--stone, #8d8379); /* Stein – mittlerer Ton, NUR Flächen, nie Fliesstext */
  --c-umber:       var(--wp--preset--color--umber, #3a3430); /* Umbra – dunkle Abschnitte (Papier darauf 10.8:1)   */
  --c-ink:         var(--wp--preset--color--ink, #2b2724); /* Tinte – warmes Fast-Schwarz (13.1:1 auf Papier)    */
  --c-ink-muted:   var(--wp--preset--color--ink-muted, #5f574f); /* Tinte gedämpft – Sekundärtext (6.2:1)              */
  --c-brick:       var(--wp--preset--color--brick, #a63f2c); /* Ziegelrot – Akzent, sparsam gesetzt (5.5:1)        */
  --c-line:        var(--wp--preset--color--line, #ddd7cd); /* Linie – Trennlinien, Rahmen                        */

  /* ---------- 2. Farben: Rollen (das, was im Regelwerk benutzt wird) ---------- */
  --color-bg:            var(--c-paper);
  --color-bg-alt:        var(--c-linen);
  --color-bg-figure:     var(--c-sand);
  --color-bg-invert:     var(--c-umber);
  --color-text:          var(--c-ink);
  --color-text-muted:    var(--c-ink-muted);
  --color-text-invert:   #f1ece4;
  --color-text-invert-muted: #c2b9ae; /* 6.3:1 auf Umbra */
  --color-accent:        var(--c-brick);
  --color-accent-hover:  #8c3423;
  --color-accent-invert: #d98b74;     /* 4.6:1 auf Umbra */
  --color-border:        var(--c-line);
  --color-focus:         var(--c-brick);

  /* ---------- 3. Schrift: Familien (keine Google Fonts – Systemstack) ---------- */
  /* Überschriften: hohe Strichstärkenkontraste, lange Oberlängen, eine
     Kursive mit Charakter. Der Systemfallback ist bewusst eine Renaissance-
     Antiqua, damit die Seite ohne Webfont nicht kippt.                    */
  --font-heading: var(--wp--preset--font-family--heading,
                  "Cormorant Garamond", Georgia, "Times New Roman", serif);
  /* Fließtext: geometrische Grotesk, ruhig, kein Charakterwettbewerb.      */
  --font-body:    var(--wp--preset--font-family--body,
                  "Jost", -apple-system, "Segoe UI", ui-sans-serif, system-ui,
                  "Helvetica Neue", Helvetica, Arial, sans-serif);
  /* Bildunterschriften und Metazeilen: die Kursive der Serife.             */
  --font-accent:  var(--font-heading);
  /* Dachzeilen: gesperrte Versalien der Textschrift.                       */
  --font-label:   var(--font-body);

  /* Stil der Überschriften. Für ein Projekt mit gesperrten Versalien
     (Spa, Mode, Gastronomie) genügt hier:
     --heading-transform: uppercase; --tracking-heading: 0.08em;          */
  --heading-transform: none;

  /* ---------- 4. Schrift: Skala (fluid, mobile-first) ---------- */
  /* Fließtext 18px mobil → 20px Desktop. Kleine Grade nur dekorativ.      */
  --text-label:   var(--wp--preset--font-size--label, clamp(0.8125rem, 0.79rem + 0.12vw, 0.875rem));  /* 13 → 14, nur Dachzeilen */
  --text-small:   var(--wp--preset--font-size--small, clamp(0.9375rem, 0.9rem + 0.2vw, 1rem));        /* 15 → 16  */
  --text-body:    var(--wp--preset--font-size--body, clamp(1.1875rem, 1.12rem + 0.32vw, 1.3125rem)); /* 19 → 21, Jost setzt klein */
  --text-lead:    var(--wp--preset--font-size--lead, clamp(1.3125rem, 1.14rem + 0.8vw, 1.6875rem));  /* 21 → 27  */
  --text-h4:      var(--wp--preset--font-size--h4, clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem));       /* 20 → 24  */
  --text-h3:      var(--wp--preset--font-size--h3, clamp(1.5rem, 1.28rem + 1.1vw, 2.0625rem));     /* 24 → 33  */
  --text-h2:      var(--wp--preset--font-size--h2, clamp(1.75rem, 1.3rem + 2.1vw, 3rem));          /* 28 → 48  */
  --text-h1:      var(--wp--preset--font-size--h1, clamp(2rem, 1.2rem + 3.6vw, 4rem));             /* 32 → 64  */
  --text-display: var(--wp--preset--font-size--display, clamp(2.375rem, 1.4rem + 4.6vw, 5rem));         /* 38 → 80  */

  /* ---------- 5. Schrift: Rhythmus ---------- */
  --leading-display: 0.98;
  --leading-heading: 1.14;
  --leading-lead:    1.5;
  --leading-body:    1.78;  /* großzügig und ruhig */
  --tracking-display: -0.025em;
  --tracking-heading: -0.012em;
  --tracking-label:   0.28em;   /* Dachzeilen sind gesperrte Versalien */
  --tracking-nav:     0.1em;
  --weight-body:      400;
  --weight-strong:    500;      /* Jost trägt bis 500, darüber wird gerechnet */
  --weight-heading:   400;  /* Serife trägt ohne Fettung */

  /* ---------- 6. Abstände (fluid) ---------- */
  --space-3xs: var(--wp--preset--spacing--3xs, clamp(0.25rem, 0.24rem + 0.1vw, 0.375rem));
  --space-2xs: var(--wp--preset--spacing--2xs, clamp(0.5rem, 0.47rem + 0.15vw, 0.625rem));
  --space-xs:  var(--wp--preset--spacing--xs, clamp(0.75rem, 0.7rem + 0.25vw, 1rem));
  --space-s:   var(--wp--preset--spacing--s, clamp(1rem, 0.93rem + 0.35vw, 1.375rem));
  --space-m:   var(--wp--preset--spacing--m, clamp(1.5rem, 1.35rem + 0.7vw, 2.25rem));
  --space-l:   var(--wp--preset--spacing--l, clamp(2.25rem, 1.95rem + 1.5vw, 3.5rem));
  --space-xl:  var(--wp--preset--spacing--xl, clamp(3.5rem, 2.9rem + 3vw, 6rem));
  --space-2xl: var(--wp--preset--spacing--2xl, clamp(5rem, 3.9rem + 5.5vw, 9rem));
  --space-3xl: var(--wp--preset--spacing--3xl, clamp(7rem, 5rem + 10vw, 14rem));

  /* Abschnittsabstand. Die Haarlinie über einem Abschnitt trennt, deshalb
     braucht es dazwischen weniger Leere als ohne sie. Der Abstand über der
     Linie ist doppelt so gross wie der darunter: die Linie gehört zu dem
     Abschnitt, den sie eröffnet. */
  --section-y:  var(--space-xl);
  --section-y-tight: var(--space-l);
  --gutter:     clamp(1.25rem, 0.9rem + 3vw, 4rem);

  /* ---------- 7. Maße ---------- */
  /* Randabzug: zieht ein Element bis an den Fensterrand, egal wie breit das Raster steht */
  --bleed-right:    calc((100vw - min(100vw - 2 * var(--gutter), var(--page-max))) / -2);

  --measure:        62ch;   /* Zeilenlänge Fließtext (60–75 Zeichen) */
  --measure-narrow: 52ch;
  --page-max:       74rem;  /* schmaler als üblich: eine Seite, kein Plakat */

  /* ---------- 8. Handlungen: die Form der Knöpfe ----------
     Ein Kundenprojekt wechselt hier den CTA-Stil, ohne eine Regel
     anzufassen. Kantig und gesperrt wäre etwa:
     --btn-radius: 0; --btn-transform: uppercase; --btn-tracking: 0.12em; */
  --btn-font:          var(--font-label);
  --btn-size:          var(--text-label);
  --btn-weight:        400;
  --btn-tracking:      0.2em;
  --btn-transform:     uppercase;
  --btn-padding:       1.05em 2.3em;
  --btn-radius:        0;
  /* Der Hauptknopf ist eine Haarlinie, keine Flaeche. Als schwarze Flaeche
     war er der schwerste Gegenstand der Seite und zog das Auge von der
     Ueberschrift weg. Gefuellt wird er erst beim Zeigen; dort darf er
     zupacken, weil dann klar ist, worauf man zeigt. */
  --btn-bg:            transparent;
  --btn-color:         var(--color-text);
  /* 55 Prozent, nicht weniger: die Kontur ist das, was den Hauptknopf vom
     leisen daneben unterscheidet, und muss dafuer 3:1 gegen den Grund
     halten (gemessen 3.4:1). */
  --btn-border:        color-mix(in oklab, var(--color-text) 55%, transparent);
  --btn-bg-hover:      var(--color-text);
  --btn-color-hover:   var(--color-bg);
  --btn-border-hover:  var(--color-text);
  /* Dasselbe auf dunklem Grund. */
  --btn-border-invert: color-mix(in oklab, var(--color-text-invert) 50%, transparent); /* 3.9:1 auf Umbra */

  /* ---------- 9. Listen und Tabellen ----------
     Leistungsliste, Beitragsliste, Faktenliste und echte Tabellen teilen
     sich diese Werte. Zebrastreifen sind aus; ein Projekt setzt dafür
     --table-row-bg-odd auf eine Fläche.                                  */
  --table-rule:         1px solid var(--color-border);
  --table-head-color:   var(--color-text-muted);
  --table-row-bg:       transparent;
  --table-row-bg-odd:   transparent;
  --table-row-bg-hover: color-mix(in oklab, var(--c-linen) 55%, transparent);
  --table-row-padding:  var(--space-m);

  /* ---------- 10. Radien & Linien ---------- */
  --radius-s:     4px;
  --radius-m:     8px;
  /* Bildform. Vier organische Radien, bewusst nah an der Mitte: sie sollen
     wie mit der Hand gezogen wirken, nicht wie eine Amöbe. Weil die Werte
     Prozente sind, dehnen sie sich mit dem Rahmen; ein breiter Rahmen ergibt
     ein liegendes Ei, ein hoher ein stehendes. Sechs Varianten, damit
     nebeneinanderliegende Bilder nicht dieselbe Form tragen.
     Für ein Kundenprojekt, das rechteckig bleiben soll, genügt hier
     --shape-image: 0. */
  /* Die sechs Werte sind Drehungen desselben Verzugs, nicht sechs Einfälle.
     Deshalb wirken sie verwandt: eine Familie, keine Sammlung. */
  --shape-organic-1: 56% 44% 68% 32% / 38% 42% 58% 62%;
  --shape-organic-2: 44% 56% 32% 68% / 62% 58% 42% 38%;
  --shape-organic-3: 68% 32% 56% 44% / 42% 38% 62% 58%;
  --shape-organic-4: 32% 68% 44% 56% / 58% 62% 38% 42%;
  --shape-organic-5: 62% 38% 34% 66% / 44% 62% 38% 56%;
  --shape-organic-6: 38% 62% 66% 34% / 56% 38% 62% 44%;
  --shape-image:  var(--shape-organic-1);
  --rule:         1px solid var(--color-border);

  /* ---------- 11. Bewegung ---------- */
  --ease:       cubic-bezier(0.2, 0.6, 0.2, 1);
  --duration:   180ms;

  /* Auftauchen beim Scrollen. Der Weg ist kurz: er soll die Zeile ankündigen,
     nicht sie herbeifliegen lassen. Wer den Effekt für ein Kundenprojekt
     abschalten will, setzt --reveal-shift und --reveal-zoom auf 0 bzw. 1. */
  --reveal-shift: 1.5rem;    /* Weg von unten                                  */
  /* Fenster, in dem es passiert. Das Ende liegt bewusst innerhalb von
     `entry`: spätestens wenn ein Element zu 60 Prozent eingetreten ist, steht
     es voll da. Ein Bereich, der erst danach endet, lässt Inhalt sichtbar
     halbtransparent stehen, und wenn die Zeitachse hängt, bleibt er ganz
     unsichtbar. */
  --reveal-range: entry 0% entry 60%;
  --reveal-zoom:  1.05;      /* Bilder setzen sich, statt zu stehen            */
  --reveal-range-image: entry 0% cover 55%;
}

/* =============================================================
   Reset & Grundlagen
   ============================================================= */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  overflow-x: clip;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-body);
  font-weight: var(--weight-body);
  line-height: var(--leading-body);
  text-wrap: pretty;
  font-kerning: normal;
  font-variant-ligatures: common-ligatures;
  -webkit-font-smoothing: antialiased;
}
::selection { background: var(--c-sand); color: var(--c-ink); }

/* Deutsche Trennung nur im Fließtext – Überschriften brechen nie mit Bindestrich */
.prose p, .article p, .service__body p {
  hyphens: auto;
  -webkit-hyphens: auto;
  hyphenate-limit-chars: 9 4 4;
}
h1, h2, h3, h4, .prose p.lead, .lead { hyphens: manual; -webkit-hyphens: manual; }
h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: var(--weight-heading);
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-heading);
  text-transform: var(--heading-transform);
  margin: 0;
  text-wrap: balance;
  font-variant-numeric: oldstyle-num;
}
/* Ein einzelnes Wort einer Überschrift darf kursiv und farbig sein. Mehr
   als eines je Seite ist eines zu viel. */
:is(h1, h2, h3) em {
  font-style: italic;
  color: var(--color-accent);
}
/* Cormorant setzt zierlich: kleine Überschriften bekommen einen Grad
   mehr Strichstärke, grosse tragen mit 400. */
:is(h3, h4) { font-weight: 500; }
h1 { font-size: var(--text-h1); }
h2 { font-size: var(--text-h2); }
h3 { font-size: var(--text-h3); }
h4 { font-size: var(--text-h4); }
p  { margin: 0; }
img, svg { display: block; max-width: 100%; height: auto; }
ul, ol { margin: 0; padding: 0; list-style: none; }
strong { font-weight: var(--weight-strong); }

a { color: var(--color-accent); text-decoration: none;
    text-underline-offset: 0.22em; text-decoration-thickness: 1px;
    border-bottom: 1px solid color-mix(in oklab, var(--color-accent) 45%, transparent);
    transition: color var(--duration) var(--ease), border-color var(--duration) var(--ease); }
a:hover { color: var(--color-accent-hover); border-bottom-color: var(--color-accent-hover); }

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

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* =============================================================
   Layout-Primitive
   ============================================================= */
.page { width: 100%; }

/* 12-Spalten-Raster, mobile-first: unten einspaltig, ab 60rem asymmetrisch */
.grid {
  position: relative;
  z-index: 1;
  width: min(100% - 2 * var(--gutter), var(--page-max));
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--space-m);
  row-gap: var(--space-l);
}
/* Mobile-first: jedes Kind belegt die volle Breite. Die Spaltenzuordnung
   weiter unten greift erst ab 48rem und überschreibt diese Regel dort. */
.grid > * { grid-column: 1 / -1; }
.prose { max-width: var(--measure); }
.prose > * + * { margin-top: var(--space-s); }
.prose-narrow { max-width: var(--measure-narrow); }

.section { padding-block: var(--section-y); position: relative; overflow: clip; isolation: isolate; }
.section--tight { padding-block: var(--section-y-tight); }

/* Haarlinie auf Rasterbreite als Auftakt eines Abschnitts. Sie gibt der
   Seite ein Rückgrat: jeder Abschnitt beginnt sichtbar, statt nur nach
   viel Leere wieder anzufangen. */
.section--linie::before {
  content: "";
  display: block;
  width: min(100% - 2 * var(--gutter), var(--page-max));
  margin-inline: auto;
  margin-bottom: var(--space-xl);
  border-top: var(--rule);
}
.section--linie.section--tight::before { margin-bottom: var(--space-l); }
.section--alt { background: var(--color-bg-alt); }
.section--invert {
  background: var(--color-bg-invert);
  color: var(--color-text-invert);
}
.section--invert h1, .section--invert h2, .section--invert h3 { color: var(--color-text-invert); }
.section--invert .label { color: var(--color-text-invert-muted); }
.section--invert a { color: var(--color-accent-invert);
  border-bottom-color: color-mix(in oklab, var(--color-accent-invert) 45%, transparent); }
.section--invert .btn--primary {
  background: transparent; color: var(--color-text-invert);
  border-color: var(--btn-border-invert);
}
.section--invert .btn--primary:hover {
  background: var(--color-text-invert); color: var(--color-bg-invert);
  border-color: var(--color-text-invert);
}

/* Spaltenzuordnung nur ab Tablet – darunter fließt alles einspaltig */
.col-full { grid-column: 1 / -1; }
@media (min-width: 48rem) {
  .col-1-7  { grid-column: 1 / 8; }
  .col-1-8  { grid-column: 1 / 9; }
  .col-1-5  { grid-column: 1 / 6; }
  .col-1-6  { grid-column: 1 / 7; }
  .col-2-8  { grid-column: 2 / 9; }
  .col-3-11 { grid-column: 3 / 12; }
  .col-4-12 { grid-column: 4 / 13; }
  .col-5-12 { grid-column: 5 / 13; }
  .col-6-12 { grid-column: 6 / 13; }
  .col-7-12 { grid-column: 7 / 12; }
  .col-7-13 { grid-column: 7 / 13; }
  .col-8-13 { grid-column: 8 / 13; }
  .col-9-13 { grid-column: 9 / 13; }
}

/* =============================================================
   Bausteine: Label, Fließtext-Varianten, Buttons, Trennlinie
   ============================================================= */
/* Dachzeile: gesperrte Versalien, klein, in der Akzentfarbe. Trennt
   mehrere Begriffe mit Mittelpunkten, nicht mit Kommata. */
.label {
  font-family: var(--font-label);
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-accent);
  margin: 0 0 var(--space-m);
}
.lead {
  font-size: var(--text-lead);
  line-height: var(--leading-lead);
  color: var(--color-text-muted);
  max-width: 42ch;
}
.caption {
  font-family: var(--font-accent);
  font-style: italic;
  font-size: 1.0625rem;
  color: var(--color-text-muted);
  line-height: 1.5;
  max-width: 40ch;
}

.actions { display: flex; flex-wrap: wrap; gap: var(--space-s) var(--space-m); align-items: center; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  white-space: nowrap;
  font-family: var(--btn-font);
  font-size: var(--btn-size);
  font-weight: var(--btn-weight);
  letter-spacing: var(--btn-tracking);
  text-transform: var(--btn-transform);
  line-height: 1;
  padding: var(--btn-padding);
  border: 1px solid transparent;
  border-radius: var(--btn-radius);
  background: none;
  cursor: pointer;
  transition: background var(--duration) var(--ease), color var(--duration) var(--ease),
              border-color var(--duration) var(--ease);
}
.btn--primary { background: var(--btn-bg); color: var(--btn-color); border-color: var(--btn-border); }
.btn--primary:hover {
  background: var(--btn-bg-hover); color: var(--btn-color-hover);
  border-color: var(--btn-border-hover);
}
.btn:active { transform: translateY(1px); }
.btn--quiet {
  border-radius: 0;
  /* Eigene Innenabstände: mit denen des Vollknopfs sässe die Linie eine
     Zeilenhöhe unter der Schrift und wirkte wie ein verirrter Strich.
     Oben derselbe Wert wie unten, sonst sitzt die Schrift des leisen Knopfs
     nicht auf der Höhe der Schrift des Hauptknopfs daneben. */
  padding: 0.55em 0;
  color: var(--color-text);
  border-bottom: 1px solid color-mix(in oklab, var(--color-accent) 45%, transparent);
}
.btn--quiet:hover { color: var(--color-accent); border-bottom-color: var(--color-accent); }
a.btn { border-bottom-width: 1px; }
a.btn--primary { border-bottom-color: transparent; }

.rule { border: 0; border-top: var(--rule); margin: 0; }

/* =============================================================
   BLOCK: Header / Navigation
   ============================================================= */
/* Keine Trennlinie: der Weissraum trennt, und das Herobild darf bis unter
   die Kopfzeile laufen. */
.site-header { background: var(--color-bg); }
.site-header__inner {
  width: min(100% - 2 * var(--gutter), var(--page-max));
  margin-inline: auto;
  display: flex;
  flex-wrap: wrap;
  /* Mobil steht die Wortmarke einem Symbol gegenüber, nicht einer Zeile
     Text: Basislinienausrichtung würde den Knopf tiefer hängen lassen, weil
     das Balkensymbol keine Textbasislinie hat. Also optisch mitteln. */
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2xs) var(--space-m);
  padding-block: var(--space-m);
}
/* Die Marke ist der Name. Kein Zeichen daneben, nur ein Punkt am Ende,
   und der ist der einzige Farbakzent in der Kopfzeile. */
.brand {
  display: inline-flex;
  align-items: baseline;
  gap: 0.6em;
  font-family: var(--font-heading);
  font-size: clamp(1.5rem, 1.3rem + 0.7vw, 1.875rem);
  letter-spacing: -0.005em;
  color: var(--color-text);
  border: 0;
}
.brand:hover { color: var(--color-accent); }
.brand__name { white-space: nowrap; }
.brand__dot { color: var(--color-accent); margin-left: -0.1em; font-size: 1.25em; }
.brand__sub {
  font-family: var(--font-accent); font-style: italic;
  font-size: var(--text-small); color: var(--color-text-muted);
}
/* Auf schmalen Geräten trägt der Name allein; der Zusatz würde umbrechen. */
@media (max-width: 59.999rem) { .brand__sub { display: none; } }

.nav-toggle {
  font: inherit; font-size: var(--text-small);
  display: inline-flex; align-items: center; gap: 0.6em;
  background: none; border: 0; cursor: pointer; color: var(--color-text);
  /* 44px Tippziel, ohne die Kopfzeile höher zu machen */
  min-height: 2.75rem; padding: 0 0 0 var(--space-xs); margin-right: -0.25rem;
}
.nav-toggle__bars { display: grid; gap: 4px; width: 20px; }
.nav-toggle__bars i { display: block; height: 1px; background: currentColor; }

.site-nav ul { display: grid; gap: var(--space-xs); }
.site-nav a {
  color: var(--color-text); border: 0; font-size: var(--text-body);
}
.site-nav a:hover { color: var(--color-accent); }
.site-nav a[aria-current="page"] {
  color: var(--color-text);
  border-bottom: 1px solid var(--color-accent);
  padding-bottom: 0.2em;
}

/* Mobil: Menü eingeklappt, per JS umgeschaltet */
@media (max-width: 47.999rem) {
  .site-nav {
    display: none;
    flex-basis: 100%;
    padding-block: var(--space-s) var(--space-2xs);
    margin-top: var(--space-2xs);
    border-top: var(--rule);
  }
  .site-nav[data-open="true"] { display: block; }
}
@media (min-width: 48rem) {
  /* Rechts steht jetzt eine Zeile Text: hier ist die Basislinie richtig. */
  .site-header__inner { align-items: baseline; }
  .nav-toggle { display: none; }
  .site-nav { margin-left: auto; }
  .site-nav ul { display: flex; flex-wrap: wrap; gap: var(--space-l); align-items: baseline; }
  .site-nav a { font-size: var(--text-small); letter-spacing: var(--tracking-nav); }
}

/* =============================================================
   BLOCK: Hero
   ============================================================= */
.hero {
  position: relative;
  isolation: isolate;
  display: grid;
  align-items: center;
  min-height: min(82svh, 44rem);
  /* Mobil sitzt die Buehne direkt unter der Kopfzeile, die selbst schon
     polstert. Mit dem Desktopwert stand die Ueberschrift zu tief und das
     Bild fiel ganz unter den Falz. */
  padding-block: var(--space-m) var(--space-l);
}
.hero .grid { align-items: center; }
.hero__title {
  font-size: var(--text-display);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  max-width: 14ch;
}
.hero__body { margin-top: var(--space-s); }
.hero__actions { margin-top: var(--space-m); }
/* Quadratisch auf jeder Breite. Im Hochformat war das Bild auf dem Telefon
   eine Wand, die den Rest der Seite wegdrueckte, und von der organischen
   Kontur blieb nichts zu sehen. */
.hero__figure { margin: 0; }
.hero__figure .figure__frame { aspect-ratio: 1 / 1; }
@media (min-width: 48rem) {
  /* Bild und Dachzeile beginnen auf derselben Linie. Ein Bild, das höher
     ansetzt als der Text, drängt sich vor; die Seite soll mit dem Wort
     anfangen und das Bild danebenstellen.

     Das Bild wird nicht mehr auf die Rasterzeile gestreckt, sondern hat ein
     Seitenverhältnis. Gestreckt war es so hoch wie der Textblock und damit
     zu gross; ausserdem hing seine Höhe an der Textlänge, was bei einer
     kürzeren Überschrift jedes Mal anders aussieht. */
  .hero { padding-block: var(--space-l) var(--space-xl); align-items: center; }
  .hero .grid { align-items: start; }
}

/* =============================================================
   BLOCK: Bild (Figur mit Bildunterschrift)
   ============================================================= */
.figure { margin: 0; display: grid; gap: var(--space-s); }
.figure__frame {
  position: relative;
  aspect-ratio: 4 / 5;
  background: var(--color-bg-figure);
  border-radius: var(--shape-image);
  overflow: hidden;
  /* Ohne die eigene Ebene rechnet Safari den Beschnitt an der gebogenen
     Kante bei jedem Scrollschritt neu und die Kante flimmert. */
  will-change: transform;
}
/* Die Formen wechseln durch, damit zwei Bilder auf einer Seite nie dieselbe
   Kontur haben. Die Zuweisung hängt am Block, nicht an nth-child: ein Block
   soll seine Form behalten, auch wenn er verschoben wird. */
/* Die Form wird am Bild gewählt, nicht am Block: so trägt jede Seite eine
   eigene Kontur, und im Editor wird daraus später eine Stilvariante des
   Bildblocks. Ohne Angabe gilt die erste Form. */
.figure--form-1 { --shape-image: var(--shape-organic-1); }
.figure--form-2 { --shape-image: var(--shape-organic-2); }
.figure--form-3 { --shape-image: var(--shape-organic-3); }
.figure--form-4 { --shape-image: var(--shape-organic-4); }
.figure--form-5 { --shape-image: var(--shape-organic-5); }
.figure--form-6 { --shape-image: var(--shape-organic-6); }
.figure__frame--wide { aspect-ratio: 16 / 10; }
/* Zweite Bildfläche, dunkler. Zwei Tonwerte nebeneinander geben einer
   Bildstrecke Halt, auch solange nur Platzhalter darin stehen. */
.figure--tone .figure__frame { background: var(--c-stone); }
.figure--bleed .figure__frame { margin-right: var(--bleed-right); }
.figure--bleed-l .figure__frame { margin-left: var(--bleed-right); }
@media (max-width: 47.999rem) {
  .figure--bleed .figure__frame { margin-right: 0; }
  .figure--bleed-l .figure__frame { margin-left: 0; }
}
.figure__frame--tall { aspect-ratio: 3 / 4; }
/* Das Bild liegt absolut im Rahmen. Sonst gibt es seine Eigenhöhe an das
   Raster weiter und ein Rahmen, dessen Höhe aus dem Fenster kommen soll,
   wächst wieder auf die Pixelhöhe der Datei. Jeder Rahmen hat entweder ein
   Seitenverhältnis oder eine Mindesthöhe, also fehlt nie eine Höhe. */
.figure__frame img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
}

/* =============================================================
   BLOCK: Bild-Text-Abschnitt
   ============================================================= */
.mediatext { align-items: start; }
.mediatext__body > * + * { margin-top: var(--space-s); }
@media (min-width: 48rem) {
  /* Text mittig gegen das Bild, nicht mit einem festen Vorschub nach unten
     geschoben: der Vorschub stimmt nur bei einer Bildhöhe. */
  .mediatext { align-items: center; }
}

/* =============================================================
   BLOCK: Leistungsliste (Zeilen mit Linien, keine Karten)
   ============================================================= */
.services { display: grid; gap: 0; container-type: inline-size; }
.service {
  display: grid;
  gap: var(--space-xs);
  padding: var(--table-row-padding) var(--space-s);
  margin-inline: calc(-1 * var(--space-s));
  border-top: var(--table-rule);
  background: var(--table-row-bg);
  transition: background var(--duration) var(--ease);
}
.service:nth-child(odd) { background: var(--table-row-bg-odd); }
.service:last-child { border-bottom: var(--table-rule); }
.service h2 { font-size: var(--text-h3); }
.service__index {
  font-family: var(--font-label);
  font-size: var(--text-label);
  letter-spacing: var(--tracking-nav);
  color: var(--color-accent);
  font-variant-numeric: lining-nums;
}
.service:hover { background: var(--table-row-bg-hover); }
.service__body { max-width: var(--measure); }
/* In der Liste sind Titel immer gleich gross, egal ob h2 (Leistungsseite)
   oder h3 (Startseite). */
.service :is(h2, h3) { font-size: var(--text-h3); font-weight: 500; }

@container (min-width: 44rem) {
  /* Feste Spalten statt Bruchteilen: bei einem kurzen Titel riss sonst
     zwischen Titel und Text eine leere Spalte auf. */
  .service {
    grid-template-columns: 2.5rem minmax(0, 17rem) minmax(0, 32rem);
    column-gap: var(--space-l);
    /* Nummer, Titel und Text sitzen auf einer gemeinsamen ersten
       Basislinie. Das erledigt das Raster; Innenabstände von Hand
       stimmen immer nur bei einer Fenstergrösse. */
    align-items: baseline;
  }
  .service__note { grid-column: 3; margin-top: var(--space-s); }
}

@container (min-width: 62rem) {
  /* Breit genug für vier Spalten: Nummer, Titel, Beschreibung, Konditionen.
     Damit endet die Zeile am rechten Rasterrand, statt eine hohe leere
     Fläche neben sich stehen zu lassen. */
  .service {
    grid-template-columns: 2.5rem minmax(0, 15rem) minmax(0, 30rem) minmax(0, 1fr);
  }
  .service__note { grid-column: 4; margin-top: 0; }
}

/* =============================================================
   BLOCK: Zitat
   ============================================================= */
.pullquote { margin: 0; }
.pullquote blockquote p::before { content: "\201E"; margin-left: -0.52em; color: var(--color-accent); }
.pullquote blockquote p::after { content: "\201C"; color: var(--color-accent); }
.pullquote blockquote {
  margin: 0;
  font-family: var(--font-heading);
  font-style: italic;
  font-size: var(--text-h3);
  line-height: 1.34;
  letter-spacing: var(--tracking-heading);
  max-width: 34ch;
  text-wrap: pretty;
}
.pullquote figcaption {
  margin-top: var(--space-m);
  font-size: var(--text-small);
  color: var(--color-text-muted);
}

/* =============================================================
   BLOCK: Notizen / Beitragsliste
   ============================================================= */
.postlist { display: grid; container-type: inline-size; }
.post-row {
  display: grid;
  gap: var(--space-2xs);
  padding: var(--table-row-padding) var(--space-s);
  margin-inline: calc(-1 * var(--space-s));
  border-top: var(--table-rule);
  background: var(--table-row-bg);
  transition: background var(--duration) var(--ease);
}
.post-row:nth-child(odd) { background: var(--table-row-bg-odd); }
.post-row:hover { background: var(--table-row-bg-hover); }
.post-row:last-child { border-bottom: var(--table-rule); }
.post-row > div { display: grid; gap: var(--space-2xs); max-width: var(--measure); }
.post-row__meta { font-family: var(--font-accent); font-style: italic;
  font-size: var(--text-small); font-variant-numeric: oldstyle-num;
  color: var(--table-head-color); }
.post-row :is(h2, h3) { font-size: var(--text-h4); font-weight: 500; }
.post-row :is(h2, h3) a { color: var(--color-text); border: 0; }
.post-row :is(h2, h3) a:hover { color: var(--color-accent); }
.post-row p { color: var(--color-text-muted); max-width: var(--measure); }
@container (min-width: 40rem) {
  .post-row {
    grid-template-columns: 10rem minmax(0, 1fr);
    column-gap: var(--space-m);
    align-items: baseline;
  }
}

/* =============================================================
   BLOCK: Call-to-Action
   ============================================================= */
.cta__title { font-size: var(--text-h2); max-width: 26ch; }
.cta__body { margin-top: var(--space-m); }
.cta__actions { margin-top: var(--space-l); }

/* =============================================================
   BLOCK: Footer
   ============================================================= */
.site-footer {
  /* Die Trennlinie sitzt auf der Rasterbreite, nicht am Fensterrand:
     alle anderen Linien der Seite (Listen, Fakten, Beitragsnavigation)
     enden ebenfalls an der Textkante. */
  padding-block: 0 var(--space-l);
  font-size: var(--text-small);
  color: var(--color-text-muted);
}
.site-footer > .grid:first-of-type {
  border-top: var(--rule);
  padding-top: var(--space-xl);
}
.site-footer .grid { row-gap: var(--space-l); }
.site-footer h2 { font-size: var(--text-h4); color: var(--color-text); }
.site-footer ul { display: grid; gap: var(--space-2xs); }
.site-footer a { color: var(--color-text-muted); border-bottom-color: var(--color-border); }
.site-footer a:hover { color: var(--color-accent); }
.site-footer__legal {
  margin-top: var(--space-xl);
  padding-top: var(--space-s);
  border-top: var(--rule);
  display: flex; flex-wrap: wrap; gap: var(--space-s) var(--space-m);
  justify-content: space-between;
  font-family: var(--font-accent);
  font-style: italic;
  font-size: var(--text-small);
}

/* =============================================================
   BLOCK: Seitenkopf (Unterseiten) (→ block-pattern: page-header)
   ============================================================= */
/* Seitenkopf. Oben knapp: die Kopfzeile hat schon Innenabstand, und ein
   Seitenanfang, der erst nach 270 Pixeln Leere beginnt, wirkt nicht
   grosszügig, sondern unfertig. */
.pagehead { padding-block: var(--space-l) var(--section-y); position: relative; isolation: isolate; }
.pagehead__title { font-size: var(--text-h1); max-width: 20ch; }
.pagehead__body { margin-top: var(--space-m); }

/* Seitenkopf mit Bild: dieselbe Komposition wie die Bühne der Startseite.
   Das Bild beginnt an der Oberkante des Abschnitts und hat eine feste Höhe,
   der Text steht mittig dagegen. Vorher hingen beide unverbunden
   nebeneinander und das Bild lief unten aus dem Bild. */
@media (min-width: 48rem) {
  /* Dieselbe Konstruktion wie die Bühne der Startseite, nur kleiner: eine
     Unterseite soll anfangen, nicht auftrumpfen. Die Begründung für die
     beiden unscheinbaren Angaben (height am Rahmen, minmax(0,1fr) an der
     Figur) steht beim Hero. */
  .pagehead--media { padding-block: var(--space-l) var(--section-y); }
  .pagehead--media .grid { align-items: start; }
  .pagehead--media .figure__frame { aspect-ratio: 1 / 1; }
}

/* =============================================================
   BLOCK: Formular (→ block-pattern: contact-form)
   ============================================================= */
.form { display: grid; gap: var(--space-m); max-width: 34rem; }
.form__row { display: grid; gap: var(--space-m); }
@media (min-width: 40rem) { .form__row { grid-template-columns: 1fr 1fr; } }
.form-field { display: grid; gap: var(--space-3xs); }
.form-field > label { font-size: var(--text-small); color: var(--color-text); }
.input, .textarea {
  font: inherit;
  font-size: var(--text-body);
  color: var(--color-text);
  background: transparent;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  padding: 0.7em 0.85em;
  width: 100%;
  transition: border-color var(--duration) var(--ease);
}
.input:hover, .textarea:hover { border-color: var(--color-text-muted); }
.textarea { min-height: 10rem; resize: vertical; line-height: 1.6; }
.form-field__hint { font-size: var(--text-small); color: var(--color-text-muted); }
.form__note { font-size: var(--text-small); color: var(--color-text-muted); max-width: 46ch; }

/* =============================================================
   BLOCK: Faktenliste (Kontaktdaten, Rahmenbedingungen)
   ============================================================= */
.facts { display: grid; margin: 0; container-type: inline-size; }
.facts__row {
  border-top: var(--table-rule);
  padding-block: var(--space-s);
  display: grid; gap: var(--space-3xs);
  background: var(--table-row-bg);
}
.facts__row:nth-child(odd) { background: var(--table-row-bg-odd); }
.facts__row:last-child { border-bottom: var(--table-rule); }
.facts dt {
  font-family: var(--font-accent); font-style: italic;
  font-size: var(--text-small); font-variant-numeric: oldstyle-num;
  color: var(--table-head-color);
}
.facts dd { margin: 0; max-width: 34rem; }
@container (min-width: 26rem) {
  .facts__row {
    grid-template-columns: 10rem minmax(0, 1fr);
    column-gap: var(--space-m);
    align-items: baseline;
  }
}

/* =============================================================
   Tabellen (Preise, Termine, Öffnungszeiten)
   Teilen sich die Werte mit den Listenblöcken darüber.
   ============================================================= */
table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: oldstyle-num tabular-nums;
}
caption {
  text-align: left;
  font-family: var(--font-accent); font-style: italic;
  font-size: var(--text-small); color: var(--table-head-color);
  padding-block: 0 var(--space-xs);
}
th, td {
  text-align: left;
  padding: calc(var(--table-row-padding) / 2) var(--space-s) calc(var(--table-row-padding) / 2) 0;
  border-bottom: var(--table-rule);
  vertical-align: baseline;
}
thead th {
  font-family: var(--font-accent); font-style: italic; font-weight: 400;
  font-size: var(--text-small); color: var(--table-head-color);
}
tbody tr { background: var(--table-row-bg); }
tbody tr:nth-child(odd) { background: var(--table-row-bg-odd); }
tbody tr:hover { background: var(--table-row-bg-hover); }

/* =============================================================
   BLOCK: Beitrag (Blog-Einzelbeitrag) (→ block-pattern: article)
   ============================================================= */
.article { max-width: var(--measure); }
.article > * + * { margin-top: var(--space-s); }
.article h2 { font-size: var(--text-h3); margin-top: var(--space-xl); }
.article h3 { font-size: var(--text-h4); margin-top: var(--space-l); }
.article blockquote {
  margin: var(--space-l) 0 var(--space-l) var(--space-m);
  font-family: var(--font-heading);
  font-style: italic;
  font-size: var(--text-lead);
  line-height: 1.38;
  max-width: 36ch;
}
.article ul { display: grid; gap: var(--space-2xs); }
.article li { padding-left: 1.5em; position: relative; }
.article li::before { content: "—"; position: absolute; left: 0; color: var(--color-accent); }
.article-meta {
  font-family: var(--font-accent); font-style: italic;
  font-size: var(--text-small); color: var(--color-text-muted);
}
.postnav {
  border-top: var(--rule);
  padding-top: var(--space-m);
  display: flex; flex-wrap: wrap; justify-content: space-between;
  gap: var(--space-s) var(--space-m);
  font-size: var(--text-small);
}

/* =============================================================
   Auftauchen beim Scrollen
   -------------------------------------------------------------
   Reines CSS über scroll-gesteuerte Animationen (animation-timeline).
   Kein Skript, keine Bibliothek, kein IntersectionObserver: die Regel
   „nur das Mobilmenü nutzt JavaScript" bleibt bestehen, und der Effekt
   wandert später unverändert ins Block-Theme, weil er nur im Stylesheet
   steht und an Klassen hängt, die jedes Pattern ohnehin trägt.

   Alles steckt in @supports. Wer die Technik nicht kennt (global rund 85
   Prozent Abdeckung, Chrome und Edge ab 115, Safari ab 26, Firefox ab 157),
   bekommt die Seite unbewegt und vollständig zu sehen. Ohne die Kapselung
   bliebe der Inhalt dort auf Deckkraft null stehen, also unsichtbar.

   prefers-reduced-motion schaltet den Block ganz ab. Die pauschale Regel
   weiter oben genügt hier nicht: eine scroll-gesteuerte Animation läuft
   nicht über die Zeit, eine gekürzte animation-duration erreicht sie nicht.
   ============================================================= */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* Hier wird bewusst NUR verschoben und nicht eingeblendet.
       Eine scroll-gesteuerte Animation kann stehen bleiben: in einem
       Hintergrundtab, in einer gedrosselten Ansicht, im Energiesparmodus.
       Steht eine Animation auf Deckkraft null, ist die Seite dann leer. Genau
       das ist beim Bauen passiert, ein ganzer Bildschirm blankes Papier.
       Ein hängender Versatz kostet zwanzig Pixel und verbirgt nichts.
       Deshalb gilt für dieses Fundament: kein animiertes opacity auf Inhalt. */
    @keyframes auftauchen {
      from { transform: translateY(var(--reveal-shift)); }
      to   { transform: none; }
    }
    /* Die Haarlinie zieht sich von links ein. Sie ist das Bindeglied
       zwischen zwei Abschnitten, also ist sie auch die Bewegung, die den
       nächsten Abschnitt eröffnet. */
    @keyframes linie-ziehen {
      from { transform: scaleX(0); }
      to   { transform: scaleX(1); }
    }
    /* Das Bild kommt eine Spur zu gross herein und setzt sich. Der Rahmen
       schneidet ab, der Ausschnitt bleibt also gefüllt. */
    @keyframes bild-setzen {
      from { transform: scale(var(--reveal-zoom)); }
      to   { transform: none; }
    }

    /* Die Rasterkinder eines Abschnitts. Ausgenommen sind die Spalten, in
       denen eine Liste steht: deren Zeilen tauchen einzeln auf, sonst liefe
       beides übereinander. Die Bühne bleibt unberührt, sie steht beim Laden
       schon im Bild. */
    .section .grid > *:not(:has(.services)):not(:has(.postlist)),
    .site-footer .grid > * {
      /* Langform mit duration: auto. Die Kurzform `animation: x linear both`
         setzt die Dauer auf 0s, und eine scroll-gesteuerte Animation mit der
         Dauer null steht sofort am Ende: alles wäre sichtbar, nichts bewegt
         sich. Das war der erste Anlauf und sah aus wie gar keine Regel. */
      animation-name: auftauchen;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      animation-range: var(--reveal-range);
    }

    /* Listenzeilen bekommen jede ihre eigene Zeitachse. Das ergibt die
       Staffelung von selbst, ohne animation-delay je Zeile. */
    .services .service,
    .postlist .post-row {
      /* Langform mit duration: auto. Die Kurzform `animation: x linear both`
         setzt die Dauer auf 0s, und eine scroll-gesteuerte Animation mit der
         Dauer null steht sofort am Ende: alles wäre sichtbar, nichts bewegt
         sich. Das war der erste Anlauf und sah aus wie gar keine Regel. */
      animation-name: auftauchen;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      animation-range: var(--reveal-range);
    }

    .section--linie::before {
      transform-origin: left center;
      animation-name: linie-ziehen;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      animation-range: entry 0% entry 40%;
    }

    .figure__frame img {
      animation-name: bild-setzen;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      animation-range: var(--reveal-range-image);
    }
    /* Das Herobild steht beim Laden im Bild und darf nicht zappeln. */
    .hero__figure .figure__frame img { animation-name: none; }
  }
}

/* =============================================================
   BLOCK: Galerie (→ block-pattern: gallery)
   -------------------------------------------------------------
   Übersicht und Grossansicht, ohne eine Zeile JavaScript. Die
   Grossansicht hängt an :target: ein Anker öffnet sie, ein Anker auf den
   Galerieabschnitt schliesst sie. Damit landet man auf dem Bild, das man
   angeklickt hat.

   Die naheliegendere Technik wäre `popover` gewesen, mit Escape und
   Schliessen bei Klick daneben ohne Zutun. Sie scheitert am Weiterblättern:
   ein Knopf, der aus einem offenen Popover das nächste öffnet, gilt als
   verschachtelt, beide bleiben offen und der Stapel bricht beim nächsten
   Schritt zusammen. Nachgemessen, nicht vermutet.

   Was dadurch fehlt, ist die Escape-Taste. Ersatz sind eine ganzflächige
   Schliessfläche hinter dem Bild und ein sichtbarer Knopf.
   ============================================================= */
.gallery {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-m);
}
@media (min-width: 48rem) {
  /* Fünf Bilder in einer Reihe, jedes in einer eigenen Kontur. Kein
     Karussell: was auf einen Blick passt, muss man nicht durchblättern. */
  .gallery { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--space-s); }
}
.gallery__item {
  display: block;
  border: 0;
  /* Der Anker ist nur Träger, die Kontur macht der Rahmen darin. */
}
.gallery__frame {
  display: block;
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--color-bg-figure);
  border-radius: var(--shape-image);
  transition: opacity var(--duration) var(--ease);
}
.gallery__frame img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.gallery__item:hover .gallery__frame,
.gallery__item:focus-visible .gallery__frame { opacity: 0.72; }

/* ---------- Grossansicht ---------- */
.lightbox { display: none; }
.lightbox:target {
  display: grid;
  position: fixed;
  inset: 0;
  z-index: 20;
  grid-template-rows: 1fr auto;
  justify-items: center;
  gap: var(--space-m);
  padding: var(--space-l) var(--gutter);
  background: color-mix(in oklab, var(--c-umber) 94%, transparent);
  color: var(--color-text-invert);
}
/* Liegt hinter allem und schliesst. tabindex="-1" im Markup: die Fläche ist
   kein eigenes Ziel für die Tastatur, dafür gibt es den Knopf. */
.lightbox__grund { position: absolute; inset: 0; border: 0; }
.lightbox__figur {
  margin: 0;
  display: grid;
  gap: var(--space-s);
  justify-items: center;
  align-content: center;
  position: relative;
  min-height: 0;
}
.lightbox__figur img {
  max-width: 100%;
  max-height: min(68svh, 44rem);
  width: auto; height: auto;
  object-fit: contain;
  border-radius: var(--radius-m);
}
.lightbox__figur .caption {
  color: var(--color-text-invert-muted);
  text-align: center;
}
.lightbox__nav {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: var(--space-l);
  font-family: var(--font-label);
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}
.lightbox__schritt {
  color: var(--color-text-invert);
  border-bottom: 1px solid color-mix(in oklab, var(--color-text-invert) 40%, transparent);
  padding-bottom: 0.25em;
}
.lightbox__schritt:hover { color: var(--color-accent-invert); border-bottom-color: var(--color-accent-invert); }
.lightbox__zaehler { margin: 0; color: var(--color-text-invert-muted); font-variant-numeric: lining-nums; }
.lightbox__zu {
  position: absolute;
  top: var(--space-s); right: var(--gutter);
  color: var(--color-text-invert);
  border: 0;
  font-family: var(--font-label);
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  /* 44px Tippziel, ohne dass der Text grösser wird */
  padding: var(--space-2xs) 0 var(--space-2xs) var(--space-s);
  min-height: 2.75rem;
  display: inline-flex; align-items: center;
}
.lightbox__zu:hover { color: var(--color-accent-invert); }

/* =============================================================
   Druck
   ============================================================= */
@media print {
  .site-header, .site-footer, .postnav, .nav-toggle { display: none; }
  .section, .pagehead { padding-block: var(--space-m); }
  .section--invert { background: none; color: var(--color-text); }
  .grid { width: 100%; }
  a { border: 0; color: var(--color-text); }
}

/* =============================================================
   Hilfsklassen
   ============================================================= */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.skip-link {
  position: absolute; left: var(--gutter); top: -4rem; z-index: 10;
  background: var(--color-text); color: var(--color-bg);
  padding: var(--space-2xs) var(--space-s); border: 0;
  transition: top var(--duration) var(--ease);
}
.skip-link:focus { top: var(--space-2xs); color: var(--color-bg); }

/* =============================================================
   Ergänzungen für die Unterseiten
   ============================================================= */

/* Bild innerhalb eines Beitrags */
.article > figure { margin-block: var(--space-l); display: grid; gap: var(--space-s); }
.article > figure .figure__frame { aspect-ratio: 16 / 10; }
.article > figure figcaption { font-family: var(--font-accent); font-style: italic;
  font-size: var(--text-small); color: var(--color-text-muted); max-width: 44ch; }
.article__lead { font-size: var(--text-lead); line-height: var(--leading-lead);
  color: var(--color-text-muted); }

/* Leistungsliste in der ausführlichen Fassung (Seite „Leistungen“) */
.service__body > * + * { margin-top: var(--space-s); }
.service__note { font-family: var(--font-accent); font-style: italic;
  font-size: var(--text-small); color: var(--color-text-muted); }

/* Aufzählung im Fließtext (ohne Listenpunkte, Gedankenstrich als Marke) */
.list { display: grid; gap: var(--space-2xs); max-width: var(--measure); }
.list li { padding-left: 1.5em; position: relative; }
.list li::before { content: "\2014"; position: absolute; left: 0; color: var(--color-accent); }

/* Footer-Spalten (ersetzt Inline-Styles, damit der Block 1:1 übertragbar bleibt) */
.footer-cols { display: grid; gap: var(--space-l);
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }

/* Abstandshilfen – nur dort, wo ein Block sonst ein Inline-Style bräuchte */
.mt-2xs { margin-top: var(--space-2xs); }
.mt-s   { margin-top: var(--space-s); }
.mt-m   { margin-top: var(--space-m); }
.mt-l   { margin-top: var(--space-l); }
.mb-xs  { margin-bottom: var(--space-xs); }

/* =============================================================
   BRÜCKE ZU DEN BLÖCKEN
   -------------------------------------------------------------
   Alles oberhalb dieser Linie ist das Fundament aus site/. Es kennt
   .grid und .col-1-6; WordPress liefert stattdessen .wp-block-columns
   und .wp-block-column. Dieser Abschnitt schliesst das eine an das
   andere an. Er ist der einzige Teil des Stylesheets, den die
   statische Fassung nicht hat.

   Ungenutzt im Theme und deshalb hier ohne Wirkung: .grid und die
   .col-*-Klassen, .nav-toggle (der Navigation-Block bringt sein eigenes
   Mobilmenü mit), .gallery__* und .lightbox* (die Grossansicht übernimmt
   die Lightbox des Bildblocks). Beim ersten Kundenprojekt können sie
   raus; solange das Theme aus site/ nachgezogen wird, bleiben sie stehen,
   damit beide Fassungen dieselbe Datei teilen.
   ============================================================= */

/* ---------- Abschnitte ---------- */
/* WordPress setzt die Breite selbst, über layout.contentSize: die Regel
   `.is-layout-constrained > * { margin-inline: auto }` zentriert jedes Kind
   eines Abschnitts.

   Hier stand vorher `margin-inline: 0` auf genau diesen Kindern, um die
   Zentrierung zu "korrigieren". Das war falsch und hat Leistungsliste,
   Faktenliste und Zitat an den linken Fensterrand gezogen, waehrend die
   Galerie richtig sass. Nachgemessen in build/theme-vorschau.html. Die Regel
   ist ersatzlos entfallen: WordPress macht das schon richtig. */

/* Die Haarlinie sass in der statischen Fassung auf Rasterbreite und hat sich
   die Breite selbst genommen. Im Theme bekommt sie sie vom Abschnitt. */
.wp-block-group.section--linie::before {
  width: 100%;
  max-width: var(--wp--style--global--content-size, var(--page-max));
  margin-inline: auto;
}

/* Die Leistungszeilen ziehen sich um --space-s nach aussen, damit die Flaeche
   beim Ueberfahren breiter ist als der Text. Innerhalb einer zentrierten
   Spalte wuerde das die Zeile aus der Seite schieben. */
.wp-block-group.services { max-width: var(--wp--style--global--content-size, var(--page-max)); }
.wp-block-group.service { margin-inline: 0; padding-inline: var(--space-s); }

/* Der Rasterrahmen ist das einzige Kind eines Abschnitts und wird mittig
   gestellt. WordPress tut das selbst, aber mit `:where()` und damit ohne
   Spezifitaet; das Fundament setzt an mehreren Stellen `margin: 0`, und das
   schlaegt die Regel von WordPress. */
.wp-block-group.section > .raster,
.wp-block-group.pagehead > .raster,
.wp-block-group.hero > .raster { margin-inline: auto; }

/* ---------- Seitenrand ---------- */
/* Die Polsterung liegt in den Abschnitten, nicht am Body. Sonst laufen die
   Flaechen (section--alt, section--invert) nicht bis zum Fensterrand, sondern
   enden vorher, und der dunkle Abschluss der Seite bekommt links und rechts
   einen hellen Streifen. */
.wp-block-group.section,
.wp-block-group.pagehead,
.wp-block-group.hero,
.site-header,
.site-footer { padding-inline: var(--gutter); }

/* Der Rasterrahmen. Das Gegenstueck zu .grid in der statischen Fassung: der
   Abschnitt laeuft ueber die volle Breite, damit die Flaechen bis zum
   Fensterrand reichen, und der Rahmen darin haelt die Seitenbreite.

   Ohne diese Ebene zentriert WordPress jedes einzelne Element eines
   Abschnitts auf seine eigene Breite. Ein Fliesstext mit 62 Zeichen stand
   dann mittig statt links am Raster, und die Buehne rutschte aus der Flucht.
   Mit dem Rahmen ist alles darin linksbuendig, genau wie im Zwoelfspalten-
   raster. */
.wp-block-group.raster {
  width: 100%;
  max-width: var(--wp--style--global--content-size, var(--page-max));
  margin-inline: auto;
}
.wp-block-group.raster > * { margin-inline: 0; }
/* Die inneren Gruppen stehen bewusst auf layout "default", nicht auf
   "constrained". WordPress baut fuer eine Gruppe mit eigener contentSize eine
   Regel mit Layout-Klasse, die spaeter als dieses Stylesheet greift und den
   Inhalt mittig stellt. In der Live-Installation stand der Fliesstext dadurch
   zentriert statt links am Raster. Die Breite kommt jetzt von hier. */
/* Fliesstext steht in der statischen Fassung in col-1-7, also auf 677 von
   1184 Pixeln. Die Lesbarkeitsgrenze von 62 Zeichen liegt darueber (808) und
   griffe hier nie. */
.wp-block-group.raster > .prose,
.wp-block-group.raster > .wp-block-group.prose { max-width: 42rem; }

/* Der Kopf eines Abschnitts (Dachzeile und Ueberschrift) steht in der
   statischen Fassung in col-1-7, also auf 677 von 1184 Pixeln. */
.wp-block-group.section__kopf { max-width: 42rem; margin-inline: 0; }
.wp-block-group.section__kopf > * { margin-inline: 0; }

/* ---------- Spalten statt Raster ---------- */
/* Der Abstand zwischen den Spalten ist die Luft, die in der statischen
   Fassung eine leere Rasterspalte gemacht hat. Die Breiten in den Patterns
   liegen zusammen unter hundert Prozent: flex-basis rechnet den Abstand
   nicht mit, sonst schrumpfen alle Spalten und die Bühne wird zu eng. */
.wp-block-columns { gap: var(--space-xl); margin-bottom: 0; }

/* Buehne, Seitenkopf mit Bild und Bild-Text-Abschnitt haben in der statischen
   Fassung eine leere Rasterspalte zwischen Text und Bild. Aus zwoelf Spalten
   und dem Rasterabstand ergibt das 135 von 1184 Pixeln, also 11.4 Prozent.
   Als Prozentwert skaliert der Abstand mit der Seite, genau wie das Raster.
   Die Spaltenbreiten daneben sind ebenfalls aus dem Raster gerechnet, damit
   Bild und Textspalte auf den Pixel dieselbe Breite haben wie vorher. */
/* Der Zeilenabstand steht ausdruecklich davor: ein Prozentwert als row-gap
   loest sich in einem Behaelter mit automatischer Hoehe zu null auf. Mobil,
   wo die Spalten untereinander stehen, klebte das Bild deshalb ohne jeden
   Abstand am Knopf darueber. */
.hero .wp-block-columns,
.pagehead--media .wp-block-columns,
.mediatext.wp-block-columns { gap: var(--space-l) 11.44%; }

/* WordPress nimmt Spalten mit eigener Breite aus dem Wachstum. Ohne diese
   Zeile verteilen sie den Rest unter sich und werden breiter als gedacht. */
.wp-block-column[style*="flex-basis"] { flex-grow: 0; }
.mediatext__body > * + * { margin-top: var(--space-s); }

/* Die Zeilenbegrenzung der Überschriften entfällt im Theme.
   In der statischen Fassung war 14ch auf den konkreten Satz gemünzt; im
   Theme steht dort beliebiger Text, und eine harte Grenze zerlegt ihn in
   Wortstapel. Die Spalte begrenzt bereits, das genügt. */
.hero .hero__title,
.pagehead .pagehead__title { max-width: none; }

/* ---------- Bilder: die Kontur als Blockstil ---------- */
/* register_block_style() in functions.php erzeugt is-style-form-1 bis -6.
   Die Werte selbst stehen oben in :root. */
.wp-block-image.is-style-form-1 { --shape-image: var(--shape-organic-1); }
.wp-block-image.is-style-form-2 { --shape-image: var(--shape-organic-2); }
.wp-block-image.is-style-form-3 { --shape-image: var(--shape-organic-3); }
.wp-block-image.is-style-form-4 { --shape-image: var(--shape-organic-4); }
.wp-block-image.is-style-form-5 { --shape-image: var(--shape-organic-5); }
.wp-block-image.is-style-form-6 { --shape-image: var(--shape-organic-6); }
.wp-block-image.is-style-rechteck { --shape-image: 0; }

.wp-block-image img,
.wp-block-post-featured-image img {
  border-radius: var(--shape-image);
  /* Ohne die eigene Ebene rechnet Safari den Beschnitt an der gebogenen
     Kante bei jedem Scrollschritt neu und die Kante flimmert. */
  will-change: transform;
}
/* Der Bildblock der Bühne und der Seitenköpfe steht quadratisch, damit die
   Kontur nicht von der Textlänge abhängt. Begründung im Entscheidungslog.
   Seit dem Durchgang über die mobile Ansicht gilt das auf jeder Breite: im
   Hochformat war das Bild auf dem Telefon eine Wand, die alles Weitere
   wegdrückte, und von der organischen Kontur blieb nichts zu sehen. */
.hero .wp-block-image img,
.pagehead--media .wp-block-image img,
.pagehead--media .wp-block-post-featured-image img { aspect-ratio: 1 / 1; object-fit: cover; width: 100%; }
/* Das Beitragsbild ist ein eigener Block und traegt die Stilklasse selbst,
   nicht auf einer figure darum. Ohne diese Zeile bleibt es rechteckig. */
.wp-block-post-featured-image.is-style-form-1 { --shape-image: var(--shape-organic-1); }
.wp-block-post-featured-image.is-style-form-2 { --shape-image: var(--shape-organic-2); }
.wp-block-post-featured-image.is-style-form-3 { --shape-image: var(--shape-organic-3); }
.wp-block-post-featured-image.is-style-form-4 { --shape-image: var(--shape-organic-4); }
.wp-block-post-featured-image.is-style-form-5 { --shape-image: var(--shape-organic-5); }
.wp-block-post-featured-image.is-style-form-6 { --shape-image: var(--shape-organic-6); }
.wp-block-post-featured-image { margin: 0; }
.wp-block-image figcaption {
  font-family: var(--font-accent);
  font-style: italic;
  font-size: 1.0625rem;
  color: var(--color-text-muted);
  line-height: 1.5;
  max-width: 40ch;
  margin-top: var(--space-s);
  text-align: left;
}

/* ---------- Knöpfe ---------- */
/* Die Klassen btn, btn--primary und btn--quiet sitzen bei WordPress auf dem
   Rahmen (.wp-block-button), nicht auf dem Anker. Ohne diese Zeilen greifen
   die Regeln des Fundaments ein zweites Mal auf dem Rahmen: der Hauptknopf
   trug Polsterung und Fläche doppelt und war damit ungefähr doppelt so
   gross wie vorgesehen, und unter dem leisen Knopf standen zwei rote
   Linien statt einer. Der Rahmen ist hier nur eine Hülle. */
.wp-block-button.btn {
  padding: 0;
  border: 0;
  background: none;
}

.wp-block-button.btn .wp-block-button__link {
  font-family: var(--btn-font);
  font-size: var(--btn-size);
  font-weight: var(--btn-weight);
  letter-spacing: var(--btn-tracking);
  text-transform: var(--btn-transform);
  padding: var(--btn-padding);
  border-radius: var(--btn-radius);
  background: var(--btn-bg);
  color: var(--btn-color);
  border: 1px solid var(--btn-border);
  transition: background var(--duration) var(--ease), color var(--duration) var(--ease),
              border-color var(--duration) var(--ease);
}
.wp-block-button.btn .wp-block-button__link:hover {
  background: var(--btn-bg-hover);
  color: var(--btn-color-hover);
  border-color: var(--btn-border-hover);
}
/* Der leise Knopf ist kein Knopf, sondern eine unterstrichene Zeile. */
.wp-block-button.btn--quiet .wp-block-button__link {
  background: none;
  color: var(--color-text);
  padding-inline: 0;
  padding-block: 0.55em;
  border: 0;
  border-bottom: 1px solid color-mix(in oklab, var(--color-accent) 45%, transparent);
}
.wp-block-button.btn--quiet .wp-block-button__link:hover {
  background: none;
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
}
.section--invert .wp-block-button.btn--primary .wp-block-button__link {
  background: transparent;
  color: var(--color-text-invert);
  border-color: var(--btn-border-invert);
}
.section--invert .wp-block-button.btn--primary .wp-block-button__link:hover {
  background: var(--color-text-invert);
  color: var(--color-bg-invert);
  border-color: var(--color-text-invert);
}

/* ---------- Kopfzeile ---------- */
/* Die statische Fassung setzt die Wortmarke aus zwei Spans in einer
   inline-flex-Box; deren Zeilenbox wird 67 Pixel hoch. WordPress liefert
   stattdessen ein h1 mit einem eigenen Absatz daneben, und dieselbe Schrift
   ergibt 41 Pixel. Ohne Ausgleich waere die Kopfzeile 109 statt 130 Pixel
   hoch und alles darunter um 21 Pixel verschoben.
   Der Ausgleich sitzt bewusst an der Marke, nicht an der Polsterung der
   Kopfzeile: so bleibt der Abstand zum Fensterrand derselbe. */
.site-header .brand { min-height: 4.1875rem; align-items: baseline; }
/* Nur am Desktop. Mobil steht die Wortmarke keinem zweiten Text gegenueber,
   sondern einem Symbol, und die 67 Pixel sind dann leerer Raum unter der
   Schrift: die Kopfzeile mittet das Symbol gegen den Kasten, nicht gegen die
   Zeile, und der Hamburger sass vierzehn Pixel zu tief. */
@media (max-width: 47.999rem) {
  .site-header .brand { min-height: 0; }
}


/* Die Marke ist der Seitentitel. Der Punkt am Ende ist der einzige
   Farbakzent der Kopfzeile und kommt aus dem Stylesheet, damit die
   Redaktion ihn nicht versehentlich mit eintippt. */
.site-header .wp-block-site-title {
  font-family: var(--font-heading);
  font-size: clamp(1.5rem, 1.3rem + 0.7vw, 1.875rem);
  font-weight: 400;
  letter-spacing: -0.005em;
  /* Keine feste Zeilenhoehe: der Punkt am Ende steht auf 1.25em und zieht die
     Zeilenbox auf. Genau das macht die Kopfzeile in der statischen Fassung 130
     statt 105 Pixel hoch. Mit line-height: 1.1 waere sie flacher und alles
     darunter um 25 Pixel verschoben. */
  line-height: normal;
  margin: 0;
}
.site-header .wp-block-site-title a { color: var(--color-text); border: 0; }
.site-header .wp-block-site-title a:hover { color: var(--color-accent); }
.site-header .wp-block-site-title a::after {
  content: ".";
  color: var(--color-accent);
  font-size: 1.25em;
  margin-left: -0.05em;
}
.site-header .wp-block-site-tagline {
  font-family: var(--font-accent);
  font-style: italic;
  font-size: var(--text-small);
  color: var(--color-text-muted);
  margin: 0;
}
@media (max-width: 59.999rem) { .site-header .wp-block-site-tagline { display: none; } }

.site-header .wp-block-navigation {
  font-size: var(--text-small);
  letter-spacing: var(--tracking-nav);
}
.site-header .wp-block-navigation a { color: var(--color-text); border: 0; }
.site-header .wp-block-navigation a:hover { color: var(--color-accent); }
.site-header .wp-block-navigation .current-menu-item a {
  border-bottom: 1px solid var(--color-accent);
  padding-bottom: 0.2em;
}
.site-header .wp-block-navigation { --wp--style--block-gap: var(--space-l); }

/* Mobil bleibt die Navigation in der Zeile der Wortmarke.
   Das Fundament klappt sein eigenes Menü unter die Kopfzeile: .site-nav
   bekommt dort flex-basis 100%, eine Haarlinie darüber und Abstand. Im Theme
   trägt der Navigation-Block dieselbe Klasse, und weil er sein Symbol selbst
   mitbringt, sprang der Hamburger in eine zweite Zeile unter einen Strich,
   der quer über die halbe Seite lief. Der Block klappt selbst auf, er
   braucht die Regeln des Fundaments nicht. */
@media (max-width: 47.999rem) {
  .site-header .wp-block-navigation.site-nav {
    display: flex;
    flex-basis: auto;
    margin-top: 0;
    padding-block: 0;
    border-top: 0;
  }
}

/* Der Navigation-Block klappt bei 600 Pixeln um, das Fundament bei 768.
   Zwischen den beiden Marken stand die fertige Liste da, aber noch mit den
   Regeln des eingeklappten Menues: fuenf Eintraege untereinander neben der
   Wortmarke. Fuer den Block gilt deshalb seine eigene Marke. */
@media (min-width: 37.5rem) {
  .site-header .wp-block-navigation.site-nav .wp-block-navigation__container {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
  }
  .site-header .wp-block-navigation.site-nav a {
    font-size: var(--text-small);
    letter-spacing: var(--tracking-nav);
  }
}

/* ---------- Fusszeile ---------- */
.site-footer .wp-block-navigation { --wp--style--block-gap: var(--space-2xs); }
.site-footer .wp-block-site-title { font-family: var(--font-heading); font-weight: 400; }

/* ---------- Listen ---------- */
/* Die Leistungszeilen sind Group-Blöcke, keine Listenelemente. Das Raster
   und die Linien kommen aus dem Fundament oben, hier nur der Anschluss. */
.wp-block-group.services { display: grid; gap: 0; container-type: inline-size; }
.wp-block-group.service { margin-block: 0; }
.wp-block-group.postlist, .wp-block-post-template.postlist { display: grid; gap: 0; }

/* ---------- Galerie ---------- */
/* Die Grossansicht macht die Lightbox des Bildblocks. Sie bringt Escape,
   Fokusverwaltung und Blättern mit, ohne dass das Theme ein Skript lädt. */
.wp-block-gallery.gallery { gap: var(--space-s); }
.wp-block-gallery.gallery .wp-block-image img { aspect-ratio: 1 / 1; object-fit: cover; }

/* ---------- Zitat ---------- */
/* Im Fundament ist .pullquote eine <figure> mit einem <blockquote> darin.
   WordPress setzt beide Klassen auf dasselbe <blockquote>. Ohne diese Zeilen
   steht das Zitat in der Textschrift statt in der Serife. */
blockquote.wp-block-quote.pullquote {
  font-family: var(--font-heading);
  font-style: italic;
  font-size: var(--text-h3);
  line-height: 1.28;
  color: var(--color-text);
  border: 0;
  padding: 0;
  max-width: 30ch;
}
blockquote.wp-block-quote.pullquote p::before { content: "\201E"; margin-left: -0.52em; color: var(--color-accent); }
blockquote.wp-block-quote.pullquote p::after  { content: "\201C"; color: var(--color-accent); }
blockquote.wp-block-quote.pullquote cite {
  display: block;
  margin-top: var(--space-s);
  font-family: var(--font-label);
  font-style: normal;
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* ---------- Beitrag ---------- */
.wp-block-post-content.article > * + * { margin-top: var(--space-s); }
