/* ============================================================================
   NUIT NOIRE INTERNATIONAL — motion.css
   SPEC3, Abschnitt 3: die zehn Bewegungen. Letzte Stufe der Kaskade,
   geladen nach mobile.css. Sie fügt keine Farbe, keine Komponente und kein
   Maß hinzu — nur Zustände, die sich bewegen dürfen.

   ENTHALTEN (Nummern wie in SPEC3 §3)
     1  Zeilenweiser Aufbau grosser Überschriften  — Zustände für .nni-line,
        die Zeilen selbst setzt motion.js (Range-Messung).
     2  Bildenthüllung                             — clip-path + Maßstab 1.06→1.
     3  Ruhiger Parallaxversatz                    — Trägerregel (translate),
        den Wert schreibt motion.js als --nnim-y.
     4  Seitenwechsel                              — abgeschaltet, siehe Abschnitt 4.
     5  Lichtschein am Zeiger                      — .nnim-glow, nur (hover:hover)
        und (pointer:fine); Position von motion.js.
     6  Verweilzustände                            — Haarlinie unter dem Titel,
        Bild 2 % heller; der bisherige Bildzoom wird dafür stillgelegt.
     7  Das Siegel atmet                           — Takt 6 s, Schwellenlicht
        auf ungerade Vielfache gelegt, damit die Höhepunkte zusammenfallen.
     8  Die Tür                                    — Aufglimmen der Fuge (140 ms)
        vor dem Öffnen, Nachhall am Rand der neuen Seite (400 ms).
     9  Zahlen und Indizes                         — Ruhe im Satzbild
        (tabular-nums); das Hochzählen selbst macht motion.js.
    10  Formularfelder                             — Unterlinie wächst aus der
        Mitte, Beschriftung wird nur heller.

   BEWUSST WEGGELASSEN
     ·  Auf der Schwelle wirken nur 7, 8 und 10. Zeilenaufbau, Bildenthüllung,
        Parallaxe, Lichtschein, Verweilzustände und Zahlen gehören in den
        privaten Bereich; die Schwelle soll eine einzige Geste bleiben.
     ·  Für 4 gibt es keinen Nachbau ohne View-Transitions-API. Ein selbst
        gebautes Überblenden müsste Klicks abfangen und die Seite selbst laden —
        das kostet Verlässlichkeit und bringt kein Bild dazu.
     ·  Parallaxe (3) nur in Figuren, die ihren Inhalt beschneiden; sonst
        entstünde am Rand eine Lücke. Die Auswahl trifft motion.js.
     ·  Kein will-change ausser am Lichtschein — ein einziges Element.

   REGELN, DIE HIER GELTEN
     ·  Animiert werden ausschliesslich opacity, transform (samt translate und
        scale), clip-path und filter. Keine Breite, keine Höhe, kein Rand,
        kein Abstand — nichts, was das Layout bewegt.
     ·  Jeder bewegte Abschnitt liegt in @media (prefers-reduced-motion:
        no-preference). Am Ende steht zusätzlich ein Riegel für den Fall, dass
        die Einstellung erst während der Sitzung gesetzt wird.
   ========================================================================= */


/* ---------------------------------------------------------------------------
   0 · Lokale Größen. Prefix --nnim-, damit nichts mit den Design-Tokens
       (--nni-) und den Layoutgrössen aus private.css (--nnip-) kollidiert.
   ------------------------------------------------------------------------ */

:root {
	--nnim-breath: 6s;      /* Takt des Siegels (Punkt 7)                    */
	--nnim-line-step: 60ms; /* Versatz je Überschriftszeile (Punkt 1)        */
	--nnim-veil: 900ms;     /* Dauer der Bildenthüllung (Punkt 2)            */
}


/* ---------------------------------------------------------------------------
   1 · Zeilenweiser Aufbau grosser Überschriften

   motion.js misst die Umbrüche über Range-Objekte und fasst jede Zeile in
   <span class="nni-line" aria-hidden="true">. Die Überschrift selbst trägt
   dann ein aria-label mit dem vollständigen Satz — so liest ein Screenreader
   weiterhin einen zusammenhängenden Satz und nicht vier Bruchstücke, und es
   liegt kein zweiter, versteckter Text im Dokument, der beim Markieren und
   Kopieren doppelt mitkäme.

   Die Überschrift selbst wird sichtbar gestellt: sonst liefen ihr eigener
   Reveal (.nni-reveal) und der Zeilenauftritt gegeneinander.
   ------------------------------------------------------------------------ */

