/**
 * Fünftes Viertel: Bewegung.
 *
 * Grundhaltung: alles läuft nativ über scroll-getriebene Animationen
 * (animation-timeline: view()). Kein Beobachter-Skript, kein Layout-Thrashing,
 * die Bewegung läuft im Compositor und nicht im Main-Thread.
 *
 * Wo der Browser das nicht kann, springt assets/js/site.js ein und setzt die
 * Klasse .is-inview. Dieselben Keyframes, nur anders ausgelöst.
 *
 * Wer prefers-reduced-motion gesetzt hat, sieht Inhalte sofort und vollständig.
 * Das ist kein Nice-to-have, das ist der Standardfall für einen Teil der Leute.
 *
 * Benutzung im Editor: Block auswählen, unter Erweitert eine der Klassen
 * eintragen. Alle Klassen sind unter /styleguide live zu sehen.
 *
 * Bestand (auf der Startseite im Einsatz):
 *
 *   fv-reveal            Einblenden mit leichtem Hochwandern
 *   fv-reveal--left      Einblenden aus der linken Flanke
 *   fv-reveal--scale     Einblenden mit minimalem Zoom, für Bilder
 *   fv-stagger           Auf einen Container: Kinder laufen versetzt ein
 *   fv-sticky-fade       Blendet beim Herausscrollen wieder aus
 *   fv-progress          Skaliert eine Linie über die Scroll-Distanz
 *
 * Kandidaten (19.09.2026, zur Auswahl im Styleguide; was nicht gewählt
 * wird, fliegt danach wieder raus):
 *
 *   fv-reveal--blur      Einblenden aus der Unschärfe
 *   fv-reveal--wipe      Wischt von unten frei (Clip), für Headlines
 *   fv-reveal--rise      Kommt mit Schwung von unten (leichter Überschwinger)
 *   fv-reveal--mask      Bild aus der Maske, mit Zoom, für Bilder
 *   fv-words             Wörter laufen einzeln ein (site.js zerlegt den Text)
 *   fv-line-draw         Zeichnet eine Linie über dem Block von links
 *   fv-parallax          Bild wandert leicht gegen den Scroll (auf den Bildblock)
 *   fv-count             Zahl zählt hoch (--fv-to: Zielwert als Inline-Style)
 *   fv-load-in           Kinder laufen beim Seitenaufruf versetzt ein (Hero)
 *   fv-marquee           Laufschrift (eigenes Markup, siehe Styleguide)
 *   fv-pulse             Pulsierender Punkt vor dem Text (Coming soon)
 *   fv-bounce            Hüpft leicht, für den Scroll-Hinweis
 *   fv-hover-zoom        Bild zoomt beim Hover (auf den Bildblock)
 *   fv-mark-tilt         Bildmarke kippt beim Hover (auf den Bildblock)
 *   fv-underline-slide   Link-Unterstreichung wandert beim Hover ein
 *   fv-btn-arrow         Button: Pfeil rückt beim Hover nach
 *   fv-btn-fill          Button (Outline-Stil): Füllung wischt beim Hover durch
 *   fv-btn-lift          Button: hebt sich beim Hover leicht
 */

/* ---------------------------------------------------------------------------
   Tokens
   --------------------------------------------------------------------------- */

:root {
	--fv-anim-distance: 1.25rem;
	--fv-anim-duration: 0.7s;
	--fv-anim-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
	--fv-anim-ease-over: cubic-bezier(0.34, 1.4, 0.64, 1);
	--fv-stagger-step: 90ms;
	--fv-word-step: 45ms;
	--fv-hover-duration: 0.35s;
	--fv-marquee-duration: 28s;
}

/* ---------------------------------------------------------------------------
   Keyframes
   --------------------------------------------------------------------------- */

@keyframes fv-reveal {
	from {
		opacity: 0;
		transform: translateY(var(--fv-anim-distance));
	}

	to {
		opacity: 1;
		transform: translateY(0);
	}
}

@keyframes fv-reveal-left {
	from {
		opacity: 0;
		transform: translateX(calc(var(--fv-anim-distance) * -1.6));
	}

	to {
		opacity: 1;
		transform: translateX(0);
	}
}

@keyframes fv-reveal-scale {
	from {
		opacity: 0;
		transform: scale(1.04);
	}

	to {
		opacity: 1;
		transform: scale(1);
	}
}

