/* layout.css — Container, Header/Navigation, Sektionsgerüst, Bildraster. */

/* ── Container ─────────────────────────────────────────────────────────── */
.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--sec-pad-x);
}
/* ALLE Container haben DIESELBE Obergrenze --maxw. Das ist die Einfrierbreite
   der Seite (1440 px) und muss zum clamp()-Endpunkt in base.css passen: Ein
   Container, der früher stehenbleibt als die Schrift, lässt den Text beim
   Ziehen des Fensters weiter umbrechen, obwohl die Box längst fix ist. Genau
   das war zwischen 1570 (altes --maxw) und 1660 (clamp) der Fall. Beide stehen
   jetzt auf 1440.

   Die Bildleiste steht weiter außen als der übrige Inhalt — das macht sie über
   WENIGER Innenabstand, nicht über eine größere max-width. Sonst hätte sie
   einen zweiten Einfrierpunkt.

   DREI STUFEN MIT GLEICHEM SCHRITT — und deshalb hat auch „Normal" eine eigene
   Klasse. Vorher war „Normal" das nackte .wrap und schöpfte --maxw voll aus;
   „Weit" blieb damit nur noch der Innenabstand als Spielraum, also drei Prozent
   Unterschied, während nach „Schmal" die Breite auf die Hälfte fiel. Der
   Betreiber hatte faktisch zwei Stufen und eine Attrappe.

   DIE DREI ZAHLEN STEHEN NICHT MEHR HIER, sondern in schemaFields()
   (layout.wrap_narrow / _normal / _wide) und kommen als nackte Zahlen aus
   settingsCssDecls(). Daher die Rechnung /100 statt eines Faktors: ein
   'px'-Feld hänge eine Einheit an und wäre in der Multiplikation ungültig.

   Die Rückfälle in var(…, 76) sind kein zweiter Wahrheitsort, sondern der Fall
   „Stylesheet ohne Settings-Block": die Prüfstände unter tools/ laden
   layout.css einzeln, und ohne Rückfall wäre die ganze Deklaration ungültig
   und die Stufe verschwände.

   Vorgabe 55/76/100 — eine geometrische Reihe mit ≈ ×1,45 je Schritt, Anteile
   von --maxw und damit mitwandernd. Bei 1440 px sind das 688 / 990 / 1414 px
   Inhalt, bei 2400 px 1216 / 1720 / 2374 px. Wer eine Zahl ändert, zieht die
   anderen mit: der gleichmäßige Schritt IST die Eigenschaft.

   Die Decke bleibt --maxw, und zwar im Schema (max => 100). „Weit" holt sich
   seinen Vorsprung weiterhin über den Innenabstand und nie über eine
   größere max-width — sonst wäre genau der zweite Einfrierpunkt zurück, vor
   dem der Absatz darüber warnt. */
.wrap--normal { max-width: calc(var(--maxw) * var(--wrap-normal, 76) / 100); }
.wrap--narrow { max-width: calc(var(--maxw) * var(--wrap-narrow, 55) / 100); }
.wrap--xwide  { max-width: calc(var(--maxw) * var(--wrap-xwide,  88) / 100); }
/* Nur „Weit" rückt AUCH über den Seitenrand nach außen — das ist die Stufe
   der Bildleiste. „Erweitert" ist eine reine Breitenstufe und behält den
   normalen Rand; bekäme sie ihn auch, läge sie näher an „Weit" als an
   „Normal" und wäre als eigene Stufe kaum noch zu erkennen. */
.wrap--wide   { max-width: calc(var(--maxw) * var(--wrap-wide, 100) / 100);
                padding-inline: calc(var(--sec-pad-x) * 0.25); }

/* ── Sektion ───────────────────────────────────────────────────────────── */
/* Padding laeuft ueber --pad-t/--pad-b statt direkter Werte, damit das eine
   Regel bleibt statt einer pro Sektionstyp. Seit die Sektionen gerade
   getrennt werden (DEV-LOG [26]), ist das wieder eine schlichte Addition —
   es gibt keine Kante mehr, die der Inhalt freihalten müsste. */
.sec {
  position: relative;
  background: var(--sec-bg);
  color: var(--sec-fg);
  padding-top: calc(var(--pad-t, var(--sec-pad-y)) + var(--pad-off, 0px));
  padding-bottom: var(--pad-b, var(--sec-pad-y));
  isolation: isolate;
}

/* Seite ohne Keyvisual (Unterseiten aus dem Baukasten): der Kopf liegt über
   der ersten Sektion und würde sie anschneiden. --pad-off ADDIERT den Platz,
   statt --pad-t zu ersetzen — sonst schlüge diese Regel den am Segment
   eingestellten Abstand nieder, und "Abstand oben: kein" hätte keine Wirkung
   mehr (DEV-LOG [39]). */
.main--nohero > .sec:first-child { --pad-off: var(--header-h); }
.sec > .wrap { position: relative; z-index: 1; }