.nni-body .nni-display.nnim-lines {
	opacity: 1;
	transform: none;
	transition: none;
}

.nni-body .nnim-lines .nni-line {
	display: block;
}

@media (prefers-reduced-motion: no-preference) {

	.nni-body .nnim-lines .nni-line {
		opacity: 0;
		transform: translateY(.18em);
		transition:
			opacity var(--nni-t-slow) var(--nni-ease) calc(var(--nnim-i, 0) * var(--nnim-line-step)),
			transform var(--nni-t-slow) var(--nni-ease) calc(var(--nnim-i, 0) * var(--nnim-line-step));
	}

	.nni-body .nnim-lines.is-in .nni-line {
		opacity: 1;
		transform: none;
	}

	/* Nach einer Fenstergrössenänderung werden die Zeilen neu vermessen.
	   War die Überschrift schon zu sehen, tritt sie nicht ein zweites Mal auf. */
	.nni-body .nnim-lines.nnim-still .nni-line {
		transition: none;
	}
}


/* ---------------------------------------------------------------------------
   2 · Bildenthüllung   +   3 · Ruhiger Parallaxversatz

   Beide sitzen auf denselben Elementen, deshalb stehen sie zusammen.
   Der Maßstab entsteht aus zwei Faktoren:
     --nnim-z  bleibender Spielraum für die Parallaxe (motion.js, 1.04)
     --nnim-v  die Enthüllung selbst (1.06 → 1)
   Der Versatz --nnim-y kommt je Bild aus motion.js, höchstens ±7 px.

   Die Selektoren sind so lang wie ihre Gegenstücke in private.css, weil dort
   auf denselben Bildern ein Zoom beim Verweilen liegt. Punkt 6 ersetzt diesen
   Zoom durch Helligkeit — ohne !important geht das nur über die Kaskade.
   Die Dauer des Maßstabs steckt in --nnim-veil-t: im Ruhezustand 0 ms, damit
   beim Setzen der Startwerte nichts läuft; erst .is-in schaltet sie auf 900 ms.
   ------------------------------------------------------------------------ */

.nni-body .nni-main .nni-figure img,
.nni-body .nni-main .nni-figure .nni-figure__ph,
.nni-body.post-type-archive-nni_selection .nni-main .nni-grid .nni-figure img,
.nni-body.post-type-archive-nni_selection .nni-main .nni-grid .nni-figure .nni-figure__ph {
	translate: 0 var(--nnim-y, 0px);
	scale: calc(var(--nnim-z, 1) * var(--nnim-v, 1));
	transform: none;
	transition:
		translate 160ms linear,
		scale var(--nnim-veil-t, 0ms) var(--nni-ease),
		filter var(--nni-t-med) var(--nni-ease-soft);
}

@media (prefers-reduced-motion: no-preference) {

	/* Die Blende. Sie zieht von oben auf und ist danach wieder vollständig
	   offen — motion.js nimmt die Klasse dann ab, damit kein Beschnitt und
	   kein Stapelkontext zurückbleibt. */
	.nni-body .nni-figure.nnim-veil {
		clip-path: inset(0 0 100% 0);
	}

	.nni-body .nni-figure.nnim-veil.is-in {
		clip-path: inset(0 0 0 0);
		transition: clip-path var(--nnim-veil) var(--nni-ease);
	}

	.nni-body .nni-figure.nnim-veil img,
	.nni-body .nni-figure.nnim-veil .nni-figure__ph {
		--nnim-v: 1.06;
	}

	.nni-body .nni-figure.nnim-veil.is-in img,
	.nni-body .nni-figure.nnim-veil.is-in .nni-figure__ph {
		--nnim-v: 1;
		--nnim-veil-t: var(--nnim-veil);
	}
}


/* ---------------------------------------------------------------------------
   4 · Seitenwechsel

   Cross-Document-View-Transitions: 240 ms Überblenden zwischen zwei Seiten
   desselben Hauses. Fehlt die Unterstützung, geschieht nichts — kein Ersatz,
   kein Abfangen von Klicks.

   Die Anmeldung steht bewusst auf oberster Ebene und nicht in einer
   Bedingungsgruppe: nicht jeder Browser nimmt sie dort an, und eine
   verworfene Anmeldung liesse den Übergang ganz entfallen. Abgeschaltet wird
   sie unten im Riegel, indem den Bildern die Bewegung genommen wird.
   ------------------------------------------------------------------------ */

