/* ============================================
   SECOOLIO MOTION – Stufen & Choreografie
   ============================================
   Gehört zu js/motion.js. Das Inline-Skript im <head> setzt vor dem ersten
   Bild eine Klasse am <html>:

       motion-high     alles: echtes Glas, Tilt, Parallax, Partikel
       motion-medium   volle Choreografie, aber ohne Füllraten-Fresser
       motion-low      nur Ein-/Ausblendungen und Zustandswechsel

   Warum überhaupt Stufen? Nicht wegen der Rechenlast – ein iPhone 14 ist
   schnell. Sondern wegen der FÜLLRATE: `backdrop-filter` zwingt die GPU,
   den Bereich hinter dem Element bei jedem Bild neu weichzuzeichnen. Steht
   dahinter etwas, das sich bewegt (Leuchtkugeln, Verläufe, Partikel), ist
   das Ergebnis nie zwischenspeicherbar. Auf jeder Tool-Seite lagen so drei
   Dauer-Animationen unter einer 28-px-Unschärfe – gemessen 50 ms pro Bild
   im Leerlauf, also 20 Bilder pro Sekunde, ohne dass jemand etwas tat.

   Ab `medium` fällt deshalb JEDER backdrop-filter weg. Die Karten sind mit
   rgba(28,28,30,0.8) ohnehin fast deckend; sie bekommen hier einen etwas
   kräftigeren Hintergrund und sehen praktisch gleich aus – nur ohne die
   Ebene, die pro Bild neu berechnet werden muss.
   ============================================ */

/* ============================================
   1 – Echtes Glas nur in der Vollausbaustufe
   ============================================ */
:is(.motion-medium, .motion-low) :is(
    .cv-topbar, .cv-card, .cv-cat, .cv-toast,
    .navbar, .sidebar, .sidebar-overlay, .search-overlay, .chatbot-window,
    .scroll-top, .hero-search-trigger, .btn-forum-hero,
    .content-card, .feature-card, .stat-card, .yt-card, .verse-card
) {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
}

/* Ohne Unschärfe darunter wirkt der halbtransparente Hintergrund zu dünn –
   also etwas kräftiger mischen. Optisch derselbe Look, eine Ebene weniger. */
:is(.motion-medium, .motion-low) :is(.cv-card, .cv-cat, .content-card, .feature-card, .stat-card, .yt-card, .verse-card) {
    background: rgba(22, 22, 24, 0.94);
}
:is(.motion-medium, .motion-low) :is(.cv-topbar, .navbar) {
    background: rgba(0, 0, 0, 0.92);
}
:is(.motion-medium, .motion-low) :is(.cv-toast, .sidebar, .chatbot-window) {
    background: rgba(12, 12, 14, 0.97);
}
[data-theme="light"]:is(.motion-medium, .motion-low) :is(.cv-card, .cv-cat, .content-card, .feature-card, .stat-card, .yt-card, .verse-card) {
    background: rgba(252, 252, 254, 0.97);
}
[data-theme="light"]:is(.motion-medium, .motion-low) :is(.cv-topbar, .navbar) {
    background: rgba(255, 255, 255, 0.94);
}
[data-theme="light"]:is(.motion-medium, .motion-low) :is(.cv-toast, .sidebar, .chatbot-window) {
    background: rgba(255, 255, 255, 0.98);
}

/* ============================================
   2 – Große Unschärfe-Flächen einfrieren
   ============================================
   Die Leuchtkugeln sind 500 px groß, mit `filter: blur(80px)` belegt und
   laufen in einer Endlosschleife MIT scale() – jedes Bild also ein neuer
   Weichzeichner über eine Viertelmillion Pixel. Statisch sieht ein
   radialer Verlauf genauso aus und kostet exakt nichts. */
:is(.motion-medium, .motion-low) .hero-orb {
    filter: none;
    will-change: auto;
    animation: none;
    opacity: 0.5;
}
:is(.motion-medium, .motion-low) .hero-orb-1 {
    background: radial-gradient(circle closest-side, var(--glow-primary), transparent 100%);
}
:is(.motion-medium, .motion-low) .hero-orb-2 {
    background: radial-gradient(circle closest-side, var(--glow-accent), transparent 100%);
}
:is(.motion-medium, .motion-low) .hero-orb-3 {
    background: radial-gradient(circle closest-side, var(--glow-primary), transparent 100%);
}