/* Mindesthöhe am Segment (segmentProps()['minh'], DEV-LOG [52]).

   Die Flex-Spalte gehört dazu und ist nicht Zierat: ein Block-Container
   verteilt übrige Höhe nicht, der Inhalt klebte in einem hohen Segment oben.
   Bei "Automatisch" ist die ganze Regel wirkungslos — --seg-minh wird dann
   gar nicht ausgegeben, und im Fluss liegt genau EIN Kind (.wrap); .sec-bg
   und .sec-wedge sind absolut positioniert. Mit tools/measure.php
   gegengeprüft: die Geometrie der Startseite ändert sich um keinen Pixel. */
.sec--seg {
  display: flex; flex-direction: column; justify-content: center;
  min-height: var(--seg-minh, 0px);
}

/* Hintergrundbild / Slideshow hinter dem Inhalt */
.sec-bg { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.sec-bg img { width: 100%; height: 100%; object-fit: cover; }
.sec-bg__ov { position: absolute; inset: 0; background: var(--ci-black); }

/* Unschaerfe, Abdunklung, Verlauf, Grain, Muster: fuenf Ebenen ueber dem
   Medium, in DIESER Folge im Markup (renderBackgroundOverlay()) und damit in
   dieser Folge gezeichnet. Alles ueber der Unschaerfe bleibt selbst scharf.

   UNSCHAERFE ALS backdrop-filter AUF EINER EIGENEN FLAECHE, nicht als filter
   am Medium: sie zeichnet weich, was darunter liegt — Bild, Diaschau und
   Video gleich, ohne Eingriff in slideshow.css (dort liegt transform fuer den
   Zoom schon am Bild). filter: blur() am Medium franste ausserdem die Raender
   aus, und der Sektionsgrund schiene als heller Saum durch. backdrop-filter
   spiegelt am Rand; .sec-bg bzw. .hero-media schneiden ab. DEV-LOG [91]. */
.sec-bg__bl {
  position: absolute; inset: 0;
  -webkit-backdrop-filter: blur(var(--sec-blur, 0px));
  backdrop-filter: blur(var(--sec-blur, 0px));
}

/* Der Verlauf von einer Ecke oder Seite bis zur Mitte. Die FORM steht hier,
   PHP liefert nur Richtung (Whitelist aus segmentProps()['bggr']) und Token.
   "to bottom right … 50 %" endet genau auf der Linie durch die Mitte, "to
   right … 50 %" genau in der Mitte. Die Deckkraft am Rand ist opacity auf der
   Flaeche — dieselbe Bauform wie die Abdunklung. */
.sec-bg__gr {
  position: absolute; inset: 0;
  background: linear-gradient(var(--sec-gr-dir, to right), var(--sec-gr-c, transparent), transparent 50%);
}

/* Grain (DEV-LOG [140]) — ueber dem Verlauf, unter dem Muster. Graues
   Rauschen, gemischt mit "Weiches Licht": Mittelgrau ist neutral, das Korn
   hellt und dunkelt um den Mittelton, Schwarz bleibt schwarz. Die Staerke ist
   opacity, gesetzt von renderBackgroundOverlay().
   DIE TEXTUR IST EIN SVG (feTurbulence, 200 px Kachel, nahtlos per
   stitchTiles) als Data-URI: rund 500 Byte statt eines PNG, keine Anfrage,
   img-src erlaubt data:. Die Farbmatrix macht es grau, Deckkraft 1, und
   streckt den Kontrast um den Mittelton (0,5 je Kanal, -0,25).
   ANIMIERT springt die uebergrosse Flaeche (inset -200px, eine Kachel) mit
   FESTEN 25 Hz (Vorgabe des Betreibers, alle Videos laufen mit 25 fps):
   24 Versaetze in 0,96 s, steps(1) — Rate = Versaetze / Dauer. Wer die
   Rate aendert, aendert die Dauer, nicht die Zahl der Keyframes; weniger
   Versaetze machten die Schleife sichtbar (8 wiederholten sich 3x je s).
   Die Versaetze sind gestreut und decken sich modulo Kachel (200 px) in
   keinem Paar auf beiden Achsen — sonst stuende dasselbe Korn zweimal da.
   Nur transform — laeuft auf dem Compositor, kein JavaScript, kein rAF.
   Nicht im Gleichtakt mit dem Video (das ginge nur per JavaScript), nur
   gleich schnell.
   --sec-gn-an ist der NAME der Animation (Whitelist segmentProps()['bggn']),
   'none' = stehendes Korn; so schaltet der Mobilblock um, ohne Klasse.
   Eigenes overflow: hidden — .hero-media schneidet nicht ab, und der
   springende Ueberstand ragte sonst aus der Sektion.
   Der Stapelkontext von .sec-bg / .hero-media (z-index: 0) begrenzt die
   Mischung auf den Hintergrund; der Text darueber wird nicht gemischt. */
.sec-bg__gn {
  position: absolute; inset: 0; overflow: hidden; pointer-events: none;
  mix-blend-mode: soft-light;
}
.sec-bg__gn::before {
  content: ""; position: absolute; inset: -200px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='.5 .5 .5 0 -.25 .5 .5 .5 0 -.25 .5 .5 .5 0 -.25 0 0 0 0 1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E") 0 0 / 200px 200px;
  animation: var(--sec-gn-an, none) .96s steps(1) infinite;
}
@keyframes ck-grain {
  0%       { transform: translate(0, 0); }
  4.167%   { transform: translate(-80px, 190px); }
  8.333%   { transform: translate(-160px, 100px); }
  12.5%    { transform: translate(160px, -90px); }
  16.667%  { transform: translate(-170px, 150px); }
  20.833%  { transform: translate(70px, -150px); }
  25%      { transform: translate(-20px, -130px); }
  29.167%  { transform: translate(-110px, -20px); }
  33.333%  { transform: translate(-160px, 10px); }
  37.5%    { transform: translate(-50px, -190px); }
  41.667%  { transform: translate(-20px, 30px); }
  45.833%  { transform: translate(-40px, -40px); }
  50%      { transform: translate(-90px, -100px); }
  54.167%  { transform: translate(-10px, -40px); }
  58.333%  { transform: translate(30px, -140px); }
  62.5%    { transform: translate(190px, 120px); }
  66.667%  { transform: translate(100px, 140px); }
  70.833%  { transform: translate(-90px, 70px); }
  75%      { transform: translate(-70px, 150px); }
  79.167%  { transform: translate(-130px, -100px); }
  83.333%  { transform: translate(-110px, 10px); }
  87.5%    { transform: translate(120px, 30px); }
  91.667%  { transform: translate(60px, -60px); }
  95.833%  { transform: translate(10px, -170px); }
}
@media (prefers-reduced-motion: reduce) {
  .sec-bg__gn::before { animation: none; }
}

/* Das Muster (DEV-LOG [138]) — oberste Ebene. Die Flaeche traegt die Farbe
   (Token, Regel 2), das SVG aus lib/pattern.php ist nur ihre MASKE. 16:9 und
   'cover' + mittig wie das Hintergrundvideo: die freie Mitte sitzt dort, wo
   das Video seine Mitte hat.
   LANGFORMEN, KEINE mask-Kurzform mit var(): fehlte die Variable, waere die
   ganze Deklaration ungueltig, und eine deckende Farbflaeche bliebe stehen.
   Der Rueckfall ist eine durchsichtige Maske = nichts zu sehen.
   ZWEITE EBENE (DEV-LOG [141]): der Verlauf zu einer Kante (--sec-pt-f,
   patternFadeValue()), geschnitten mit dem SVG (composite). Er misst an
   dieser Flaeche, nicht am beschnittenen SVG — die % sind die der Sektion.
   Ohne Verlauf eine deckende Ebene, die nichts wegnimmt.
   Die Regel steht als Kopie in settings.css (.ap-patpv) — der Selbsttest haelt
   beide gleich.
   .mtile__pt (DEV-LOG [146]) ist dieselbe Flaeche in einer Videothumb-Kachel,
   eingeblendet beim Hover (vthumb.css) — dieselbe Maske, darum dieselbe Regel.
   Eine eigene Klasse, weil die Segmentregeln (.ck-b-x .sec-bg__pt) sie sonst
   mit traefen. */
.mtile__pt, .sec-bg__pt {
  position: absolute; inset: 0; pointer-events: none;
  background: var(--sec-pt-c, var(--ci-1));
  -webkit-mask-image: var(--sec-pt-m, linear-gradient(transparent, transparent)),
                      var(--sec-pt-f, linear-gradient(#000, #000));
          mask-image: var(--sec-pt-m, linear-gradient(transparent, transparent)),
                      var(--sec-pt-f, linear-gradient(#000, #000));
  -webkit-mask-size: cover, 100% 100%; mask-size: cover, 100% 100%;
  -webkit-mask-position: center;       mask-position: center;
  -webkit-mask-repeat: no-repeat;      mask-repeat: no-repeat;
  -webkit-mask-composite: source-in;   mask-composite: intersect;
}

/* ── Parallax (DEV-LOG [112]) ──────────────────────────────────────────────
   Bild bzw. Diaschau laufen beim Scrollen langsamer als der Inhalt:
   CSS-Scroll-Animation, kein JavaScript, kein rAF — läuft auf dem Compositor.
   --sec-par (der Weg in svh, segmentProps()['bgpar'] samt Mobil-Zwilling)
   und die Klasse .sec-bg--par setzt renderBackgroundParallax().

   DIE ZEITLEISTE HÄNGT AN DER SEKTION, NICHT AM BILD. view() sucht den
   nächsten Scroll-Container über seinem Element, und .sec-bg ist mit
   overflow: hidden selbst einer — die Animation stünde still. Die Sektion hat
   den Viewport als Scroller (auch der Hero, obwohl er selbst overflow: hidden
   trägt: gesucht wird ÜBER dem Element), und ihr Name gilt für alle
   Nachfahren.

   Überstand oben und unten um den Weg; der Hintergrund fährt von -Weg nach
   +Weg, bleibt also relativ zur Sektion zurück (nach unten). Unschärfe,
   Abdunklung und Verlauf liegen darüber und bewegen sich nicht.

   Ohne Unterstützung (Firefox, Stand 2026) oder bei reduzierter Bewegung
   steht der Hintergrund wie immer: nichts außerhalb dieses Blocks ändert
   sich, auch kein Überstand. Die Geometrie der Seite bleibt gleich — alles
   liegt absolut in .sec-bg. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* inset 0: ohne Angabe rechnet die Zeitleiste scroll-padding-top (die
       Kopfhoehe, base.css) als "ausserhalb" — der Hero stuende bei
       Scrollposition 0 schon verschoben. */
    .sec:has(> .sec-bg--par) { view-timeline-name: --ck-par; view-timeline-inset: 0; }

    .sec-bg--par > img,
    .sec-bg--par > .ss {
      position: absolute; left: 0; right: auto; bottom: auto;
      width: 100%;
      top: calc(var(--sec-par, 0svh) * -1);
      height: calc(100% + var(--sec-par, 0svh) * 2);
      /* Der Kurzschreiber setzt timeline und range zurück — sie stehen danach. */
      animation: ck-par linear both;
      animation-timeline: --ck-par;
    }

    /* DER HERO NUR BEIM VERLASSEN: er kommt nie von unten herein, und mit dem
       vollen Ablauf stünde das Bild beim Laden schon halb verschoben. So steht
       es bei Scrollposition 0 genau wie ohne Parallax. Weg 0 → +Weg über die
       halbe Strecke fühlt sich wie dieselbe Stufe im Segment an; Überstand
       deshalb nur oben — das Hero-Bild ist das größte beim Laden. */
    .sec--hero > .sec-bg--par > img,
    .sec--hero > .sec-bg--par > .ss {
      height: calc(100% + var(--sec-par, 0svh));
      animation-name: ck-par-hero;
      animation-range: exit;
    }
  }
}
@keyframes ck-par {
  from { transform: translateY(calc(var(--sec-par, 0svh) * -1)); }
  to   { transform: translateY(var(--sec-par, 0svh)); }
}
@keyframes ck-par-hero {
  from { transform: translateY(0); }
  to   { transform: translateY(var(--sec-par, 0svh)); }
}

/* Die Diaschau-Folien stehen seit 2026-08-24 in assets/css/slideshow.css —
   sie werden auch im Bedienfeld gebraucht (Reiter „Diaschauen"), und dort gibt
   es dieses Stylesheet nicht. Eine zweite Fassung waere ein zweites Aussehen,
   das nachgezogen werden muesste. */

/* ── Keil ──────────────────────────────────────────────────────────────────
   Der einzige nicht-gerade Sektionsübergang: liegt am unteren Rand des Hero
   und ist mit der Farbe der FOLGENDEN Sektion gefüllt. Dadurch muss keine
   Sektion ihre Nachbarn kennen. Höhe und Form sind fest, siehe sections.css
   und HERO_WEDGE_POINTS.                                                    */
.sec-wedge {
  position: absolute;
  left: -1px; right: -1px; bottom: -1px;
  height: var(--wedge-h, 0px);
  /* -1px an allen Kanten: schliesst die Haarlinien, die durch
     Subpixel-Rundung zwischen zwei Flaechen entstehen. */
  z-index: 2;
  pointer-events: none;
}
.sec-wedge svg { width: 100%; height: 100%; display: block; }

/* ── Header ────────────────────────────────────────────────────────────── */
.site-header {
  position: absolute;
  inset-inline: 0; top: 0;
  z-index: 100;
  height: var(--header-h);
  transition: height 0.25s ease, background-color 0.25s ease;
}
.site-header.is-sticky { position: fixed; }

/* Unter 820 px steht die Seite still und scrollt seitlich (base.css,
   DEV-LOG [127]). Der Kopf misst mit inset-inline:0 aber am FENSTER, nicht an
   der Seite — er braucht die Mindestbreite selbst. Fixiert wandert er ueber
   --hx (nav.js, scrollX) mit, sonst stuenden die Menuepunkte rechts ausserhalb
   und liessen sich nicht erreichen. left statt transform: ein transform am Kopf
   machte ihn zum umschliessenden Block des mobilen Vollbildmenues.
   820 = LAYOUT_MIN_W, 567 = BP_MOBILE + 1 (lib/schema.php). */
@media (min-width: 567px) {
  .site-header { min-width: 820px; }
  /* width: 100% MUSS dazu: mit right:auto und ohne Breite waere die fixierte
     Leiste nur so breit wie ihr Inhalt, und die Mindestbreite wuerde zur
     festen Breite - 820 px auch bei 1920 (so passiert, DEV-LOG [127]). */
  .site-header.is-sticky { left: calc(0px - var(--hx, 0px)); right: auto; width: 100%; }
}

/* Über dem Hero liegt die Leiste transparent. Sobald der Seiteninhalt darunter
   durchläuft, würden die weißen Menütexte darin verschwinden — ab ~40 px
   Scrollweg setzt nav.js deshalb .is-scrolled. --nav-fg leitet templates/
   header.php aus --nav-bg ab (paletteAutoInk), damit die Unterstreichung nicht
   im Hintergrund verschwindet. */
.site-header.is-scrolled {
  background: var(--nav-bg);
  height: var(--header-h-s);
}
.site-header.is-scrolled .site-nav__link { color: var(--nav-fg); }
.site-header.is-scrolled .site-nav__link::after { background: var(--nav-fg); }
.site-header.is-scrolled .nav-toggle__bar { background: var(--nav-fg); }

.site-header__inner {
  height: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--sec-pad-x);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

.site-logo img {
  height: var(--logo-h);
  width: auto;
  transition: height 0.25s ease;
  /* Das Logo ist weiß mit orangem Schlagschatten und braucht keinen Kasten. */
}
/* Das Logo darf das schmalere Band nicht sprengen. */
.site-header.is-scrolled .site-logo img { height: min(var(--logo-h), calc(var(--header-h-s) - 18px)); }

.site-nav__list { display: flex; align-items: center; gap: 1.6rem; }
.site-nav__link {
  color: var(--ci-white);
  position: relative;
  padding-block: 0.4rem;
}
.site-nav__link::after {
  content: '';
  position: absolute; left: 0; right: 100%; bottom: 0;
  height: 2px; background: var(--ci-1);
  transition: right 0.22s ease;
}
.site-nav__link:hover::after,
.site-nav__link:focus-visible::after { right: 0; }

/* Burger */
.nav-toggle {
  display: none;
  width: 44px; height: 44px;
  padding: 10px;
  flex-direction: column; justify-content: space-between;
  z-index: 2;
}
.nav-toggle__bar {
  display: block; height: 2px; width: 100%;
  background: var(--ci-white);
  transition: transform 0.25s, opacity 0.2s;
  transform-origin: center;
}
.nav-open .nav-toggle__bar:nth-child(1) { transform: translateY(10px) rotate(45deg); }
.nav-open .nav-toggle__bar:nth-child(2) { opacity: 0; }
.nav-open .nav-toggle__bar:nth-child(3) { transform: translateY(-10px) rotate(-45deg); }

/* ── Bildcontainer ─────────────────────────────────────────────────────────
   Ein Container ist Raster × Seitenverhältnis, alle Kacheln gleich groß
   (siehe layoutGrids()/layoutRatios() in lib/blocks.php). Hier steht keine
   einzige layoutspezifische Regel — der Renderer liefert nur --g-cols und
   --tile-ratio.

   Die HÖHE kommt von den Kacheln, nicht vom Container: .mgrid trug früher ein
   aspect-ratio des Gesamtrasters. Das stimmt nur, solange der Abstand
   proportional mitskaliert — er steht aber in px. Mit aspect-ratio auf der
   Kachel folgt die Rasterhöhe exakt, bei jedem Abstand und jeder Breite.    */
/* min-width:0 ist hier nicht optional: Grid- und Flex-Kinder haben
   min-width:auto und schrumpfen NICHT unter ihre Inhaltsbreite. Der
   Swipe-Slider würde seinen Elternknoten sonst auf ein Vielfaches der
   Viewportbreite aufblasen, statt selbst zu scrollen — die Seite läuft dann
   rechts aus dem Bild. */
.mseg > *, .ft > *, .mgrid { min-width: 0; }

.mgrid {
  display: grid;
  grid-template-columns: repeat(var(--g-cols), 1fr);
  gap: var(--grid-gap);
  /* Wie breit die Box in ihrer Spalte steht (cellProps()['boxw'], E12).
     --box-w gibt renderGrid() nur aus, wenn es NICHT 100 % sind — dieselbe
     Konvention wie ueberall: der Normalzustand steht nicht in den Daten.
     Die HOEHE folgt daraus von selbst, denn sie kommt von den Kacheln
     (--tile-ratio an .mtile) und nicht vom Container. Genau deshalb greift
     hier die Breite und nicht eine Hoehenbegrenzung am Segment. */
  width: calc(100% * var(--box-w, 1));
}

/* WOHIN die verkleinerte Box rueckt, sagt die BUENDIGKEIT DER ZELLE — es gibt
   dafuer keine zweite Einstellung. 'margin-inline: auto' fest waere bequem und
   falsch: es zentrierte auch dort, wo die Spalte linksbuendig steht.

   Linksbuendig braucht keine Regel (0 auto ist der Normalfall eines Blocks mit
   Breite unter 100 %) — mobil aber schon, weil dort ein Desktopwert
   ueberschrieben werden muss. */
.mcell--a-center > .mgrid { margin-inline: auto; }
.mcell--a-end    > .mgrid { margin-inline: auto 0; }

.mtile {
  position: relative;
  overflow: hidden;
  /* DER GRUND, WARUM ES HIER NICHT SCHWARZ IST (DEV-LOG [55]).
     Bis dahin stand hier var(--ci-black). Fuer ein deckendes Bild ist das
     folgenlos — object-fit: cover fuellt die Kachel vollstaendig aus, die
     Farbe war nur waehrend des Ladens zu sehen. Ein freigestelltes PNG
     dagegen bekam damit einen SCHWARZEN Grund, und zwar unabhaengig davon,
     ob sein Derivat den Alphakanal behalten hat: die Transparenz war
     einwandfrei, dahinter lag nur Schwarz. Genau das sah aus wie ein Fehler
     in der Bildpipeline.
     Jetzt die Sektionsfarbe — dieselbe, die auch neben der Kachel liegt. */
  background: var(--sec-bg, transparent);
  aspect-ratio: var(--tile-ratio);
  min-width: 0; min-height: 0;   /* sonst sprengen Bilder die Grid-Zelle */
}
/* video mit gemeint: base.css setzt height:auto für alle Medien, das würde
   einen Video-Slot aus seiner Kachel schieben. */
.mtile img, .mtile video { width: 100%; height: 100%; object-fit: cover; }

/* Platzhalter: das Cyan aus der Vorlage, damit unbelegte Slots sofort
   auffallen. Wird ersetzt, sobald ein Bild zugewiesen ist. */
.mtile--empty { background: #1fffef; }
.mtile__ph { position: absolute; inset: 0; }

/* Video-Slots laufen stumm in Schleife und öffnen keine Lightbox. */
.mtile--video { cursor: default; }

/* Was die Lightbox öffnet, sieht auch danach aus. Die Bedingung ist DIESELBE
   wie in lightbox.js (SEL plus "hat ein <img>"): nur in einem Container mit
   .mgrid--lb, kein Video, keine leere Kachel. Ob ein Raster die Großansicht
   hat, entscheidet seit DEV-LOG [93] die Zelle (cellProps()['lb']) und damit
   .mgrid--lb allein; das .mtile--nolb je Kachel gibt es nicht mehr. Eine
   unbelegte Kachel zeigt sonst einen Zeigefinger für einen Klick, der nichts
   tut.
   body:not(.ck-edit) ist Absicht und keine Vorsicht: im Adminmodus öffnet der
   Klick den Editor, nicht die Großansicht. Die Regel dafür steht in admin.css
   (body.ck-edit .mtile), hat aber WENIGER Spezifität als dieser Selektor mit
   seinen Klassen — ohne den Ausschluss verlöre sie. */
body:not(.ck-edit) .mgrid--lb .mtile:not(.mtile--video):not(.mtile--empty) {
  cursor: pointer;
}

/* ── Videothumbs ──────────────────────────────────────────────────────────
   Stehen seit DEV-LOG [142] in assets/css/vthumb.css — Seite UND Bedienfeld
   laden sie (die Vorschau der Hover-Texte im Tab „Videos"), wie carousel.css. */

/* ── Karussell ────────────────────────────────────────────────────────────
   Steht seit DEV-LOG [108] in assets/css/carousel.css — Seite UND Bedienfeld
   laden sie (die Vorschau im Tab „Diaschauen"), wie slideshow.css. */

/* ── Mobil ─────────────────────────────────────────────────────────────────
   566px muss zu BP_MOBILE in lib/schema.php passen. Das Duplikat ist
   unvermeidbar: statische CSS-Dateien kennen keine PHP-Konstante.          */
@media (max-width: 566px) {
  .nav-toggle { display: flex; }

  .site-nav {
    position: fixed; inset: 0;
    background: var(--ci-black);
    display: grid; place-items: center;
    opacity: 0; visibility: hidden;
    transition: opacity 0.25s, visibility 0.25s;
  }
  .nav-open .site-nav { opacity: 1; visibility: visible; }
  .site-nav__list { flex-direction: column; gap: 1.8rem; }

  /* Auf Mobil braucht die Bildleiste denselben Rand wie alles andere — 25 %
     von 1.35rem wären 5 px und die Kacheln klebten am Displayrand.
     Die max-width-Stufen brauchen hier nichts: 792 bzw. 1094 px greifen unter
     BP_MOBILE ohnehin nie, alle drei Breiten sind dort dasselbe. */
  .wrap--wide { padding-inline: var(--sec-pad-x); }
  /* Das Off-Canvas-Panel hat seinen eigenen schwarzen Grund — dort gilt die
     Kontrastfarbe der Kopfleiste nicht (sie könnte hell sein und verschwände). */
  .site-header.is-scrolled .site-nav__link { color: var(--ci-white); }

  /* Voreinstellung 'keep': der Container bleibt, wie er ist, und skaliert am
     Stück auf Browserbreite unter Text und Button. Nichts umzuschalten.
     Nur die 6er-Leiste bekommt ein anderes --g-cols (2 statt 6) — das setzt
     renderGrid() über ckStyleM(), die Zeilen ergeben sich implizit. */

  /* Wischbarer Slider — MIT sichtbarem Scrollbalken. Ohne ihn sah man zwei
     Kacheln nebeneinander und keinen Hinweis darauf, dass es weitergeht;
     genau das wurde als kaputtes Layout gemeldet. */
  .mgrid--slider {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 78%;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    padding-bottom: 0.5rem;
  }
  .mgrid--slider > .mtile { scroll-snap-align: center; }

  .mgrid--stack { grid-template-columns: 1fr; }
  .mgrid--grid2 { grid-template-columns: 1fr 1fr; }

  /* Das Karussell mobil: assets/css/carousel.css (DEV-LOG [108]). */

  /* Die Ausrichtung der verkleinerten Bilderbox mobil (cellProps()['align_m']).
     Alle DREI Stufen brauchen hier eine Regel — anders als im Desktop-Block,
     wo 'linksbuendig' der Normalfall ist: mobil muss ein Desktopwert
     ueberschrieben werden koennen, und dafuer genuegt "keine Regel" nicht.
     'auto' gibt keine Klasse aus und laesst den Desktopwert damit stehen. */
  /* Die mobile Reihenfolge der SEGMENTE (E12). Die Klasse setzt layout.php
     nur, wenn eine Reihenfolge gespeichert ist; das 'order' je Sektion kommt
     aus sectionOpen() ueber ckStyleM(). Ohne beides bleibt <main> ein ganz
     normaler Block-Container.
     BEWUSSTER KOMPROMISS wie bei den Zellen: Tabreihenfolge und Screenreader
     folgen dem DOM, die Augen dem 'order' (WCAG 1.3.2). Deshalb wird auch nur
     die MITTE sortiert — Hero und Footer behalten ihren Platz. */
  .main--ordm { display: flex; flex-direction: column; }
  .main--ordm > .sec { min-width: 0; }

  .mcell--am-start  > .mgrid { margin-inline: 0 auto; }
  .mcell--am-center > .mgrid { margin-inline: auto; }
  .mcell--am-end    > .mgrid { margin-inline: auto 0; }
}

/* ── Lightbox ──────────────────────────────────────────────────────────── */
#ck-lightbox {
  position: fixed; inset: 0; z-index: 300;
  display: none;
  align-items: center; justify-content: center;
  gap: 1rem;
  padding: clamp(1rem, 4vw, 3rem);
  background: color-mix(in srgb, var(--ci-black) 92%, transparent);
}
.lb-open #ck-lightbox { display: flex; }
.lb-open { overflow: hidden; }

/* Die Bühne ist so breit wie das BILD (fit-content), damit die linksbündige
   Unterschrift an dessen linker Kante beginnt und nicht am Rand des
   Overlays. */
.lb__stage {
  display: flex; flex-direction: column;
  width: fit-content; max-width: min(100%, 1400px); max-height: 100%;
}
.lb__stage img { max-height: 82vh; max-width: 100%; width: auto; object-fit: contain; }
/* Ein VIDEO in der Großansicht (Videothumbs, DEV-LOG [137]): derselbe Player
   wie im Modul, so breit, wie das Fenster erlaubt, und nicht höher als ein
   Bild dort. Die Bühne ist fit-content — ohne eigene Breite fiele der Player
   auf seine Mindestgröße zusammen. */
.lb__vid { width: min(82vh * 16 / 9, 100vw - 2 * clamp(1rem, 4vw, 3rem) - 7rem, 1400px); }
.lb__vid[hidden], .lb__stage > img[hidden] { display: none; }
.lb__vid .vp { width: 100%; }

/* Das nächste Bild lädt noch (lightbox.js setzt .is-wait, DEV-LOG [71]).
   Das bisherige Bild bleibt samt Unterschrift stehen, nur zurückgenommen —
   der Klick ist damit sichtbar angekommen, und Bild und Text wechseln
   trotzdem gemeinsam. Der Ring sitzt am Overlay und nicht an der Bühne:
   beim ersten Öffnen hat die Bühne noch gar keine Größe. */
.lb__stage { transition: opacity 0.2s; }
#ck-lightbox.is-wait .lb__stage { opacity: 0.4; }
#ck-lightbox.is-wait::after {
  content: "";
  position: absolute; left: 50%; top: 50%;
  width: 2.2rem; height: 2.2rem; margin: -1.1rem 0 0 -1.1rem;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--ci-white) 25%, transparent);
  border-top-color: var(--ci-white);
  animation: lb-spin 0.8s linear infinite;
  pointer-events: none;
}
@keyframes lb-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  #ck-lightbox.is-wait::after { animation: none; }
}