/*
 * ABGESCHALTET, mit Absicht.
 *
 * `@view-transition { navigation: auto }` liess den Browser jeden
 * Seitenwechsel ueberblenden. Das sah gut aus und hatte einen Preis, den der
 * Auftraggeber in der Konsole gefunden hat:
 *
 *     Uncaught (in promise) InvalidStateError:
 *     Transition was aborted because of invalid state
 *
 * Der Uebergang haelt die alte Seite fest, bis die neue steht. Kommt etwas
 * dazwischen — ein zweiter Klick, ein Wechsel in einen anderen Tab, eine
 * Seite, die noch laedt —, bricht der Browser ihn ab und verwirft ein
 * Versprechen, das niemand auffangen kann: es gehoert dem Browser, nicht uns.
 * Auf einem Telefon, wo man beim Scrollen leicht zweimal tippt, passiert das
 * regelmaessig.
 *
 * Zweihundertvierzig Millisekunden Ueberblendung sind diesen Fehler nicht
 * wert. Die Seite hat ihre Bewegung ohnehin dort, wo sie hingehoert: im
 * Einblenden beim Scrollen und im Lauf der Kopfgalerie.
 *
 * Wer sie zurueckholen will, muss zuerst klaeren, wie die abgebrochenen
 * Uebergaenge stillgelegt werden — und das geht bei seitenuebergreifenden
 * Uebergaengen aus dem Skript heraus nicht.
 */


/* ---------------------------------------------------------------------------
   5 · Der Zeiger

   Ein einziges Element, von motion.js an den Körper gehängt und nur dort, wo
   es wirklich eine Maus gibt. Deckkraft am Scheitel 5 %, warm, träge.
   ------------------------------------------------------------------------ */

@media (prefers-reduced-motion: no-preference) and (hover: hover) and (pointer: fine) {

	.nni-body .nnim-glow {
		position: fixed;
		top: 0;
		left: 0;
		inline-size: 320px;
		block-size: 320px;
		margin: -160px 0 0 -160px;
		z-index: 0;
		pointer-events: none;
		opacity: 0;
		background:
			radial-gradient(closest-side,
				color-mix(in srgb, var(--nni-cream) 5%, transparent) 0%,
				color-mix(in srgb, var(--nni-bronze) 3%, transparent) 44%,
				transparent 72%);
		will-change: transform;
		transition: opacity var(--nni-t-med) var(--nni-ease-soft);
	}

	.nni-body .nnim-glow.is-in {
		opacity: 1;
	}
}


/* ---------------------------------------------------------------------------
   6 · Verweilzustände

   Karten heben sich nicht. Eine Haarlinie wandert unter dem Titel von links
   nach rechts, das Bild wird um zwei Prozent heller. Mehr nicht — der
   bisherige Bildzoom ist oben bereits stillgelegt (transform: none).

   Die Wege auf der Startseite (.nni-card--way) bleiben ausgenommen: sie
   tragen bereits eine eigene Linie über dem Titel.
   ------------------------------------------------------------------------ */

.nni-body .nni-card:not(.nni-card--way) .nni-card__title,
.nni-body .nni-selection__item .nni-card__title,
.nni-body .nni-band .nni-card__title {
	position: relative;
}

@media (prefers-reduced-motion: no-preference) {

	.nni-body .nni-card:not(.nni-card--way) .nni-card__title::after,
	.nni-body .nni-selection__item .nni-card__title::after,
	.nni-body .nni-band .nni-card__title::after {
		content: "";
		position: absolute;
		inset-inline: 0;
		inset-block-end: -.24em;
		block-size: var(--nni-hair);
		background: currentColor;
		opacity: .34;
		transform: scaleX(0);
		transform-origin: 0 50%;
		transition: transform var(--nni-t-med) var(--nni-ease);
		pointer-events: none;
	}

	.nni-body .nni-card:not(.nni-card--way):focus-within .nni-card__title::after,
	.nni-body .nni-selection__item:focus-within .nni-card__title::after,
	.nni-body .nni-band:focus-within .nni-card__title::after {
		transform: scaleX(1);
	}

	@media (hover: hover) {

		.nni-body .nni-card:not(.nni-card--way):hover .nni-card__title::after,
		.nni-body .nni-selection__item:hover .nni-card__title::after,
		.nni-body .nni-band:hover .nni-card__title::after {
			transform: scaleX(1);
		}
	}
}

@media (hover: hover) {

	.nni-body .nni-card:hover .nni-figure img,
	.nni-body .nni-card:focus-within .nni-figure img,
	.nni-body .nni-card:hover .nni-figure .nni-figure__ph,
	.nni-body .nni-card:focus-within .nni-figure .nni-figure__ph,
	.nni-body .nni-selection__item:hover .nni-figure img,
	.nni-body .nni-selection__item:focus-within .nni-figure img {
		filter: brightness(1.02);
	}
}