@keyframes fv-fade {
	from {
		opacity: 0;
	}

	to {
		opacity: 1;
	}
}

@keyframes fv-progress {
	from {
		transform: scaleX(0);
	}

	to {
		transform: scaleX(1);
	}
}

/* Kandidaten */

@keyframes fv-reveal-blur {
	from {
		opacity: 0;
		filter: blur(10px);
		transform: translateY(0.5rem);
	}

	to {
		opacity: 1;
		filter: blur(0);
		transform: translateY(0);
	}
}

@keyframes fv-reveal-wipe {
	from {
		opacity: 1;
		clip-path: inset(100% 0 0 0);
		transform: translateY(0.75rem);
	}

	to {
		opacity: 1;
		clip-path: inset(0 0 0 0);
		transform: translateY(0);
	}
}

@keyframes fv-reveal-rise {
	from {
		opacity: 0;
		transform: translateY(2.5rem);
	}

	to {
		opacity: 1;
		transform: translateY(0);
	}
}

@keyframes fv-reveal-mask {
	from {
		opacity: 1;
		clip-path: inset(100% 0 0 0);
		transform: scale(1.08);
	}

	to {
		opacity: 1;
		clip-path: inset(0 0 0 0);
		transform: scale(1);
	}
}

@keyframes fv-parallax {
	from {
		transform: translateY(7%) scale(1.15);
	}

	to {
		transform: translateY(-7%) scale(1.15);
	}
}

@keyframes fv-count {
	from {
		--fv-n: 0;
	}

	to {
		--fv-n: var(--fv-to, 0);
	}
}

@keyframes fv-marquee {
	from {
		transform: translateX(0);
	}

	to {
		transform: translateX(-50%);
	}
}

@keyframes fv-pulse {
	0% {
		box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 70%, transparent);
	}

	70% {
		box-shadow: 0 0 0 0.7em color-mix(in srgb, currentColor 0%, transparent);
	}

	100% {
		box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 0%, transparent);
	}
}

@keyframes fv-bounce {
	0%,
	100% {
		transform: translateY(0);
	}

	50% {
		transform: translateY(0.35em);
	}
}

/* ---------------------------------------------------------------------------
   Variante A: scroll-getrieben, der Normalfall
   --------------------------------------------------------------------------- */

@supports (animation-timeline: view()) {
	.fv-reveal,
	.fv-reveal--left,
	.fv-reveal--scale,
	.fv-reveal--blur,
	.fv-reveal--wipe,
	.fv-reveal--rise,
	.fv-reveal--mask,
	.fv-stagger > *,
	.fv-words .fv-word {
		animation: fv-reveal var(--fv-anim-duration) var(--fv-anim-ease) both;

		/* Die Animation läuft, während das Element von unten ins Bild kommt,
		   und ist fertig, sobald es 25 Prozent weit drin steht. */
		animation-timeline: view();
		animation-range: entry 0% entry 25%;
	}

	.fv-reveal--left {
		animation-name: fv-reveal-left;
	}

	.fv-reveal--scale {
		animation-name: fv-reveal-scale;
	}

	.fv-reveal--blur {
		animation-name: fv-reveal-blur;
	}

	.fv-reveal--wipe {
		animation-name: fv-reveal-wipe;
	}

	.fv-reveal--rise {
		animation-name: fv-reveal-rise;
		animation-timing-function: var(--fv-anim-ease-over);
	}

	.fv-reveal--mask {
		animation-name: fv-reveal-mask;
		animation-range: entry 0% entry 40%;
	}

	/* Wörter: jedes Wort hat seinen eigenen View-Timeline, der Versatz kommt
	   über einen verschobenen Range (--i setzt site.js). */
	.fv-words .fv-word {
		animation-range: entry calc(0% + var(--i, 0) * 2%) entry calc(25% + var(--i, 0) * 2%);
	}

	/* Versatz für Kinder. Sieben Stufen reichen für jede vernünftige Reihe,
	   danach bleibt es beim letzten Wert, statt immer träger zu werden. */
	.fv-stagger > *:nth-child(1) {
		animation-delay: 0ms;
	}

	.fv-stagger > *:nth-child(2) {
		animation-delay: var(--fv-stagger-step);
	}

	.fv-stagger > *:nth-child(3) {
		animation-delay: calc(var(--fv-stagger-step) * 2);
	}

	.fv-stagger > *:nth-child(4) {
		animation-delay: calc(var(--fv-stagger-step) * 3);
	}

	.fv-stagger > *:nth-child(5) {
		animation-delay: calc(var(--fv-stagger-step) * 4);
	}

	.fv-stagger > *:nth-child(6) {
		animation-delay: calc(var(--fv-stagger-step) * 5);
	}

	.fv-stagger > *:nth-child(n + 7) {
		animation-delay: calc(var(--fv-stagger-step) * 6);
	}

	/* Blendet beim Verlassen des Viewports wieder aus. Sparsam einsetzen,
	   sonst wirkt die Seite unruhig. */
	.fv-sticky-fade {
		animation: fv-fade var(--fv-anim-duration) var(--fv-anim-ease) both;
		animation-timeline: view();
		animation-range: entry 0% entry 30%, exit 70% exit 100%;
		animation-direction: alternate;
	}

	/* Fortschrittslinie über die Scroll-Distanz des eigenen Containers. */
	.fv-progress {
		transform-origin: left center;
		animation: fv-progress linear both;
		animation-timeline: view();
		animation-range: entry 100% exit 0%;
	}

	/* Linie zeichnet sich beim Hereinscrollen. */
	.fv-line-draw::before {
		animation: fv-progress var(--fv-anim-duration) var(--fv-anim-ease) both;
		animation-timeline: view();
		animation-range: entry 0% entry 40%;
	}

	/* Parallax: das Bild wandert über die gesamte Sichtbarkeit leicht
	   gegen die Scrollrichtung. */
	.fv-parallax img {
		animation: fv-parallax linear both;
		animation-timeline: view();
		animation-range: cover 0% cover 100%;
	}

	/* Zähler läuft, während die Zahl ins Bild kommt. */
	.fv-count {
		animation: fv-count 1.4s ease-out both;
		animation-timeline: view();
		animation-range: entry 0% entry 60%;
	}
}