/* Die Bildunterschrift. Sie ist bewusst NICHT einstellbar (Vorgabe des
   Betreibers) — sie erbt die Fließtext-Rolle aus der Typo-Registry und zieht
   damit automatisch nach, wenn der Betreiber Schrift oder Größe der Seite
   ändert. Ein eigener Satz Variablen wäre ein zweiter Ort für dieselbe Frage.

   Die FARBE kommt dagegen nicht von dort: --t-body-color steht auf "auto" und
   löst gegen --sec-fg auf, das es hier nicht gibt — die Lightbox ist keine
   Sektion. Vor der fast schwarzen Fläche ist Weiß ohnehin die einzige Wahl.

   width:0 + min-width:100% ist kein Trick um seiner selbst willen: ohne das
   ginge die Breite eines langen Textes in die fit-content-Breite der Bühne
   ein, und das Bild rückte neben seiner eigenen Unterschrift aus der Mitte.
   So trägt nur das Bild zur Breite bei, und der Text bricht auf genau dieser
   Breite um. Seit DEV-LOG [70] sitzt das an der figcaption (.lb__caps), die
   Unterschrift und Subline gemeinsam trägt.

   DIE GRÖSSE IST EIN FAKTOR AUF DIE FLIESSTEXT-ROLLE, KEIN EIGENER WERT
   (DEV-LOG [70]). Auf dem Desktop war volle Fließtextgröße unter einem Bild
   zu laut; mobil passte sie. Zwei Faktoren, zwei Breakpoints — die Schrift
   folgt damit weiter jeder Änderung an der Rolle, und mobil bleibt die
   Unterschrift so, wie sie war (Faktor 1, im @media-Block unten).
   Bemessen am Stand von theme.json (Fließtext 1.1rem): Desktop ~16,4 px, mobil
   ~14 px; die Subline ~13,8 px Desktop, ~12 px mobil. Kleiner als 0.85 hieße,
   die Desktop-Unterschrift fiele unter die mobile. 0.85 / 0.7 war dem
   Betreiber einen Tick zu klein (DEV-LOG [72]). */
