/* ==========================================================================
   VISARO — Motion
   --------------------------------------------------------------------------
   Bewegung erklärt. Sie dekoriert nicht.

   Vier Dauern, drei Kurven — alle aus tokens.css. Bewegt werden
   ausschließlich `opacity`, `transform` und `clip-path`; das läuft im
   Compositor und erzeugt keinen Layout-Shift.

   Verboten und deshalb hier nicht vorhanden: Daueranimationen,
   Typewriter, Scramble Text, Scroll Hijacking, Cursor-Spielereien.

   Reduced Motion: Die Dauer-Tokens stehen dann bereits auf 1 ms
   (tokens.css). Zusätzlich verzichten die Reveal-Klassen unten auf jede
   Verschiebung im Raum — es bleibt beim Ein- und Ausblenden.
   ========================================================================== */

/* ══════════════════════════════════════════════════════════════════════════
   REVEAL
   Beim Eintritt in den Viewport. Die Klasse `is-revealed` setzt das
   JavaScript; ohne JavaScript ist der Inhalt sofort sichtbar — es wird
   nie etwas versteckt, was nicht wieder erscheinen kann.
   ══════════════════════════════════════════════════════════════════════════ */

.vis-reveal {
	opacity: 1;
	transform: none;
}

/* Erst wenn das Skript übernimmt, wird der Startzustand gesetzt. */
.js .vis-reveal:not(.is-revealed) {
	opacity: 0;
	transform: translateY(var(--vis-space-5));
}

.js .vis-reveal {
	transition:
		opacity var(--vis-duration-content) var(--vis-ease-out),
		transform var(--vis-duration-content) var(--vis-ease-out);
}

/* Fokus schlägt Choreografie. Wer per Tastatur in einen Block springt,
   der noch auf sein Reveal wartet, sieht ihn sofort — unabhängig davon,
   ob der Observer schon gemeldet hat oder das Skript überhaupt so weit
   gekommen ist. */
.js .vis-reveal:not(.is-revealed):focus-within {
	opacity: 1;
	transform: none;
}

/* Gestaffelt: Kinder folgen nacheinander. Maximal sechs Stufen —
   danach wartet der Besucher zu lange. */
.js .vis-reveal-group > * {
	transition-delay: 0ms;
}

.js .vis-reveal-group.is-revealed > *:nth-child(2) { transition-delay: 60ms; }
.js .vis-reveal-group.is-revealed > *:nth-child(3) { transition-delay: 120ms; }
.js .vis-reveal-group.is-revealed > *:nth-child(4) { transition-delay: 180ms; }
.js .vis-reveal-group.is-revealed > *:nth-child(5) { transition-delay: 240ms; }
.js .vis-reveal-group.is-revealed > *:nth-child(6) { transition-delay: 300ms; }

/* Variante: nur einblenden, ohne Verschiebung. */
.js .vis-reveal--fade:not(.is-revealed) { transform: none; }

/* Variante: leichter Zoom für Medien. 1.04 auf 1, nicht mehr. */
.js .vis-reveal--scale:not(.is-revealed) {
	transform: scale(1.04);
	opacity: 0;
}

.js .vis-reveal--scale {
	transition:
		opacity var(--vis-duration-cinematic) var(--vis-ease-out),
		transform var(--vis-duration-cinematic) var(--vis-ease-out);
}

@media (prefers-reduced-motion: reduce) {
	.js .vis-reveal:not(.is-revealed),
	.js .vis-reveal--scale:not(.is-revealed) {
		transform: none;
	}

	.js .vis-reveal-group.is-revealed > * { transition-delay: 0ms !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   HOVER
   Nur auf Zeigegeräten. Auf Touch gibt es keinen Hover-Zustand, und ein
   simulierter fühlt sich falsch an.
   ══════════════════════════════════════════════════════════════════════════ */

@media (hover: hover) and (pointer: fine) {
	.vis-hover-lift {
		transition: transform var(--vis-duration-ui) var(--vis-ease-out);
	}

	.vis-hover-lift:hover { transform: translateY(-2px); }

	.vis-hover-zoom > img {
		transition: transform var(--vis-duration-content) var(--vis-ease-out);
	}

	.vis-hover-zoom:hover > img { transform: scale(1.04); }
}

/* ══════════════════════════════════════════════════════════════════════════
   ÜBERGÄNGE FÜR ZUSTÄNDE
   Für Menüs, Panels und Overlays, die später dazukommen.
   ══════════════════════════════════════════════════════════════════════════ */

.vis-fade {
	opacity: 0;
	visibility: hidden;
	transition:
		opacity var(--vis-duration-ui) var(--vis-ease-out),
		visibility 0s linear var(--vis-duration-ui);
}

.vis-fade.is-open {
	opacity: 1;
	visibility: visible;
	transition-delay: 0s;
}

/* ══════════════════════════════════════════════════════════════════════════
   LADEZUSTAND
   Ein ruhiger Puls statt eines wandernden Glanzstreifens — der wäre eine
   Daueranimation und damit ausgeschlossen.
   ══════════════════════════════════════════════════════════════════════════ */

.vis-skeleton {
	background: var(--vis-bg-surface);
	border-radius: var(--vis-radius-sm);
	animation: vis-pulse 1.6s var(--vis-ease-inout) infinite;
}

@keyframes vis-pulse {
	0%, 100% { opacity: 1; }
	50%      { opacity: 0.55; }
}

@media (prefers-reduced-motion: reduce) {
	.vis-skeleton { animation: none; opacity: 0.8; }
}

/* Gedruckter Inhalt wartet nicht auf einen Scroll-Beobachter. */
@media print {
	.js .vis-reveal:not(.is-revealed) { opacity: 1; transform: none; }
}

/* ══ BILD-REVEAL ════════════════════════════════════════════════════════════
   Arbeitsfotos öffnen sich von unten wie ein Rollo und setzen sich dabei
   aus leichter Nähe ab. Der Rahmen (Karte, Text) kommt wie jedes Reveal;
   das Bild selbst zieht nach. Ohne JavaScript und bei reduzierter
   Bewegung steht alles sofort da. */

.js .vis-project:not(.is-revealed) .vis-project__image,
.js .vis-glimpse__item:not(.is-revealed) .vis-glimpse__image {
	clip-path: inset(100% 0 0 0);
	scale: 1.08;
}

.js .vis-project .vis-project__image,
.js .vis-glimpse__item .vis-glimpse__image {
	clip-path: inset(0 0 0 0);
	transition:
		clip-path var(--vis-duration-cinematic) var(--vis-ease-out) var(--vis-duration-micro),
		scale var(--vis-duration-cinematic) var(--vis-ease-out) var(--vis-duration-micro),
		transform var(--vis-duration-content) var(--vis-ease-out);
}

@media (prefers-reduced-motion: reduce) {
	.js .vis-project:not(.is-revealed) .vis-project__image,
	.js .vis-glimpse__item:not(.is-revealed) .vis-glimpse__image {
		clip-path: none;
		scale: 1;
	}
}

/* Schon beim Laden sichtbar: ohne Übergang da (main.js, revealOnEntry). */
.js .is-instant,
.js .is-instant * {
	transition: none !important;
}