/* ---------------------------------------------------------------------------
   Variante B: Fallback über IntersectionObserver
   --------------------------------------------------------------------------- */

@supports not (animation-timeline: view()) {
	/* Nur verstecken, wenn das Skript auch wirklich läuft. Ohne JS bleibt
	   der Inhalt sichtbar, statt für immer auf opacity 0 zu stehen. */
	.js-anim .fv-reveal,
	.js-anim .fv-reveal--left,
	.js-anim .fv-reveal--scale,
	.js-anim .fv-reveal--blur,
	.js-anim .fv-reveal--wipe,
	.js-anim .fv-reveal--rise,
	.js-anim .fv-reveal--mask,
	.js-anim .fv-stagger > *,
	.js-anim .fv-words .fv-word {
		opacity: 0;
	}

	.js-anim .fv-reveal.is-inview,
	.js-anim .fv-reveal--left.is-inview,
	.js-anim .fv-reveal--scale.is-inview,
	.js-anim .fv-reveal--blur.is-inview,
	.js-anim .fv-reveal--wipe.is-inview,
	.js-anim .fv-reveal--rise.is-inview,
	.js-anim .fv-reveal--mask.is-inview,
	.js-anim .fv-stagger.is-inview > *,
	.js-anim .fv-words.is-inview .fv-word {
		animation: fv-reveal var(--fv-anim-duration) var(--fv-anim-ease) both;
	}

	.js-anim .fv-reveal--left.is-inview {
		animation-name: fv-reveal-left;
	}

	.js-anim .fv-reveal--scale.is-inview {
		animation-name: fv-reveal-scale;
	}

	.js-anim .fv-reveal--blur.is-inview {
		animation-name: fv-reveal-blur;
	}

	.js-anim .fv-reveal--wipe.is-inview {
		animation-name: fv-reveal-wipe;
	}

	.js-anim .fv-reveal--rise.is-inview {
		animation-name: fv-reveal-rise;
		animation-timing-function: var(--fv-anim-ease-over);
	}

	.js-anim .fv-reveal--mask.is-inview {
		animation-name: fv-reveal-mask;
	}

	.js-anim .fv-words.is-inview .fv-word {
		animation-delay: calc(var(--fv-word-step) * var(--i, 0));
	}

	.js-anim .fv-stagger.is-inview > *:nth-child(2) {
		animation-delay: var(--fv-stagger-step);
	}

	.js-anim .fv-stagger.is-inview > *:nth-child(3) {
		animation-delay: calc(var(--fv-stagger-step) * 2);
	}

	.js-anim .fv-stagger.is-inview > *:nth-child(4) {
		animation-delay: calc(var(--fv-stagger-step) * 3);
	}

	.js-anim .fv-stagger.is-inview > *:nth-child(5) {
		animation-delay: calc(var(--fv-stagger-step) * 4);
	}

	.js-anim .fv-stagger.is-inview > *:nth-child(n + 6) {
		animation-delay: calc(var(--fv-stagger-step) * 5);
	}

	.js-anim .fv-line-draw::before {
		transform: scaleX(0);
	}

	.js-anim .fv-line-draw.is-inview::before {
		animation: fv-progress var(--fv-anim-duration) var(--fv-anim-ease) both;
	}

	.js-anim .fv-count.is-inview {
		animation: fv-count 1.4s ease-out both;
	}

	/* Ohne Scroll-Timeline gibt es keinen Parallax, das Bild steht einfach. */
}