#ck-lightbox { --lb-cap-k: 0.95; --lb-sub-k: 0.8; }
.lb__caps {
  width: 0; min-width: 100%;
  margin-top: 0.9rem;
  text-align: left;
  color: var(--ci-white);
  font-family: var(--t-body-family);
  font-weight: var(--t-body-weight);
  letter-spacing: var(--t-body-ls);
}
.lb__cap {
  font-size: calc(var(--t-body-size) * var(--lb-cap-k));
  line-height: var(--t-body-lh);
}
/* Die Subline — Fotocredit und dergleichen. Kleiner auf BEIDEN Breakpoints
   und etwas zurückgenommen, damit sie als Nachsatz und nicht als zweite
   Unterschrift gelesen wird. */
.lb__sub {
  font-size: calc(var(--t-body-size) * var(--lb-sub-k));
  line-height: 1.45;
  color: color-mix(in srgb, var(--ci-white) 72%, transparent);
}
.lb__cap:not([hidden]) + .lb__sub { margin-top: 0.35em; }
.lb__caps[hidden], .lb__cap[hidden], .lb__sub[hidden] { display: none; }
.lb__close {
  position: absolute; top: 1rem; right: 1.4rem;
  font-size: 2.4rem; line-height: 1; color: var(--ci-white);
}