/* Der große Radialverlauf hinter der Seite: bleibt sichtbar, hört auf zu
   atmen. `scale()` auf einer 900-px-Fläche erzwingt Neu-Rasterung.

   WICHTIG: Pseudo-Elemente dürfen NICHT in :is() stehen – die ganze Regel
   wäre sonst ungültig und würde stillschweigend verworfen. Deshalb hier
   ausgeschrieben, auch wenn es länger ist. */
.motion-medium .cv-body::before,   .motion-low .cv-body::before,
.motion-medium .vu-body::before,   .motion-low .vu-body::before,
.motion-medium .hero::before,      .motion-low .hero::before,
.motion-medium .hero::after,       .motion-low .hero::after,
.motion-medium .features::before,  .motion-low .features::before,
.motion-medium .yt-showcase::before, .motion-low .yt-showcase::before,
.motion-medium .tool-hero::before, .motion-low .tool-hero::before,
.motion-medium .hero-orbs,         .motion-low .hero-orbs {
    animation: none !important;
}

/* ============================================
   3 – Dauerläufer, die neu zeichnen statt zu schieben
   ============================================
   transform und opacity kann der Compositor allein bewegen. Alles andere
   – background-position, box-shadow, text-shadow, filter – bedeutet ein
   Neuzeichnen pro Bild. In der Vollausbaustufe ist das zu verkraften,
   darunter nicht. */
:is(.motion-medium, .motion-low) :is(
    .cv-title, .hero-title, .vu-title, .vu-404-code, .error-code,
    .cv-brand-dot, .scroll-progress, .stat-number, .verse-card,
    .flow-node-icon, .chatbot-toggle, .chatbot-pulse,
    .spec-chip-value, .afk-key, .afk-key-e, .hof-bit
) {
    animation: none !important;
}
/* Dieselben Fälle als Pseudo-Element – siehe Hinweis oben, die dürfen nicht
   mit in die :is()-Liste. */
.motion-medium .cv-drop::before,        .motion-low .cv-drop::before,
.motion-medium .vu-drop::before,        .motion-low .vu-drop::before,
.motion-medium .verse-card::after,      .motion-low .verse-card::after,
.motion-medium .verse-card:hover::after, .motion-low .verse-card:hover::after,
.motion-medium .glitch::before,         .motion-low .glitch::before,
.motion-medium .glitch::after,          .motion-low .glitch::after,
.motion-medium .yt-play-btn::after,     .motion-low .yt-play-btn::after,
.motion-medium .chatbot-status::before, .motion-low .chatbot-status::before,
.motion-medium .btn-glow::after,        .motion-low .btn-glow::after {
    animation: none !important;
}
/* Der Marken-Punkt darf weiter pulsieren – aber über transform statt über
   einen Schlagschatten, der bei jedem Bild neu gerechnet wird. */
:is(.motion-medium, .motion-low) .cv-brand-dot {
    animation: moDot 3s ease-in-out infinite !important;
    box-shadow: 0 0 14px rgba(41, 151, 255, 0.5);
}
@keyframes moDot {
    0%, 100% { transform: scale(1); opacity: 1; }
    50% { transform: scale(1.18); opacity: 0.75; }
}

/* Der Farbverlauf in den großen Überschriften bleibt – nur eingefroren an
   der schönsten Stelle statt endlos durchzulaufen. */
:is(.motion-medium, .motion-low) :is(.cv-title, .hero-title, .vu-title) {
    background-position: 50% 50%;
}

/* ============================================
   4 – Sparsamste Stufe
   ============================================
   „low" soll gestaltet aussehen, nicht kaputt: Ein- und Ausblendungen,
   Zustandswechsel und der Erfolgsmoment bleiben. Weg fällt alles, was
   dauerhaft läuft, ohne etwas mitzuteilen. */