/* ---------------------------------------------------------------------------
   7 · Das Siegel atmet

   Takt 6 s. Das Licht der Schwelle läuft auf ungeraden Vielfachen davon
   (5×, 7×, und der wechselnde Atem der Halle auf 4,5× = 27 s): nur so fallen
   die Höhepunkte beider Bewegungen regelmässig zusammen statt sich zu
   verschleifen. Übernommen wird ausschliesslich die Dauer — die Keyframes
   bleiben, wo sie stehen.
   ------------------------------------------------------------------------ */

.nni-seal > i,
.nni-seal::after,
.nni-gate-body .nni-seal i {
	animation-duration: var(--nnim-breath);
}

.nni-halo {
	animation-duration: calc(var(--nnim-breath) * 4.5);
}

.nni-halo::before {
	animation-duration: calc(var(--nnim-breath) * 5);
}

.nni-halo::after {
	animation-duration: calc(var(--nnim-breath) * 7);
}


/* ---------------------------------------------------------------------------
   8 · Die Tür

   a) Kurz bevor die Flügel auseinandergehen (gate.css: bei 53 % der
      Türlaufzeit) glimmt die Fuge 140 ms lang auf. Der eigene Platz dafür ist
      ::before der Fuge — ::after trägt bereits den breiten Schein.
   b) Auf der neuen Seite hallt die Lichtlinie 400 ms an beiden Rändern nach,
      solange site.js die Ankunft (.is-opening) hält.
   ------------------------------------------------------------------------ */

@media (prefers-reduced-motion: no-preference) {

	.nni-gate-body .nni-passage.is-open .nni-passage__seam::before {
		content: "";
		position: absolute;
		inset-block: 0;
		left: 50%;
		inline-size: 18px;
		margin-left: -9px;
		background: linear-gradient(90deg, transparent 0%, var(--nni-cream) 50%, transparent 100%);
		opacity: 0;
		transform: scaleX(.4);
		animation: nnim-seam-flare 140ms var(--nni-ease-soft)
			calc(var(--nni-t-door) * .53 - 140ms) both;
		pointer-events: none;
	}

	@keyframes nnim-seam-flare {
		0%   { opacity: 0;   transform: scaleX(.4); }
		55%  { opacity: .5;  transform: scaleX(1); }
		100% { opacity: 0;   transform: scaleX(1.7); }
	}

	.nni-body.is-opening::before {
		content: "";
		position: fixed;
		inset: 0;
		z-index: 9999;
		pointer-events: none;
		background:
			linear-gradient(90deg,
				color-mix(in srgb, var(--nni-cream) 30%, transparent) 0 1px,
				color-mix(in srgb, var(--nni-cream) 5%, transparent) 1px,
				transparent 72px),
			linear-gradient(270deg,
				color-mix(in srgb, var(--nni-cream) 30%, transparent) 0 1px,
				color-mix(in srgb, var(--nni-cream) 5%, transparent) 1px,
				transparent 72px);
		animation: nnim-arrival-echo 400ms var(--nni-ease-soft) both;
	}

	@keyframes nnim-arrival-echo {
		0%   { opacity: 1; }
		100% { opacity: 0; }
	}
}


/* ---------------------------------------------------------------------------
   9 · Zahlen und Indizes

   Das Hochzählen übernimmt motion.js. Hier steht nur, was verhindert, dass
   der Satz dabei zappelt: gleich breite Ziffern.
   ------------------------------------------------------------------------ */

.nni-card__idx,
.nni-stages__idx,
.nni-profile__meta-value {
	font-variant-numeric: tabular-nums;
}


/* ---------------------------------------------------------------------------
   10 · Formularfelder

   Die Unterlinie wächst aus der Mitte, die Beschriftung wandert nicht — sie
   wird nur heller. Damit das Wachsen überhaupt zu sehen ist, bleibt die
   Grundlinie im Fokus ruhig; das helle Signal ist die wachsende Linie selbst.
   Der Fokusring aus base.css bleibt davon unberührt.

   .nni-field und .nni-profile__field enden beide exakt an der Unterkante
   ihres Eingabefeldes — deshalb genügt hier ein ::after am unteren Rand.
   Auf der Schwelle sitzt unter dem Feld noch die Schaltfläche; dort setzt
   motion.js Höhe und Breite als --nnim-line-y / --nnim-line-w und schaltet
   die Regel erst frei, wenn beide Werte stimmen (Klasse .nnim-line).
   ------------------------------------------------------------------------ */

