{
  "name": "mzone-ui",
  "version": "1.1.0",
  "summary": "Design-Vokabular fuer maierzone-Artefakte. CSS-Klassen fuer Report-Chrome, Daten-Darstellung, Status und Bedienelemente; ES-Module fuer Canvas- und WebGL-Effekte, Scroll-Choreographie und chart.js-Defaults.",
  "sinceLast": "Neu gegenueber 1.0.0: zwanzig Effekt-Module. Text (fx-decrypt, fx-particle-reveal, fx-glitch), Hintergrund (fx-glyphrain, fx-hexfloat, fx-hero), Interaktion (fx-ripple mit WebGL-Brechung, fx-magnify, fx-confetti, fx-shatter, fx-vhs), Optik (fx-frost, fx-bend, fx-peel), Energie (fx-flame, fx-forcefield, fx-laser), Scroll (fx-scroll, fx-particle-scroll) und 3D (fx-object3d). Dazu die Klassen mz-hero, mz-reveal, mz-sr-only, mz-fx-overlay und ein Bedienelement-Set (mz-controls, mz-btn, mz-range, mz-select). 1.0.0 bleibt unveraendert erreichbar.",
  "usage": {
    "css": "<link rel=\"stylesheet\" href=\"/lib/mzone-ui@1.1.0/mzone.css\">",
    "bodyClass": "Das <body> braucht class=\"mz-body\", sonst greifen Hintergrund und Grundschrift nicht.",
    "shell": "<body class=\"mz-body\"><main class=\"mz-page\"> … </main></body>",
    "theme": "Dunkel ist Default. Hell kommt automatisch bei prefers-color-scheme: light. Erzwingen mit <html data-theme=\"light\"> bzw. \"dark\". Alle fx-Module lesen ihre Farben bei einem Wechsel neu.",
    "prefix": "Alle Klassen beginnen mit mz- und kollidieren nicht mit artefakteigenem CSS.",
    "modules": "Die fx-Module sind ES-Module und werden mit <script type=\"module\"> importiert. Sie haben keine externen Abhaengigkeiten; untereinander teilen sie fx-core.js, das automatisch mitgeladen wird."
  },
  "classes": [
    {
      "name": "mz-body",
      "group": "Geruest",
      "purpose": "Grundflaeche, Textfarbe und Grundschrift. Gehoert auf <body>.",
      "example": "<body class=\"mz-body\">"
    },
    {
      "name": "mz-page",
      "group": "Geruest",
      "purpose": "Zentrierter Inhaltsbereich, max. 980px, mit Aussenabstand. Variante mz-page--wide fuer 1280px.",
      "example": "<main class=\"mz-page\"> … </main>"
    },
    {
      "name": "mz-prose",
      "group": "Geruest",
      "purpose": "Begrenzt direkte Absaetze und Listen darin auf lesbare Zeilenlaenge. Tabellen und Karten bleiben breit.",
      "example": "<div class=\"mz-prose\"><p>Flie&szlig;text …</p></div>"
    },
    {
      "name": "mz-eyebrow",
      "group": "Typografie",
      "purpose": "Kleine Versalien-Zeile ueber einer Ueberschrift, Monospace mit weitem Zeichenabstand. Varianten: --muted, --ok, --info, --special.",
      "example": "<div class=\"mz-eyebrow\">ABNAHME BESTANDEN</div>"
    },
    {
      "name": "mz-title",
      "group": "Typografie",
      "purpose": "Seitentitel, skaliert mit der Viewportbreite.",
      "example": "<h1 class=\"mz-title\">ADR-007</h1>"
    },
    {
      "name": "mz-subtitle",
      "group": "Typografie",
      "purpose": "Untertitel unter dem Seitentitel, gedaempft und auf Lesebreite begrenzt.",
      "example": "<p class=\"mz-subtitle\">Tool-Klassifikation am lebenden System</p>"
    },
    {
      "name": "mz-h2",
      "group": "Typografie",
      "purpose": "Abschnittsueberschrift mit Trennlinie darunter.",
      "example": "<h2 class=\"mz-h2\">Befunde</h2>"
    },
    {
      "name": "mz-h3",
      "group": "Typografie",
      "purpose": "Unterabschnitt ohne Trennlinie.",
      "example": "<h3 class=\"mz-h3\">Mittel</h3>"
    },
    {
      "name": "mz-lead",
      "group": "Typografie",
      "purpose": "Hervorgehobener Einleitungsabsatz.",
      "example": "<p class=\"mz-lead\">Der Rebuild loest alle fuenf Pain Points.</p>"
    },
    {
      "name": "mz-muted",
      "group": "Typografie",
      "purpose": "Gedaempfte Textfarbe fuer Nebeninformation.",
      "example": "<span class=\"mz-muted\">zuletzt geprueft 2026-08-03</span>"
    },
    {
      "name": "mz-small",
      "group": "Typografie",
      "purpose": "Kleinerer Flie&szlig;text fuer Fu&szlig;noten und Randbemerkungen.",
      "example": "<p class=\"mz-small mz-muted\">Quelle: request_audit</p>"
    },
    {
      "name": "mz-code",
      "group": "Typografie",
      "purpose": "Inline-Code mit Rahmen und Monospace.",
      "example": "<code class=\"mz-code\">publishArtifact</code>"
    },
    {
      "name": "mz-pre",
      "group": "Typografie",
      "purpose": "Code-Block. Scrollt in sich selbst statt die Seite quer laufen zu lassen.",
      "example": "<pre class=\"mz-pre\">notices: [ … ]</pre>"
    },
    {
      "name": "mz-card",
      "group": "Karten",
      "purpose": "Umrandeter Block. Varianten: --ok (gruen), --danger (rot), --accent (linker Balken).",
      "example": "<div class=\"mz-card\"><div class=\"mz-card__label\">ARTEFAKT</div>Inhalt</div>"
    },
    {
      "name": "mz-card__label",
      "group": "Karten",
      "purpose": "Versalien-Label am Kartenkopf. Faerbt sich mit der Kartenvariante mit.",
      "example": "<div class=\"mz-card__label\">HEUTE</div>"
    },
    {
      "name": "mz-card__note",
      "group": "Karten",
      "purpose": "Gedaempfte Erlaeuterung am Kartenfu&szlig;.",
      "example": "<p class=\"mz-card__note\">Ein Fehlalarm und eine Erfolgsmeldung.</p>"
    },
    {
      "name": "mz-grid",
      "group": "Layout",
      "purpose": "Automatisch umbrechendes Raster. Varianten: --2 (breitere Spalten), --4 (schmalere, fuer Stat-Kacheln).",
      "example": "<div class=\"mz-grid mz-grid--4\"> … </div>"
    },
    {
      "name": "mz-table-wrap",
      "group": "Daten",
      "purpose": "Pflicht-Umschlag um jede mz-table. Traegt Rahmen und horizontales Scrollen.",
      "example": "<div class=\"mz-table-wrap\"><table class=\"mz-table\"> … </table></div>"
    },
    {
      "name": "mz-table",
      "group": "Daten",
      "purpose": "Tabelle mit Versalien-Kopfzeile und feinen Zeilentrennern. Variante --mono fuer Zahlenkolonnen.",
      "example": "<table class=\"mz-table\"><tr><th>#</th><th>Status</th></tr></table>"
    },
    {
      "name": "mz-table__key",
      "group": "Daten",
      "purpose": "Erste Spalte als gedaempfte Beschriftung, ohne Umbruch.",
      "example": "<td class=\"mz-table__key\">Groesse</td>"
    },
    {
      "name": "mz-stat",
      "group": "Daten",
      "purpose": "Kennzahl-Kachel. Varianten: --ok, --warn, --danger faerben den Wert.",
      "example": "<div class=\"mz-stat\"><div class=\"mz-stat__label\">Bundles</div><div class=\"mz-stat__value\">23<span class=\"mz-stat__unit\">Stk</span></div></div>"
    },
    {
      "name": "mz-pill",
      "group": "Status",
      "purpose": "Kleine Status-Kapsel. Varianten: --ok, --warn, --danger, --info, --accent; --solid fuellt die Flaeche.",
      "example": "<span class=\"mz-pill mz-pill--ok\">ERFUELLT</span>"
    },
    {
      "name": "mz-meta",
      "group": "Report-Chrome",
      "purpose": "Zweispaltiger Kopfblock aus Schluessel und Wert.",
      "example": "<dl class=\"mz-meta\"><dt class=\"mz-meta__key\">Datum</dt><dd class=\"mz-meta__value\">2026-08-03</dd></dl>"
    },
    {
      "name": "mz-findings",
      "group": "Report-Chrome",
      "purpose": "Liste von Befunden. Jedes Kind ist ein mz-finding; Severity ueber --ok, --warn, --danger.",
      "example": "<ol class=\"mz-findings\"><li class=\"mz-finding mz-finding--warn\"> … </li></ol>"
    },
    {
      "name": "mz-finding",
      "group": "Report-Chrome",
      "purpose": "Einzelner Befund mit farbigem Balken links, Kopfzeile und Text.",
      "example": "<li class=\"mz-finding mz-finding--danger\"><div class=\"mz-finding__head\"><span class=\"mz-finding__id\">B1</span><span class=\"mz-pill mz-pill--danger\">mittel</span></div><div class=\"mz-finding__body\">Importmap-Fehlalarm.</div></li>"
    },
    {
      "name": "mz-crossrefs",
      "group": "Report-Chrome",
      "purpose": "Verweisleiste am Seitenfu&szlig;. Kinder sind mz-crossref.",
      "example": "<nav class=\"mz-crossrefs\"><a class=\"mz-crossref\" href=\"/published/wiki-adr-006\">ADR-006</a></nav>"
    },
    {
      "name": "mz-footer",
      "group": "Report-Chrome",
      "purpose": "Abschlusszeile in Monospace, gedaempft.",
      "example": "<footer class=\"mz-footer\">mzone-publisher · 2026-08-03</footer>"
    },
    {
      "name": "mz-chart",
      "group": "Diagramme",
      "purpose": "Container fester Hoehe fuer ein chart.js-Canvas. Variante --tall fuer 420px.",
      "example": "<div class=\"mz-chart\"><canvas id=\"c\"></canvas></div>"
    },
    {
      "name": "mz-fx-canvas",
      "group": "Effekte",
      "purpose": "Vollflaechiger Hintergrund-Canvas hinter dem Inhalt. Wird von den Hintergrund-Modulen selbst angelegt. Bei prefers-reduced-motion per CSS ausgeblendet.",
      "example": "<canvas class=\"mz-fx-canvas\"></canvas>"
    },
    {
      "name": "mz-fx-overlay",
      "group": "Effekte",
      "purpose": "Canvas ueber einem Container. Anders als mz-fx-canvas bleibt es bei reduced-motion sichtbar, weil die Module dort ihr stehendes Bild hineinzeichnen. Variante --fixed fuer Vollbild-Overlays. Legen die Module selbst an.",
      "example": "<canvas class=\"mz-fx-overlay\"></canvas>"
    },
    {
      "name": "mz-fx-ghost",
      "group": "Effekte",
      "purpose": "Geisterschicht von fx-glitch: geklonter Inhalt, aria-hidden, nicht anklickbar. Wird vom Modul angelegt.",
      "example": "<div class=\"mz-fx-ghost\" aria-hidden=\"true\"> … </div>"
    },
    {
      "name": "mz-hero",
      "group": "Effekte",
      "purpose": "Titelflaeche fuer fx-hero, fx-particle-reveal, fx-ripple und fx-hexfloat. Bringt Rahmen, Radius, Mindesthoehe und einen eigenen Stacking-Kontext mit; der Inhalt liegt automatisch ueber dem Effekt. Variante --tall fuer 340px.",
      "example": "<section class=\"mz-hero\" data-text=\"MaierZone\"><h1 class=\"mz-title\">MaierZone</h1></section>"
    },
    {
      "name": "mz-reveal",
      "group": "Scroll",
      "purpose": "Startzustand einer Einblendung: unsichtbar und leicht versetzt. fx-scroll.reveal() setzt die Klasse und schaltet mit mz-reveal--in um. Feineinstellung ueber --mz-reveal-y, --mz-reveal-duration, --mz-reveal-delay.",
      "example": "<div class=\"mz-card mz-reveal\"> … </div>"
    },
    {
      "name": "mz-scroll-progress",
      "group": "Scroll",
      "purpose": "Duenner Fortschrittsbalken am oberen Rand. Wird von fx-scroll.progressBar() angelegt.",
      "example": "<div class=\"mz-scroll-progress\" aria-hidden=\"true\"></div>"
    },
    {
      "name": "mz-sr-only",
      "group": "Barrierefreiheit",
      "purpose": "Sichtbar fuer Screenreader, unsichtbar auf dem Bildschirm. fx-decrypt und fx-particle-reveal legen damit den echten Text neben ihre Darstellung.",
      "example": "<span class=\"mz-sr-only\">MaierZone</span>"
    },
    {
      "name": "mz-controls",
      "group": "Bedienelemente",
      "purpose": "Leiste fuer Regler und Schalter eines interaktiven Artefakts. Kinder sind mz-control oder mz-btn.",
      "example": "<div class=\"mz-controls\"> … </div>"
    },
    {
      "name": "mz-control",
      "group": "Bedienelemente",
      "purpose": "Einzelnes beschriftetes Bedienelement. Enthaelt mz-control__label, optional mz-control__value und das Eingabefeld.",
      "example": "<label class=\"mz-control\"><span class=\"mz-control__label\">Zoom</span><input class=\"mz-range\" type=\"range\"></label>"
    },
    {
      "name": "mz-control__value",
      "group": "Bedienelemente",
      "purpose": "Aktueller Wert eines Reglers, Monospace mit gleichbreiten Ziffern — springt beim Ziehen nicht.",
      "example": "<span class=\"mz-control__value\">2.4</span>"
    },
    {
      "name": "mz-btn",
      "group": "Bedienelemente",
      "purpose": "Schaltflaeche in Akzentfarbe. Variante --ghost fuer nachrangige Aktionen; aria-pressed=\"true\" faerbt sie gefuellt.",
      "example": "<button class=\"mz-btn\" type=\"button\">Abspielen</button>"
    },
    {
      "name": "mz-range",
      "group": "Bedienelemente",
      "purpose": "Schieberegler in mz-Farben, inklusive Fokusring.",
      "example": "<input class=\"mz-range\" type=\"range\" min=\"1\" max=\"8\" step=\"0.1\">"
    },
    {
      "name": "mz-select",
      "group": "Bedienelemente",
      "purpose": "Auswahlfeld in Monospace, passend zu mz-range.",
      "example": "<select class=\"mz-select\"><option>webgl</option><option>2d</option></select>"
    }
  ],
  "modules": [
    {
      "path": "/lib/mzone-ui@1.1.0/fx-particles.js",
      "export": "particleField",
      "group": "Hintergrund",
      "purpose": "Driftendes Partikelfeld mit Verbindungslinien und Maus-Parallaxe. Legt sein Canvas selbst an.",
      "options": "density, maxParticles, color, lineColor, linkDistance, speed, radius, parallax, opacity, target",
      "example": "import { particleField } from '/lib/mzone-ui@1.1.0/fx-particles.js';\nconst fx = particleField({ parallax: 30 });"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-grid.js",
      "export": "pulseGrid",
      "group": "Hintergrund",
      "purpose": "Technisches Raster, durch das in Abstaenden eine Welle laeuft. Zwischen den Wellen steht das Bild still.",
      "options": "cell, lineColor, pulseColor, baseOpacity, interval, pulseSpeed, pulseWidth, target",
      "example": "import { pulseGrid } from '/lib/mzone-ui@1.1.0/fx-grid.js';\nconst fx = pulseGrid({ cell: 44, interval: 7 });"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-hexfloat.js",
      "export": "hexFloat",
      "group": "Hintergrund",
      "purpose": "Wabenraster, das langsam atmet; unter dem Zeiger leuchtet die Nachbarschaft auf. Ohne Ziel Vollbild, mit Ziel nur dort. Bei reduced-motion bleibt das Raster stehen statt zu verschwinden.",
      "signature": "hexFloat(target?, options?) — Ziel als 1. Argument wie bei allen fx-Modulen. Die aeltere Form hexFloat({ target, … }) bleibt gueltig.",
      "options": "target, size, baseOpacity, liveRatio, speed, pointer, color, glow",
      "example": "import { hexFloat } from '/lib/mzone-ui@1.1.0/fx-hexfloat.js';\nconst fx = hexFloat('.mz-hero', { size: 26 });\nconst bg = hexFloat();   // ohne Ziel: Vollbild-Hintergrund"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-glyphrain.js",
      "export": "glyphRain",
      "group": "Hintergrund",
      "purpose": "Fallende Glyphen-Spalten mit leuchtendem Kopf und verglimmendem Schweif. Ohne Ziel Vollbild, mit Ziel nur dort.",
      "signature": "glyphRain(target?, options?) — Ziel als 1. Argument wie bei allen fx-Modulen. Die aeltere Form glyphRain({ target, … }) bleibt gueltig.",
      "options": "target, fontSize, glyphs, speed, density, fade, color, headColor, opacity",
      "example": "import { glyphRain } from '/lib/mzone-ui@1.1.0/fx-glyphrain.js';\nconst fx = glyphRain('.mz-hero', { speed: 14 });\nconst bg = glyphRain();   // ohne Ziel: Vollbild-Hintergrund"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-hero.js",
      "export": "hero",
      "group": "Hintergrund",
      "purpose": "Langsam wanderndes Farbfeld (WebGL-Rauschen) als Untergrund fuer Titelbereiche. Faellt ohne WebGL und bei reduced-motion auf denselben Farbaufbau als stehendes Bild zurueck; welcher Weg lief, steht in fx.renderer.",
      "options": "speed, scale, opacity, vignette, parallax, colors",
      "example": "import { hero } from '/lib/mzone-ui@1.1.0/fx-hero.js';\nconst fx = hero('.mz-hero', { speed: 0.06, opacity: 0.5 });"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-decrypt.js",
      "export": "decrypt",
      "group": "Text",
      "purpose": "Text entschluesselt sich aus Rauschen. Zeichenweise korrekt auch bei Emoji; der echte Text steht waehrenddessen als mz-sr-only daneben, damit Screenreader nie das Rauschen lesen.",
      "options": "duration, pool, easing, hold, trigger ('load'|'click'|'hover'|'view'|'none'), threshold, once",
      "example": "import { decrypt } from '/lib/mzone-ui@1.1.0/fx-decrypt.js';\nconst fx = decrypt('.mz-title', { trigger: 'view', hold: 0.15 });"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-particle-reveal.js",
      "export": "particleReveal",
      "group": "Text",
      "purpose": "Partikel formieren sich zu einem Schriftzug und weichen dem Cursor aus. Bei reduced-motion steht der Schriftzug sofort als Punktebild.",
      "options": "text, fontSize, gap, particleSize, scatter, spring, friction, repel, repelForce, color, color2, accentRatio, trigger, fontFamily",
      "example": "import { particleReveal } from '/lib/mzone-ui@1.1.0/fx-particle-reveal.js';\nconst fx = particleReveal('.mz-hero', { text: 'MaierZone' });\nfx.setText('1.1.0');"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-glitch.js",
      "export": "glitch",
      "group": "Text",
      "purpose": "RGB-Split und Scheiben-Versatz auf beliebigen Elementen. Ohne Canvas — der Inhalt wird geklont, geschnitten und eingefaerbt, das Original bleibt lesbar stehen.",
      "options": "trigger ('hover'|'interval'|'manual'), every, duration, slices, offset, split, colorA, colorB",
      "example": "import { glitch } from '/lib/mzone-ui@1.1.0/fx-glitch.js';\nconst fx = glitch('.mz-title', { trigger: 'hover' });\nfx.burst(500);"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-ripple.js",
      "export": "ripple",
      "group": "Interaktion",
      "purpose": "Wasserwellen auf Klick, Hover oder von selbst. Zwei Renderer: Canvas-2D-Ringe ueberall, WebGL-Brechung mit chromatischer Dispersion, wo Kontext und Quelle da sind. 'auto' faellt lautlos zurueck; fx.renderer sagt, was lief.",
      "options": "renderer ('auto'|'webgl'|'2d'), speed, wavelength, rings, life, trigger, ambient, maxRipples, color, shine, source, live, refraction, dispersion, specular",
      "example": "import { ripple } from '/lib/mzone-ui@1.1.0/fx-ripple.js';\nconst fx = ripple('.mz-hero', { renderer: 'webgl', ambient: 4 });\nfx.splash(120, 80, 1);"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-magnify.js",
      "export": "magnify",
      "group": "Interaktion",
      "purpose": "Lupe mit Fadenkreuz ueber Canvas- oder Bildinhalten, mit Mausrad-Zoom. Werkzeug, kein Schmuck — laeuft deshalb auch bei abgewaehlter Bewegung.",
      "options": "source, zoom, radius, crosshair, label, ring, wheelZoom, minZoom, maxZoom",
      "example": "import { magnify } from '/lib/mzone-ui@1.1.0/fx-magnify.js';\nconst fx = magnify('.mz-chart', { zoom: 3 });"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-confetti.js",
      "export": "confetti",
      "group": "Interaktion",
      "purpose": "Konfetti-Bursts in den Statusfarben. Physik framerate-unabhaengig; burst() nimmt Viewport-Fraktion oder Pixel, cannon() schiesst schraeg von einer Kante.",
      "options": "colors, count, power, spread, angle, gravity, drag, life, zIndex, unit ('auto'|'fraction'|'px')",
      "example": "import { confetti } from '/lib/mzone-ui@1.1.0/fx-confetti.js';\nconst fx = confetti();\nfx.burst(0.5, 0.35);\nfx.cannon('left');"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-shatter.js",
      "export": "shatter",
      "group": "Interaktion",
      "purpose": "Ein Bild, ein Diagramm-Canvas oder ein Schriftzug zerspringt in Dreiecksscherben und kann zurueckfliegen. Der Inhalt bleibt in den Scherben sichtbar.",
      "options": "source, text, fontSize, columns, rows, jitter, power, gravity, spin, life, hideSource, seed",
      "example": "import { shatter } from '/lib/mzone-ui@1.1.0/fx-shatter.js';\nconst fx = shatter('.mz-chart');\nfx.blast();\nsetTimeout(() => fx.restore(), 600);"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-vhs.js",
      "export": "vhs",
      "group": "Interaktion",
      "purpose": "Bandrauschen, Scanlines und durchlaufende Spurfehler als Auflage ueber beliebigem Inhalt. Braucht keine Quelle und kostet unabhaengig vom Untergrund dasselbe.",
      "options": "intensity, scanline, sweep, grain, chroma, jitter, color",
      "example": "import { vhs } from '/lib/mzone-ui@1.1.0/fx-vhs.js';\nconst fx = vhs('.mz-card', { intensity: 0.4 });"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-scroll.js",
      "export": "scrollFx",
      "group": "Scroll",
      "purpose": "Scroll-Choreographie ohne Fremdbibliothek: reveal() blendet Abschnitte beim Hereinscrollen ein, counter() zaehlt Zahlen hoch, parallax() versetzt Ebenen, progressBar() zeigt den Lesefortschritt. Ein Scroll-Listener bedient alles. Kein Scroll-Hijacking.",
      "options": "reveal(sel, {y, duration, stagger, threshold, once}) · parallax(sel, {factor}) · counter(sel, {duration}) · progress(cb) · progressBar({height, color})",
      "example": "import { scrollFx } from '/lib/mzone-ui@1.1.0/fx-scroll.js';\nconst fx = scrollFx();\nfx.reveal('.mz-card', { stagger: 60 });\nfx.counter('.mz-stat__value');\nfx.progressBar();"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-frost.js",
      "export": "frost",
      "group": "Optik",
      "purpose": "Milchglas ueber einem Diagramm, Bild oder dem Token-Untergrund; der Zeiger wischt eine klare Stelle frei. Braucht WebGL — ohne WebGL bleibt der Inhalt einfach unverdeckt (fx.supported sagt, was der Fall ist).",
      "options": "source, blur, roughness, grain, tintAmount, tint, clearRadius, clearFeather, live, drift",
      "example": "import { frost } from '/lib/mzone-ui@1.1.0/fx-frost.js';\nconst fx = frost('.mz-card', { blur: 7, clearRadius: 110 });"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-bend.js",
      "export": "bend",
      "group": "Optik",
      "purpose": "Optische Woelbung: eine Linse, die dem Zeiger folgt ('bulge'), oder gleichmaessige Tonnen- bzw. Kissenverzeichnung ueber der ganzen Flaeche. Anders als fx-magnify bleibt das Bild zusammenhaengend und wird gebogen statt ausgeschnitten.",
      "options": "source, mode ('bulge'|'barrel'|'pincushion'), strength, radius, follow, dispersion, rim, rimColor, live, ease",
      "example": "import { bend } from '/lib/mzone-ui@1.1.0/fx-bend.js';\nconst fx = bend('.mz-chart', { mode: 'bulge', strength: 0.6 });\nfx.focus(120, 80);"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-peel.js",
      "export": "peel",
      "group": "Optik",
      "purpose": "Eine Ecke blaettert auf; darunter liegt die Rueckseite, auf der abgeloesten Ecke steht der Inhalt gespiegelt und beschattet. Canvas 2D — das Umschlagen ist eine Spiegelung an der Mittelsenkrechten zwischen Ecke und Zeiger.",
      "options": "source, corner ('br'|'bl'|'tr'|'tl'), follow, maxAmount, ease, under, back, shade, shadow",
      "example": "import { peel } from '/lib/mzone-ui@1.1.0/fx-peel.js';\nconst fx = peel('.mz-card', { corner: 'br' });\nfx.setAmount(0.6);"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-particle-scroll.js",
      "export": "particleScroll",
      "group": "Scroll",
      "purpose": "Partikel bauen sich beim Scrollen von einer Formation in die naechste um. Anders als fx-particle-reveal wird nicht simuliert, sondern interpoliert — die Bewegung ist beim Zurueckscrollen exakt umkehrbar. Der Text steht zusaetzlich als mz-sr-only im DOM.",
      "options": "stages, formations, fontSize, fontFamily, gap, particleSize, swirl, color, color2, accentRatio, scroll, scrollSpan, label",
      "example": "import { particleScroll } from '/lib/mzone-ui@1.1.0/fx-particle-scroll.js';\nconst fx = particleScroll('.mz-hero', { stages: ['2019', '2026', 'jetzt'] });\nfx.setProgress(0.5);"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-flame.js",
      "export": "flame",
      "group": "Energie",
      "purpose": "Flammen entlang der abgerundeten Kante eines Elements, additiv gezeichnet. Bei abgewaehlter Bewegung bleibt der Effekt aus — Feuer ohne Bewegung ist kein Feuer.",
      "options": "edges ('all'|'bottom'|'top'|'sides'), intensity, height, speed, wind, flicker, colors, radius, above",
      "example": "import { flame } from '/lib/mzone-ui@1.1.0/fx-flame.js';\nconst fx = flame('.mz-card', { edges: 'bottom', intensity: 0.8 });"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-forcefield.js",
      "export": "forceField",
      "group": "Energie",
      "purpose": "Wabenschild, durch das bei einem Treffer eine Schockwelle laeuft. Ohne Treffer steht die Schleife still — im Gegensatz zu fx-hexfloat, das von selbst atmet.",
      "options": "cell, idle, speed, life, front, color, hitColor, onClick, auto, shieldRadius",
      "example": "import { forceField } from '/lib/mzone-ui@1.1.0/fx-forcefield.js';\nconst fx = forceField('.mz-card');\nfx.impact(120, 60);"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-laser.js",
      "export": "laser",
      "group": "Energie",
      "purpose": "Scannende Strahlen ueber einer Flaeche plus gezielte Schuesse mit Aufschlagblitz. Additiv, damit Kreuzungen heller werden statt sich zu ueberdecken.",
      "options": "sweep ('horizontal'|'vertical'|'diagonal'|'none'), beams, speed, width, glow, color, coreColor, shotLife, flash",
      "example": "import { laser } from '/lib/mzone-ui@1.1.0/fx-laser.js';\nconst fx = laser('.mz-chart', { sweep: 'horizontal', beams: 2 });\nfx.fire(0, 0, 300, 180);"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-object3d.js",
      "export": "object3d, parseGlb",
      "group": "3D",
      "purpose": "Ein GLB-Modell als ASCII, Partikelwolke, Dither-Bild, Drahtgitter oder Glaskoerper. Eigener GLB-Parser und eigene Projektion statt three.js: der GLTFLoader liegt in three/addons und ist nicht gepinnt. Ohne src wird ein Ikosaeder gezeigt. Grenzen: kein Draco, kein Meshopt, keine Animationen, keine Materialien. Laden ist asynchron — fx.ready abwarten und abfangen.",
      "options": "src, geometry, mode ('ascii'|'particles'|'dither'|'wire'|'glass'), spin, tilt, zoom, drag, color, color2, cell, ramp, pointSize, pixel, maxTriangles",
      "example": "import { object3d } from '/lib/mzone-ui@1.1.0/fx-object3d.js';\nconst fx = object3d('.mz-hero', { src: 'modell.glb', mode: 'ascii' });\nawait fx.ready;\nfx.setMode('particles');"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/chart-preset.js",
      "export": "applyMzoneChartTheme, mzPalette, mzChartOptions",
      "group": "Diagramme",
      "purpose": "Setzt chart.js-Defaults auf die maierzone-Palette. Kein Wrapper — die chart.js-API bleibt unveraendert.",
      "requires": "/lib/chart.js@4.4.6/chart.umd.js muss vorher als <script> geladen sein (UMD, kein ES-Modul).",
      "example": "import { applyMzoneChartTheme, mzPalette, mzChartOptions } from '/lib/mzone-ui@1.1.0/chart-preset.js';\napplyMzoneChartTheme(Chart);\nnew Chart(el, { type: 'line', data: { labels, datasets: [{ label: 'RAM', data, ...mzPalette.series(0, { fill: true }) }] }, options: mzChartOptions() });"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-core.js",
      "export": "intern",
      "group": "Intern",
      "purpose": "Gemeinsamer Unterbau der fx-Module (Ziel-Aufloesung, Token-Farben mit Theme-Watcher, live gelesenes prefers-reduced-motion, Canvas-Mount mit devicePixelRatio, rAF-Schleife mit Tab-Pause). Nicht direkt importieren — wird von den fx-Modulen mitgeladen.",
      "options": "—",
      "example": "// kein direkter Aufruf vorgesehen"
    },
    {
      "path": "/lib/mzone-ui@1.1.0/fx-gl.js",
      "export": "intern",
      "group": "Intern",
      "purpose": "WebGL-Grundausstattung der Optik-Module: Shader uebersetzen, Fullscreen-Dreieck, Uniform-Cache, Quell-Textur, Token-Untergrund. Benutzt von fx-frost und fx-bend. fx-ripple und fx-hero haben ihre eigene, aeltere Fassung und ziehen in 1.2.0 nach. Nicht direkt importieren.",
      "options": "—",
      "example": "// kein direkter Aufruf vorgesehen"
    }
  ],
  "tokens": {
    "note": "Alle Farben sind CSS-Custom-Properties auf :root und wechseln mit dem Theme. Eigene Elemente sollten sie benutzen statt Hex-Werte zu wiederholen. Die fx-Module lesen sie ueber getComputedStyle und aktualisieren sich bei einem Theme-Wechsel selbst.",
    "surfaces": ["--mz-bg", "--mz-surface", "--mz-surface-alt", "--mz-border", "--mz-border-soft"],
    "text": ["--mz-text", "--mz-text-strong", "--mz-text-muted"],
    "status": ["--mz-accent", "--mz-ok", "--mz-warn", "--mz-danger", "--mz-info", "--mz-special"],
    "type": ["--mz-font", "--mz-mono"],
    "shape": ["--mz-radius", "--mz-radius-sm", "--mz-gap", "--mz-measure"],
    "effects": ["--mz-reveal-y", "--mz-reveal-duration", "--mz-reveal-delay"]
  },
  "accessibility": {
    "reducedMotion": "Jedes Modul liest prefers-reduced-motion live und reagiert auf Aenderungen waehrend der Sitzung. Hintergrund-Module halten an, fx-particle-reveal und fx-hexfloat zeigen ihr stehendes Bild, fx-hero seinen statischen Verlauf, fx-decrypt den fertigen Text. fx-magnify laeuft weiter: es ist ein Werkzeug und animiert nichts von selbst.",
    "decorative": "Alle Effekt-Canvas tragen aria-hidden und pointer-events: none. Die Geisterschichten von fx-glitch ebenfalls.",
    "text": "Auf Canvas gemalter oder verrauschter Text steht zusaetzlich als mz-sr-only im DOM — sonst waere er fuer Screenreader und Suche nicht vorhanden.",
    "degradation": "Der Inhalt ist reines HTML mit Klassen — faellt JS aus, bleibt die Seite vollstaendig lesbar. Faellt WebGL aus, greifen die 2D-Wege."
  },
  "conventions": {
    "signature": "Regel: fx(target, options) — Ziel zuerst, Optionen als zweites Argument. Drei bewusste Abweichungen: confetti(options) und scrollFx() haben kein Ziel (Vollbild-Overlay bzw. die Seite selbst); hexFloat(target?, options?) und glyphRain(target?, options?) nehmen das Ziel optional und legen sich ohne Ziel als Vollbild-Hintergrund an. Die in 1.1.0 zuerst ausgelieferte Form hexFloat({ target }) bleibt gueltig, ist aber nicht mehr die dokumentierte.",
    "instance": "Jedes Modul liefert ein Objekt mit destroy(). Wer ein Artefakt neu aufbaut, ruft destroy() — sonst bleiben Canvas, Observer und Listener liegen.",
    "colors": "Farb-Optionen sind null-Standard = Token. Ein String schlaegt das Token.",
    "noDependencies": "Keine externen Abhaengigkeiten, kein CDN, kein Build-Schritt. Die Dateien laufen so, wie sie ausgeliefert werden.",
    "inspiration": "Die Effekt-Module orientieren sich am Verhalten von canvas-ui (github.com/DavidHDev/canvas-ui, MIT + Commons Clause). Nachgebaut, nicht uebernommen: kein Code und keine Shader von dort."
  }
}
