diff --git a/web/frontend/index.html b/web/frontend/index.html
index e8f6bcb..2e2a1f2 100644
--- a/web/frontend/index.html
+++ b/web/frontend/index.html
@@ -624,23 +624,46 @@ function moveNavigation(to) {
const after = island.getBoundingClientRect();
if (!before.width || !after.width) return;
+ const dx = before.left - after.left;
+ const dy = before.top - after.top;
+ const sx = before.width / after.width;
+ const sy = before.height / after.height;
+
+ // **Zwei Züge, nicht einer.**
+ //
+ // Ein reiner Weg von A nach B sieht aus, als würde ein Kasten verschoben.
+ // Schöner ist, was die Leiste erst *in sich* zusammenziehen lässt und dann
+ // erst umziehen — wie ein Gummiband, das sich sammelt, bevor es schnellt.
+ //
+ // Welche Richtung sich zuerst sammelt, hängt davon ab, wohin es geht:
+ // Wer nach **oben** zieht, wird flach — also klappt zuerst die Höhe
+ // zusammen, und die Breite kommt erst beim Hinüberschieben. Wer an die
+ // **Seite** zieht, wird schmal — also zuerst die Breite, die Höhe danach.
+ //
+ // Die Zwischenstation steht deshalb noch an der alten Stelle, aber schon
+ // mit dem Zielmaß in der einen Richtung.
+ const gathered = to === 'top'
+ ? `translate(${dx}px, ${dy}px) scale(${sx}, 1)` // Höhe zuerst
+ : `translate(${dx}px, ${dy}px) scale(1, ${sy})`; // Breite zuerst
+
island.animate(
[
- {
- transformOrigin: 'top left',
- transform: `translate(${before.left - after.left}px, ${before.top - after.top}px)`
- + ` scale(${before.width / after.width}, ${before.height / after.height})`,
- },
- { transformOrigin: 'top left', transform: 'none' },
+ { transformOrigin: 'top left', transform: `translate(${dx}px, ${dy}px) scale(${sx}, ${sy})`,
+ easing: 'cubic-bezier(.5,0,.75,0)', offset: 0 },
+ // Gesammelt — noch am alten Ort, aber schon im Zielmaß der einen Seite.
+ { transformOrigin: 'top left', transform: gathered,
+ easing: 'cubic-bezier(.22,1,.36,1)', offset: 0.42 },
+ // Und hinüber. Das leichte Überschwingen am Ende ist das Gummiband.
+ { transformOrigin: 'top left', transform: 'none', offset: 1 },
],
- { duration: 420, easing: 'cubic-bezier(.32,.72,0,1)' }
+ { duration: 520 }
);
// Die Beschriftungen gegen die Stauchung aufblenden, damit aus dem
// Verziehen kein Lesen wird.
island.animate(
- [{ opacity: 0.55 }, { opacity: 1 }],
- { duration: 260, easing: 'ease-out' }
+ [{ opacity: 0.35 }, { opacity: 0.5, offset: 0.42 }, { opacity: 1 }],
+ { duration: 420, easing: 'ease-out' }
);
}