/*
 * Gemeinsame Mechanik für alle Bildflächen und Raster.
 * Markup dazu: site/snippets/media.php
 * Verhalten dazu: assets/js/media-reveal.js, assets/js/media-video.js
 *
 * Zwei Bausteine, die sich unabhängig voneinander kombinieren lassen:
 *
 *   .media   Bildfläche mit drei Ebenen. Blendet das scharfe Thumbnail über
 *            die unscharfe Mini-Vorschau, sobald es geladen ist (.is-loaded).
 *
 *   .reveal  Element, das gestaffelt erscheinen soll (.is-revealed). Sitzt in
 *            der Regel auf der ganzen Karte — Bild plus Titel — nicht auf der
 *            Bildfläche selbst.
 */

/* ====================================== */
/* BILDFLÄCHE                              */
/* ====================================== */

.media {
  --media-ratio: 16 / 9;
  /* Zoomfaktor der Bildebenen; einzelne Raster setzen ihn um (siehe
     .work-gallery--tiles), der Blur-Layer skaliert automatisch mit. */
  --media-zoom: 1;
  /* Zoom der ganzen Fläche beim Erscheinen. Bewusst über eine Variable und
     nicht direkt über transform, damit Hover-Effekte in den Templates das
     transform der Fläche überschreiben können, ohne mit dem Reveal zu
     kollidieren. */
  --media-reveal-zoom: 1;
  --media-bg: #f2f2f2;
  /* Wie die Ebenen die Fläche füllen. Als Variable, damit ein Raster den
     Zuschnitt umschalten kann, ohne jede der drei Ebenen einzeln zu
     überschreiben (siehe .gallery in gallery.css). */
  --media-fit: cover;
  /* Welcher Ausschnitt stehen bleibt, wenn das Bild grösser ist als die
     Fläche. Ist im Panel ein Fokuspunkt gesetzt, schreibt ihn
     snippets/media.php hier hinein; sonst die Mitte. */
  --media-position: 50% 50%;

  position: relative;
  display: block;
  overflow: hidden;
  width: 100%;
  margin: 0;
  /* Trägt die Höhe der Fläche, bevor Bilddaten da sind: kein Layout-Sprung,
     keine leere Kachel, und der Masonry-Polyfill kann sofort messen. */
  aspect-ratio: var(--media-ratio);
  background: var(--media-bg);
  line-height: 0;
  /* Transform beeinflusst kein Layout, und masonry.js messt über
     offsetHeight – die Animation kann die Zeilenhöhen also nicht
     verfälschen (anders als früher mit getBoundingClientRect). */
  transform: scale(var(--media-reveal-zoom));
  transition: transform 900ms cubic-bezier(0.16, 1, 0.3, 1);
}

.media__lqip,
.media__image,
.media__video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: var(--media-fit);
  object-position: var(--media-position);
}

.media--contain {
  --media-fit: contain;
}

/* Ebene 1: inline eingebettete 24-px-Vorschau (data:-URI, kein Request).
   Der Blur glättet die grobe Quelle, der zusätzliche Zoom verdeckt die
   weichen Ränder, die der Filter über die Kante hinaus zieht. */
.media__lqip {
  transform: scale(calc(var(--media-zoom) * 1.12));
  filter: blur(12px);
}

/* Ebene 2: scharfes Thumbnail, blendet über die Vorschau */
.media__image {
  opacity: 0;
  transform: scale(var(--media-zoom));
}

/* Ebene 3: Teaser-Loop, blendet über das Standbild */
.media__video {
  opacity: 0;
  transform: scale(var(--media-zoom));
  pointer-events: none;
}

.media__lqip,
.media__image {
  transition: opacity 500ms ease, transform 0.5s ease;
}

.media.is-loaded .media__image {
  opacity: 1;
}

.media.is-loaded .media__lqip {
  opacity: 0;
  /* Nach dem Crossfade nicht mehr zeichnen – dauerhaft aktive blur()-Filter
     kosten sonst GPU-Speicher, auf jeder Kachel einer langen Seite. */
  visibility: hidden;
  transition: opacity 500ms ease, visibility 0s linear 500ms;
}

.media.is-playing .media__video {
  opacity: 1;
  transition: opacity 400ms ease;
}

/* ====================================== */
/* GESTAFFELTES ERSCHEINEN                 */
/* ====================================== */

/*
 * Der versteckte Ausgangszustand hängt an html.js (Inline-Script im Header):
 * ohne JavaScript erscheint alles ganz normal, statt unsichtbar zu bleiben.
 */
.js .reveal {
  opacity: 0;
}

/* Notbremse ohne JavaScript-Beteiligung: sollte media-reveal.js gar nicht
   ankommen (Deployment-Fehler, blockiertes Script), wird der Inhalt nach
   6 s trotzdem sichtbar, statt dass die Seite leer bleibt.
   Nur solange das Element noch nicht erschienen ist: Firefox lässt eine
   wartende Animation auf opacity sonst mit dem Opacity-Übergang kollidieren
   – die Kachel blitzt auf, springt zurück auf 0 und kommt erst mit der
   Notbremse wieder. Mit .is-revealed fällt die Animation ganz weg. */
.js .reveal:not(.is-revealed) {
  animation: reveal-failsafe 0s linear 6s forwards;
}

.js .reveal.is-revealed {
  opacity: 1;
  animation: none;
}

@keyframes reveal-failsafe {
  to {
    opacity: 1;
  }
}

.reveal {
  transition: opacity 500ms ease;
}

/* Die Bildfläche rastet beim Erscheinen minimal ein */
.js .reveal .media {
  --media-reveal-zoom: 1.04;
}

.js .reveal.is-revealed .media {
  --media-reveal-zoom: 1;
}

@media (prefers-reduced-motion: reduce) {
  .js .reveal .media {
    --media-reveal-zoom: 1;
  }

  .media {
    transition: none;
  }

  .reveal,
  .media__lqip,
  .media__image,
  .media__video {
    transition-duration: 200ms;
  }
}