/* ---------------------------------------------------------------------------
   Kandidaten, die keine Scroll-Timeline brauchen
   --------------------------------------------------------------------------- */

/* Linie über dem Block, wird von links gezeichnet. */
.fv-line-draw {
	position: relative;
	padding-top: var(--wp--preset--spacing--30);
}

.fv-line-draw::before {
	content: "";
	position: absolute;
	inset: 0 0 auto 0;
	height: 1px;
	background: var(--wp--preset--color--muted);
	transform-origin: left center;
}

/* Parallax braucht einen Rahmen, der den Überstand abschneidet. */
.fv-parallax {
	overflow: hidden;
}

.fv-parallax img {
	will-change: transform;
}

/* Zähler: der Zielwert kommt als --fv-to per Inline-Style. Die Zahl steht
   zusätzlich im aria-label, damit Screenreader den Endwert lesen. */
@property --fv-n {
	syntax: "<integer>";
	inherits: false;
	initial-value: 0;
}

.fv-count {
	display: inline-block;
	counter-reset: fvn var(--fv-n);
	font-variant-numeric: tabular-nums;
}

.fv-count::after {
	content: counter(fvn);
}

/* Hero-Einlauf beim Seitenaufruf: zeitbasiert, einmalig, versetzt. */
.fv-load-in > * {
	animation: fv-reveal var(--fv-anim-duration) var(--fv-anim-ease) both;
	animation-delay: calc(150ms + var(--fv-stagger-step) * 1.4 * var(--fv-i, 0));
}

.fv-load-in > *:nth-child(2) {
	--fv-i: 1;
}

.fv-load-in > *:nth-child(3) {
	--fv-i: 2;
}

.fv-load-in > *:nth-child(4) {
	--fv-i: 3;
}

.fv-load-in > *:nth-child(5) {
	--fv-i: 4;
}

.fv-load-in > *:nth-child(n + 6) {
	--fv-i: 5;
}

/* Laufschrift. Markup:
   <div class="fv-marquee"><div class="fv-marquee__track">
     <span>Food · Community · Events · Pop-ups · </span>
     <span aria-hidden="true">Food · Community · Events · Pop-ups · </span>
   </div></div>
   Der zweite Span ist die Kopie für den nahtlosen Übergang. */
.fv-marquee {
	overflow: hidden;
	white-space: nowrap;
}

.fv-marquee__track {
	display: inline-flex;
	width: max-content;
	animation: fv-marquee var(--fv-marquee-duration) linear infinite;
}

.fv-marquee__track > * {
	padding-right: 0.6em;
}

.fv-marquee:hover .fv-marquee__track {
	animation-play-state: paused;
}

/* Pulsierender Punkt vor dem Text. */
.fv-pulse::before {
	content: "";
	display: inline-block;
	width: 0.55em;
	height: 0.55em;
	margin-right: 0.7em;
	border-radius: 50%;
	background: currentColor;
	vertical-align: middle;
	animation: fv-pulse 1.8s ease-out infinite;
}

/* Hüpfender Hinweis. */
.fv-bounce {
	display: inline-block;
	animation: fv-bounce 1.6s ease-in-out infinite;
}

/* ---------------------------------------------------------------------------
   Hover-Kandidaten
   .is-hover simuliert den Hover für die Demo im Styleguide (Touch).
   --------------------------------------------------------------------------- */

.fv-hover-zoom {
	overflow: hidden;
}

