/* ==========================================================================
   fx.css — Bewegungsbibliothek für statische Seiten
   Kein Framework, keine Abhängigkeit, palettenneutral.

   Alle Farben kommen aus currentColor oder --fx-*, damit dieselbe Datei
   in jedem Projekt mit dessen eigener Palette läuft.

   Jede Bewegung steht in @media (prefers-reduced-motion: no-preference).
   Wer Bewegung abgeschaltet hat, bekommt die Seite statisch und vollständig.
   ========================================================================== */

:root {
  --fx-dur:   10s;    /* Grundtempo für Dauerbewegungen */
  --fx-ease:  cubic-bezier(.22, .61, .36, 1);
  --fx-rise:  18px;   /* Hubhöhe beim Einblenden */
  --fx-ring:  currentColor;
}

/* --------------------------------------------------------------------------
   1. Dauerbewegung — für Icons, Logos, Statuspunkte
   -------------------------------------------------------------------------- */

@keyframes fx-spin    { to { transform: rotate(360deg); } }

@keyframes fx-bob     { from { transform: translateY(0); }
                        to   { transform: translateY(-9px); } }

@keyframes fx-float   { 0%, 100% { transform: translateY(0)    rotate(0deg); }
                        30%      { transform: translateY(-9px) rotate(-1.2deg); }
                        65%      { transform: translateY(4px)  rotate(1deg); } }

@keyframes fx-breathe { 0%, 100% { transform: scale(1);    opacity: .6; }
                        50%      { transform: scale(1.35); opacity: 1; } }

@keyframes fx-ring    { from { box-shadow: 0 0 0 0    var(--fx-ring); opacity: .5; }
                        to   { box-shadow: 0 0 0 12px var(--fx-ring); opacity: 0; } }

@keyframes fx-flicker { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

@keyframes fx-flicker-hard { 0% { opacity: 1; } 25% { opacity: .1; }
                             50% { opacity: 1; } 75% { opacity: .4; } 100% { opacity: 1; } }

@keyframes fx-shake   { 0%, 100% { transform: translate(0)               skewX(0deg); }
                        25%      { transform: translate(-3px, 1px)       skewX(-.4deg); }
                        50%      { transform: translate(3px, -1px)       skewX(.4deg); }
                        75%      { transform: translate(-1px, 0)         skewX(-.2deg); } }

@keyframes fx-pop     { from { transform: scale(0);   opacity: 0; }
                        to   { transform: scale(1);   opacity: 1; } }

@media (prefers-reduced-motion: no-preference) {
  .fx-spin      { animation: fx-spin var(--fx-dur) linear infinite; }
  .fx-bob       { animation: fx-bob calc(var(--fx-dur) / 2) ease-in-out infinite alternate; }
  .fx-float     { animation: fx-float var(--fx-dur) ease-in-out infinite; }
  .fx-breathe   { animation: fx-breathe 2.4s ease-in-out infinite; }
  .fx-flicker   { animation: fx-flicker 4s ease-in-out infinite; }
  .fx-flicker-hard { animation: fx-flicker-hard 6s steps(1, end) infinite; }
  .fx-pop       { animation: fx-pop .45s var(--fx-ease) both; }

  /* Nur auf Zeigergeräten: Wackeln beim Überfahren. Auf Touch würde es
     bei jeder Berührung feuern und nur stören. */
  @media (hover: hover) {
    .fx-shake-hover:hover,
    .fx-shake-hover:focus-visible { animation: fx-shake .4s ease-in-out; }
  }
}

/* Pulsierender Ring — der klassische Lebendpunkt (geöffnet/live/neu).
   Braucht ein Element mit fester Größe und border-radius. */
.fx-dot {
  position: relative;
  display: inline-block;
  width: .5rem; height: .5rem;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}
@media (prefers-reduced-motion: no-preference) {
  .fx-dot::after {
    content: "";
    position: absolute; inset: 0;
    border-radius: 50%;
    animation: fx-ring 1.8s ease-out infinite;
  }
}

/* --------------------------------------------------------------------------
   2. Einblenden beim Scrollen
   Versteckt wird ausschließlich, wenn JS läuft (html.fx-js) UND der Nutzer
   Bewegung zulässt. Ohne JS oder mit reduced-motion steht alles sofort da.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .fx-js [data-fx-reveal] {
    opacity: 0;
    transform: translateY(var(--fx-rise));
    transition: opacity .7s var(--fx-ease), transform .7s var(--fx-ease);
  }
  .fx-js [data-fx-reveal="scale"] { transform: scale(.94); }
  .fx-js [data-fx-reveal="left"]  { transform: translateX(calc(var(--fx-rise) * -1.6)); }
  .fx-js [data-fx-reveal="right"] { transform: translateX(calc(var(--fx-rise) *  1.6)); }

  .fx-js [data-fx-reveal].fx-in {
    opacity: 1;
    transform: none;
  }

  /* Gestaffelt: Kinder laufen nacheinander ein */
  .fx-js [data-fx-stagger] > * { transition-delay: calc(var(--fx-i, 0) * 80ms); }
}