/* DER FOKUS LIEGT AUF DEM DIALOG, NICHT AUF DEM × (DEV-LOG [51]).
   base.css gibt jedem :focus-visible einen Ring aus 2 px currentColor mit
   3 px Abstand — am Schliessknopf also WEISS um ein 2,4 rem grosses Zeichen,
   ein dicker Kasten mitten im Bild. Er erschien nur "ab und zu", weil show()
   den Fokus selbst setzt und Chrome einen programmatischen Fokus dann als
   :focus-visible wertet, wenn zuletzt die Tastatur im Spiel war (z. B. Escape
   beim vorigen Schliessen). Der Dialog nimmt den Fokus jetzt stumm auf; die
   Knöpfe behalten einen Ring, aber einen schmalen — wer mit der Tabulatortaste
   navigiert, muss sehen, wo er steht. */
#ck-lightbox:focus { outline: none; }
#ck-lightbox button:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--ci-white) 45%, transparent);
  outline-offset: 0; border-radius: 4px;
}
.lb__nav {
  font-size: 3rem; line-height: 1; color: var(--ci-white);
  padding: 0.4rem 0.9rem; opacity: 0.65; transition: opacity 0.15s;
}
.lb__nav:hover { opacity: 1; }

/* 566px muss zu BP_MOBILE in lib/schema.php passen. */
@media (max-width: 566px) {
  /* Mobil hat die Unterschrift die volle Fließtextgröße (war stimmig), die
     Subline bleibt kleiner (DEV-LOG [70]). */
  #ck-lightbox { --lb-cap-k: 1; --lb-sub-k: 0.85; }
  .lb__nav { position: absolute; bottom: 1.2rem; }
  .lb__prev { left: 1.2rem; }
  .lb__next { right: 1.2rem; }
}