.fv-hover-zoom img {
	transition: transform 0.8s var(--fv-anim-ease);
}

.fv-hover-zoom:hover img,
.is-hover .fv-hover-zoom img {
	transform: scale(1.05);
}

.fv-mark-tilt img {
	transition: transform 0.5s var(--fv-anim-ease-over);
}

.fv-mark-tilt:hover img,
.is-hover .fv-mark-tilt img {
	transform: rotate(-6deg) scale(1.04);
}

a.fv-underline-slide,
.fv-underline-slide a {
	text-decoration: none;
	background-image: linear-gradient(currentColor, currentColor);
	background-repeat: no-repeat;
	background-size: 0% 1px;
	background-position: left calc(100% - 0.05em);
	padding-bottom: 0.1em;
	transition: background-size var(--fv-hover-duration) var(--fv-anim-ease);
}

a.fv-underline-slide:hover,
.fv-underline-slide a:hover,
.is-hover a.fv-underline-slide,
.is-hover .fv-underline-slide a {
	background-size: 100% 1px;
}

.fv-btn-arrow .wp-block-button__link::after {
	content: "→";
	display: inline-block;
	margin-left: 0.5em;
	transition: transform var(--fv-hover-duration) var(--fv-anim-ease);
}

.fv-btn-arrow:hover .wp-block-button__link::after,
.is-hover .fv-btn-arrow .wp-block-button__link::after {
	transform: translateX(0.35em);
}

.fv-btn-fill .wp-block-button__link {
	background-color: transparent;
	background-image: linear-gradient(var(--wp--preset--color--contrast), var(--wp--preset--color--contrast));
	background-repeat: no-repeat;
	background-size: 0% 100%;
	background-position: left center;
	border: 2px solid var(--wp--preset--color--contrast);
	color: var(--wp--preset--color--contrast);
	transition: background-size 0.45s var(--fv-anim-ease), color 0.25s var(--fv-anim-ease);
}

.fv-btn-fill:hover .wp-block-button__link,
.is-hover .fv-btn-fill .wp-block-button__link {
	background-size: 100% 100%;
	color: var(--wp--preset--color--base);
}

.fv-btn-lift .wp-block-button__link {
	transition: transform var(--fv-hover-duration) var(--fv-anim-ease), box-shadow var(--fv-hover-duration) var(--fv-anim-ease);
}

.fv-btn-lift:hover .wp-block-button__link,
.is-hover .fv-btn-lift .wp-block-button__link {
	transform: translateY(-3px);
	box-shadow: 0 12px 28px rgba(0, 0, 51, 0.35);
}

/* ---------------------------------------------------------------------------
   Sanfte Seitenwechsel
   --------------------------------------------------------------------------- */

@view-transition {
	navigation: auto;
}

::view-transition-old(root) {
	animation: fv-fade 180ms var(--fv-anim-ease) reverse both;
}

::view-transition-new(root) {
	animation: fv-fade 220ms var(--fv-anim-ease) both;
}

/* ---------------------------------------------------------------------------
   Respekt vor der Systemeinstellung
   --------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	.fv-reveal,
	.fv-reveal--left,
	.fv-reveal--scale,
	.fv-reveal--blur,
	.fv-reveal--wipe,
	.fv-reveal--rise,
	.fv-reveal--mask,
	.fv-sticky-fade,
	.fv-progress,
	.fv-stagger > *,
	.fv-words .fv-word,
	.fv-line-draw::before,
	.fv-parallax img,
	.fv-load-in > *,
	.fv-marquee__track,
	.fv-pulse::before,
	.fv-bounce {
		animation: none !important;
		opacity: 1 !important;
		transform: none !important;
		clip-path: none !important;
		filter: none !important;
	}

	/* Der Zähler zeigt sofort den Endwert. */
	.fv-count {
		animation: none !important;
		--fv-n: var(--fv-to, 0);
	}

	.fv-hover-zoom img,
	.fv-mark-tilt img,
	.fv-btn-arrow .wp-block-button__link::after,
	.fv-btn-fill .wp-block-button__link,
	.fv-btn-lift .wp-block-button__link,
	a.fv-underline-slide,
	.fv-underline-slide a {
		transition: none !important;
	}

	::view-transition-old(root),
	::view-transition-new(root) {
		animation: none !important;
	}

	html {
		scroll-behavior: auto;
	}
}