/* --------------------------------------------------------------------------
   3. Scroll-Fortschritt — ein Element wandert, während die Sektion durchläuft
   Nutzt die native Scroll-Timeline, wo es sie gibt (Chrome/Edge 115+,
   Safari 26+). Firefox kennt sie nicht, dort setzt fx.js --fx-p per Scroll.
   Beide Wege schreiben in dieselbe Variable, das CSS bleibt identisch.
   -------------------------------------------------------------------------- */

/* Die Strecke ist die Bezugsbreite, das Element sitzt absolut darin.
   left in Prozent rechnet gegen die Strecke, translate in Prozent gegen das
   Element selbst — die Kombination schiebt es exakt von Kante zu Kante,
   ohne dass man eine feste Pixelbreite kennen muss. */
[data-fx-track] { position: relative; }
[data-fx-rail]  { position: relative; }

[data-fx-move] {
  position: absolute;
  left: 0;
  will-change: left, translate;
}

@media (prefers-reduced-motion: no-preference) {
  [data-fx-move] {
    left: calc(var(--fx-p, 0) * 100%);
    translate: calc(var(--fx-p, 0) * -100%)
               calc(var(--fx-p, 0) * var(--fx-lift, 0px));
  }

  @supports (animation-timeline: view()) {
    @keyframes fx-progress { to { --fx-p: 1; } }

    [data-fx-track] { view-timeline-name: --fx-tl; }

    .fx-native [data-fx-move] {
      animation-name: fx-progress;
      /* MUSS auto sein. Die Kurzschreibweise `animation:` würde hier 0s
         eintragen und die Timeline damit wirkungslos machen. */
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: --fx-tl;
      animation-range: entry 20% cover 80%;
    }
  }
}

/* --fx-p muss registriert sein, damit es sich animieren lässt */
@property --fx-p {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

/* --------------------------------------------------------------------------
   4. Spotlight
   Ein wandernder Lichtkegel über einer Fläche. Bewusst so abgestimmt, dass
   der Text darunter lesbar bleibt: Der Kegel hellt auf, statt den Rest
   abzudunkeln. Ein Effekt darf Inhalt nie verbergen — sonst sperrt er
   Tastatur- und Screenreader-Nutzer von etwas aus, das alle sehen sollen.
   -------------------------------------------------------------------------- */

[data-fx-spotlight] { position: relative; isolation: isolate; }

[data-fx-spotlight] > .fx-light {
  position: absolute; inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(
    circle var(--fx-light-size, 220px) at var(--fx-lx, 50%) var(--fx-ly, 40%),
    color-mix(in srgb, var(--fx-light-color, #fff) 22%, transparent) 0%,
    transparent 70%
  );
  mix-blend-mode: screen;
}
/* Inhalt liegt über dem Licht, damit nichts verschluckt wird.
   :where() setzt die Spezifität auf null — sonst würde diese Regel ein
   position:absolute aus dem Projekt-CSS überschreiben und absolut
   positionierte Hero-Bilder zurück in den Fluss zwingen. */
:where([data-fx-spotlight]) > :where(:not(.fx-light)) { position: relative; z-index: 2; }

@media (prefers-reduced-motion: reduce) {
  [data-fx-spotlight] > .fx-light { display: none; }
}

/* --------------------------------------------------------------------------
   5. Laufband — endlose Textschleife
   -------------------------------------------------------------------------- */

.fx-marquee { overflow: hidden; display: flex; gap: var(--fx-gap, 2rem); }
.fx-marquee > * { flex: 0 0 auto; }

@keyframes fx-slide { to { transform: translateX(calc(-100% - var(--fx-gap, 2rem))); } }

@media (prefers-reduced-motion: no-preference) {
  .fx-marquee > * { animation: fx-slide var(--fx-dur) linear infinite; }
}
@media (prefers-reduced-motion: reduce) {
  .fx-marquee { overflow-x: auto; }   /* dann eben von Hand scrollbar */
}