.motion-low #particleCanvas { display: none !important; }
.motion-low :is(
    .cv-drop-icon, .cv-flow-arrow, .cv-eyebrow .dot, .cv-brand-dot,
    .yk-brand-icon, .sk-brand-icon, .vu-big-icon,
    /* Die vier neuen Werkzeugkaesten tragen dasselbe Marken-Emoji-Muster
       wie Yoink und Skribo und gehoeren deshalb in dieselbe Liste. */
    .cl-brand-icon, .bl-brand-icon, .pd-brand-icon, .qr-brand-icon,
    .feature-icon, .live-dot, .marquee-track, .scroll-indicator, .wheel,
    .tool-hero-icon, .tool-hero-orb, .cursor, .heartbeat, .hof-fill
) {
    animation: none !important;
}
/* Ohne Animation waere der Stick leer — dann lieber halb gefuellt stehen
   lassen, sonst sieht das Bild kaputt aus statt ruhig. Der Fuellstand laeuft
   waagerecht (siehe .hof-fill in style.css), deshalb scaleX; und die Bloecke
   stehen auf halber Strecke der 120-px-Bahn statt bei 30 % ihrer eigenen
   10-px-Breite, wo sie praktisch am Anfang klebten. */
:is(.motion-medium, .motion-low) .hof-bit { opacity: 0.5; transform: translateX(55px); }
.motion-low .hof-fill { transform: scaleX(0.55); }
@media (max-width: 560px) {
    :is(.motion-medium, .motion-low) .hof-bit { transform: translateX(12px); }
}
.motion-low .chatbot-status::before,
.motion-low .cv-body::before,
.motion-low .vu-body::before {
    animation: none !important;
}
/* Ausdrücklich NICHT abgeschaltet, weil es Rückmeldung ist und kein Zierrat:
   .cv-ring.indeterminate (Warte-Spinner), .spinner, .typing-indicator span,
   .sk-rec-btn.recording .sk-rec-dot (läuft gerade eine Aufnahme). */

/* ============================================
   5 – Nichts animieren, was gerade niemand sieht
   ============================================
   js/motion.js hängt .mo-idle an ambiente Dauerläufer, sobald sie aus dem
   Sichtfeld scrollen. Eine pausierte Animation kostet nichts. */
.mo-idle, .mo-idle::before, .mo-idle::after {
    animation-play-state: paused !important;
}

/* Sanfter Stufenwechsel. js/motion.js setzt diese Klasse für 0,7 s, wenn die
   Messung ergeben hat, dass das Gerät weniger verträgt als geschätzt. Ohne
   sie würde das Glas hart wegschalten – genau das sichtbare Umschalten, das
   nicht passieren soll. */
.motion-switching :is(
    .cv-topbar, .cv-card, .cv-cat, .cv-toast, .navbar, .sidebar,
    .content-card, .feature-card, .stat-card, .yt-card, .verse-card,
    .hero-orb, #particleCanvas
) {
    transition: background-color 0.55s ease, backdrop-filter 0.55s ease,
                -webkit-backdrop-filter 0.55s ease, filter 0.55s ease,
                opacity 0.55s ease !important;
}

/* ============================================
   6 – Choreografie: Stufenwechsel als Sequenz
   ============================================
   Bisher wurde beim Wechsel nur eine Klasse getauscht und der ganze Block
   ist am Stück erschienen. Jetzt kommen die Kinder gestaffelt herein –
   derselbe Aufwand für den Browser, ein völlig anderer Eindruck.
   --i setzt js/motion.js beim Wechsel pro Kind. */
.stage-in > * {
    animation: moStageChild 0.62s cubic-bezier(0.16, 1, 0.3, 1) both;
    animation-delay: calc(var(--i, 0) * 55ms);
}
@keyframes moStageChild {
    from { opacity: 0; transform: translateY(16px) scale(0.985); }
    to   { opacity: 1; transform: none; }
}
/* Kommen die Kinder gestaffelt herein, braucht der Container selbst keine
   eigene Einblendung mehr – sonst bewegen sich zwei Ebenen gegeneinander. */
.cv-stage.stage-seq { animation: none; }