@media (prefers-reduced-motion: no-preference) {

	/* — privater Bereich — */

	.nni-body .nni-field:not(.nni-field--pair),
	.nni-body .nni-profile__field {
		position: relative;
	}

	.nni-body .nni-field:not(.nni-field--pair)::after,
	.nni-body .nni-profile__field::after {
		content: "";
		position: absolute;
		inset-inline: 0;
		inset-block-end: 0;
		block-size: var(--nni-hair);
		background: var(--nni-cream);
		transform: scaleX(0);
		transform-origin: 50% 50%;
		transition: transform var(--nni-t-med) var(--nni-ease);
		pointer-events: none;
	}

	.nni-body .nni-field:not(.nni-field--pair):focus-within::after,
	.nni-body .nni-profile__field:focus-within::after {
		transform: scaleX(1);
	}

	.nni-body .nni-field:not(.nni-field--pair) :is(input, textarea, select):focus,
	.nni-body .nni-field:not(.nni-field--pair) :is(input, textarea, select):focus-visible,
	.nni-body .nni-profile__field :is(input, textarea, select):focus,
	.nni-body .nni-profile__field :is(input, textarea, select):focus-visible {
		border-bottom-color: var(--nni-line);
	}

	/* Die Beschriftung bleibt stehen und wird eine Spur heller. */
	.nni-body .nni-field:not(.nni-field--pair):focus-within > label,
	.nni-body .nni-profile__field:focus-within .nni-profile__label {
		color: var(--nni-cream-soft);
	}

	/* — die Schwelle — */

	.nni-gate-body .nni-key__row.nnim-line {
		position: relative;
	}

	.nni-gate-body .nni-key__row.nnim-line::after {
		content: "";
		position: absolute;
		top: var(--nnim-line-y, 0px);
		left: 50%;
		inline-size: var(--nnim-line-w, 0px);
		block-size: var(--nni-hair);
		margin-top: calc(-1 * var(--nni-hair));
		background: var(--nni-cream);
		transform: translateX(-50%) scaleX(0);
		transform-origin: 50% 50%;
		transition: transform var(--nni-t-med) var(--nni-ease);
		pointer-events: none;
	}

	.nni-gate-body .nni-key__row.nnim-line:focus-within::after {
		transform: translateX(-50%) scaleX(1);
	}

	.nni-gate-body .nni-key.is-error .nni-key__row.nnim-line::after {
		background: var(--nni-danger);
	}

	.nni-gate-body .nni-key:not(.is-error):not(.is-ok) .nni-key__row.nnim-line .nni-key__input:focus {
		border-bottom-color: var(--nni-line);
	}
}


/* ---------------------------------------------------------------------------
   11 · Riegel für reduzierte Bewegung

   Alles Obenstehende liegt bereits in (prefers-reduced-motion: no-preference).
   Dieser Block fängt den Fall ab, dass die Einstellung erst während der
   Sitzung gesetzt wird und motion.js seine Klassen noch nicht abgeräumt hat:
   danach steht jedes Element in seinem Endzustand, ohne Bewegung.
   ------------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {

	.nni-body .nnim-lines .nni-line,
	.nni-body .nnim-lines.is-in .nni-line {
		opacity: 1 !important;
		transform: none !important;
		transition: none !important;
	}

	.nni-body .nni-figure.nnim-veil,
	.nni-body .nni-figure.nnim-veil.is-in {
		clip-path: none !important;
		transition: none !important;
	}

	.nni-body .nni-main .nni-figure img,
	.nni-body .nni-main .nni-figure .nni-figure__ph,
	.nni-body.post-type-archive-nni_selection .nni-main .nni-grid .nni-figure img,
	.nni-body.post-type-archive-nni_selection .nni-main .nni-grid .nni-figure .nni-figure__ph {
		translate: none !important;
		scale: none !important;
		transition: none !important;
	}

	.nni-body .nnim-glow {
		display: none !important;
	}

	.nni-body .nni-card__title::after,
	.nni-body .nni-field::after,
	.nni-body .nni-profile__field::after,
	.nni-gate-body .nni-key__row.nnim-line::after {
		transition: none !important;
	}

	.nni-gate-body .nni-passage.is-open .nni-passage__seam::before,
	.nni-body.is-opening::before {
		display: none !important;
	}

	::view-transition-group(*),
	::view-transition-old(*),
	::view-transition-new(*) {
		animation: none !important;
	}
}