/* In der sparsamen Stufe ohne Versatz – aber der Wechsel bleibt sichtbar. */
.motion-low .stage-in > * { animation-delay: 0s; animation-duration: 0.3s; }

/* ============================================
   7 – Choreografie: Fortschritt
   ============================================ */
/* Mitlaufender Punkt an der Spitze des Rings. Dreht sich über transform,
   der Winkel kommt als Custom Property aus dem JS. */
.cv-ring-head {
    position: absolute;
    inset: 0;
    pointer-events: none;
    transform: rotate(calc(var(--deg, 0) * 1deg));
    transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.cv-ring-head::after {
    content: '';
    position: absolute;
    top: 3px;
    left: 50%;
    width: 8px; height: 8px;
    margin-left: -4px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 12px var(--accent);
}
.motion-low .cv-ring-head::after { box-shadow: none; }
.cv-ring.indeterminate .cv-ring-head { opacity: 0; }

/* Impuls nach außen, wenn 100 % erreicht sind. */
.mo-pulse-ring {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 2px solid var(--primary);
    pointer-events: none;
    animation: moPulseRing 0.9s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes moPulseRing {
    from { opacity: 0.85; transform: scale(0.9); }
    to   { opacity: 0; transform: scale(1.45); }
}

/* ============================================
   8 – Choreografie: Erfolgsmoment
   ============================================ */
/* Gezeichnetes Häkchen. Die Längen setzt js/motion.js über die
   Web-Animations-API – hier steht nur, wie es aussieht. */
.mo-check { display: block; margin: 0 auto; }
.mo-check path, .mo-check circle {
    fill: none;
    stroke: currentColor;
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Zeilenweise erscheinender Text (Skribo-Transkript, Log-Ausgaben). */
.mo-line {
    animation: moLine 0.45s cubic-bezier(0.16, 1, 0.3, 1) both;
    animation-delay: calc(var(--i, 0) * 40ms);
}
@keyframes moLine {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}
/* Variante ohne transform, für Inline-Elemente in einem <pre>: dort muss der
   Zeilenumbruch beim Text bleiben, damit der kopierte Inhalt stimmt. */
.mo-fade-in {
    animation: moFadeIn 0.4s ease both;
    animation-delay: calc(var(--i, 0) * 38ms);
}
@keyframes moFadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* Wellenartiger Eintritt für Reihen kleiner Elemente (Plattform-Chips,
   Format-Kacheln). */
.mo-wave {
    animation: moWave 0.55s cubic-bezier(0.34, 1.56, 0.64, 1) both;
    animation-delay: calc(var(--i, 0) * 45ms);
}
@keyframes moWave {
    from { opacity: 0; transform: translateY(14px) scale(0.9); }
    to   { opacity: 1; transform: none; }
}

/* ============================================
   9 – Reduzierte Bewegung
   ============================================
   Bisher stand hier animation-duration: 0.001ms. Das lässt die Animation
   in einem Wimpernschlag durchlaufen und im ENDZUSTAND stehen – der
   Hintergrundverlauf blieb dadurch dauerhaft auf scale(1.25) hängen.
   `animation: none` behält stattdessen den Ausgangszustand. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation: none !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
    /* Ausnahme: Warte-Anzeigen. Ohne sie sähe die Seite eingefroren aus –
       das ist keine Bewegungsempfindlichkeit, das ist ein Bug. Die Namen
       werden hier ausdrücklich wiederholt, weil die Regel darüber sie sonst
       mit abräumt; auf Seiten ohne das jeweilige Keyframe verpufft die
       Deklaration folgenlos. */
    .cv-ring.indeterminate svg { animation: cvSpin 1.4s linear infinite !important; }
    .spinner { animation: vuSpin 0.8s linear infinite !important; }
    .typing-indicator span { animation: typingDot 1.4s infinite !important; }
    .reveal, .reveal-scale, .stage-in > *, .mo-line, .mo-wave, .mo-fade-in {
        opacity: 1 !important;
        transform: none !important;
    }
    .hero-orb, #particleCanvas { display: none !important; }
}
