/* FINAZ · Catálogo estético — RENDERIZADORES.
   Diez tratamientos del mismo gráfico (línea intradía FNX 50). Cada función
   recibe { G, T, fonts, chart, intro, focusPt, open, id } y devuelve SVG.
   La capa cinética (partículas) la monta la app por separado. */
(function () {
  "use strict";
  const e = React.createElement;
  const C = window.FZCORE;
  const { VBW, VBH, PAD, smoothPath, hexA } = C;
  const plotW = VBW - PAD.l - PAD.r;
  const plotH = VBH - PAD.t - PAD.b;

  // util: clip de revelado por intro
  function revealClip(id, intro) {
    const w = PAD.l + intro * plotW + 6;
    return e("clipPath", { id: "rv-" + id, key: "rvc" },
      e("rect", { x: 0, y: 0, width: w, height: VBH }));
  }
  const linePath = (G, sm) => smoothPath(G.pts, sm);
  const areaPath = (lp, G) => lp ? lp + " L" + G.pts[G.n - 1].x.toFixed(2) + "," + G.baseY + " L" + G.pts[0].x.toFixed(2) + "," + G.baseY + " Z" : "";

  /* ───────────────── 01 · BIOLUMINISCENTE (MLF) ───────────────── */
  function bioluminiscente({ G, T, chart, intro, id }) {
    const lp = linePath(G, chart.smooth), ap = areaPath(lp, G);
    return e(React.Fragment, null,
      e("defs", null,
        revealClip(id, intro),
        e("linearGradient", { id: "ar-" + id, x1: 0, y1: 0, x2: 0, y2: 1 },
          e("stop", { offset: "0%", stopColor: T.accent, stopOpacity: chart.areaTop }),
          e("stop", { offset: "100%", stopColor: T.accent2, stopOpacity: 0 })),
        e("filter", { id: "gl-" + id, x: "-30%", y: "-60%", width: "160%", height: "220%" },
          e("feGaussianBlur", { stdDeviation: 6, result: "b" }),
          e("feMerge", null, e("feMergeNode", { in: "b" }), e("feMergeNode", { in: "SourceGraphic" })))),
      e("g", { clipPath: "url(#rv-" + id + ")" },
        e("path", { d: ap, fill: "url(#ar-" + id + ")" }),
        // hebra ancha difusa (halo)
        e("path", { d: lp, fill: "none", stroke: T.accent, strokeWidth: chart.lineWidth + 5, opacity: 0.4, filter: "url(#gl-" + id + ")", strokeLinecap: "round" }),
        e("path", { d: lp, fill: "none", stroke: T.accent3, strokeWidth: chart.lineWidth, strokeLinecap: "round", filter: "url(#gl-" + id + ")" }),
        // núcleos pulsantes en cada dato
        G.pts.filter((_, i) => i % 4 === 0).map((p) =>
          e("circle", { key: p.i, cx: p.x, cy: p.y, r: 1.6, fill: T.ink, opacity: 0.8 }))));
  }

  /* ───────────────── 02 · PIGMENTO (Refik Anadol) ───────────────── */
  function pigmento({ G, T, chart, intro, id }) {
    const lp = linePath(G, chart.smooth), ap = areaPath(lp, G);
    const cols = [T.accent, T.accent2, T.accent3];
    return e(React.Fragment, null,
      e("defs", null,
        revealClip(id, intro),
        e("linearGradient", { id: "pg-" + id, x1: 0, y1: 0, x2: 1, y2: 0 },
          e("stop", { offset: "0%", stopColor: T.accent3 }),
          e("stop", { offset: "50%", stopColor: T.accent }),
          e("stop", { offset: "100%", stopColor: T.accent2 })),
        e("linearGradient", { id: "pa-" + id, x1: 0, y1: 0, x2: 0, y2: 1 },
          e("stop", { offset: "0%", stopColor: T.accent, stopOpacity: 0.32 }),
          e("stop", { offset: "60%", stopColor: T.accent2, stopOpacity: 0.10 }),
          e("stop", { offset: "100%", stopColor: T.accent2, stopOpacity: 0 })),
        e("filter", { id: "bl-" + id }, e("feGaussianBlur", { stdDeviation: 9 }))),
      e("g", { clipPath: "url(#rv-" + id + ")" },
        e("path", { d: ap, fill: "url(#pa-" + id + ")", filter: "url(#bl-" + id + ")" }),
        // capas de pigmento desplazadas y difusas (alucinación)
        cols.map((c, k) =>
          e("path", { key: k, d: lp, fill: "none", stroke: c, strokeWidth: 7 - k * 1.5,
            opacity: 0.28, filter: "url(#bl-" + id + ")", strokeLinecap: "round",
            transform: "translate(0," + (k - 1) * 6 + ")" })),
        e("path", { d: lp, fill: "none", stroke: "url(#pg-" + id + ")", strokeWidth: chart.lineWidth, strokeLinecap: "round", opacity: 0.9 })));
  }

  /* ───────────────── 03 · ENJAMBRE (Studio DRIFT) ───────────────── */
  function enjambre({ G, T, chart, intro, id }) {
    const lp = linePath(G, chart.smooth);
    // "semillas frágiles": líneas verticales finas que caen de algunos puntos
    const seeds = G.pts.filter((_, i) => i % 7 === 0);
    return e(React.Fragment, null,
      e("defs", null, revealClip(id, intro),
        e("filter", { id: "sg-" + id, x: "-50%", y: "-50%", width: "200%", height: "200%" }, e("feGaussianBlur", { stdDeviation: 2.2 }))),
      e("g", { clipPath: "url(#rv-" + id + ")" },
        // hilos frágiles
        seeds.map((p) => e("g", { key: "s" + p.i },
          e("line", { x1: p.x, x2: p.x, y1: p.y, y2: G.baseY - 6, stroke: T.accent, strokeWidth: 0.6, opacity: 0.22 }),
          e("circle", { cx: p.x, cy: G.baseY - 6, r: 1.4, fill: T.accent3, opacity: 0.5 }))),
        // línea guía tenue (el enjambre del canvas la completa)
        e("path", { d: lp, fill: "none", stroke: T.accent2, strokeWidth: 0.8, opacity: 0.28, strokeDasharray: "1 6", strokeLinecap: "round" }),
        // nodos brillantes
        G.pts.filter((_, i) => i % 3 === 0).map((p) =>
          e("circle", { key: p.i, cx: p.x, cy: p.y, r: 1.6, fill: T.accent, opacity: 0.85, filter: "url(#sg-" + id + ")" }))));
  }

  /* ───────────────── 04 · MICELIO (Entangled Others) ───────────────── */
  function micelio({ G, T, chart, intro, id }) {
    const lp = linePath(G, chart.smooth), ap = areaPath(lp, G);
    // filamentos que crecen desde la base hasta la línea
    const fils = [];
    G.pts.forEach((p, i) => {
      if (i % 3 !== 0) return;
      const h = (G.baseY - p.y) * intro;
      const y0 = G.baseY, y1 = G.baseY - h;
      const sway = 7 * Math.sin(i * 0.7);
      const d = "M" + p.x + "," + y0 + " Q" + (p.x + sway) + "," + ((y0 + y1) / 2) + " " + p.x + "," + y1;
      fils.push(e("g", { key: "f" + i },
        e("path", { d, fill: "none", stroke: T.accent, strokeWidth: 0.8, opacity: 0.3 }),
        // ramita
        e("path", { d: "M" + p.x + "," + ((y0 + y1) / 2) + " l" + (sway > 0 ? 8 : -8) + ",-7", fill: "none", stroke: T.accent3, strokeWidth: 0.6, opacity: 0.28 }),
        e("circle", { cx: p.x, cy: y1, r: 1.7, fill: T.accent2, opacity: 0.7 })));
    });
    return e(React.Fragment, null,
      e("defs", null, revealClip(id, intro),
        e("linearGradient", { id: "ma-" + id, x1: 0, y1: 0, x2: 0, y2: 1 },
          e("stop", { offset: "0%", stopColor: T.accent, stopOpacity: chart.areaTop }),
          e("stop", { offset: "100%", stopColor: T.accent, stopOpacity: 0 }))),
      e("g", { clipPath: "url(#rv-" + id + ")" },
        e("path", { d: ap, fill: "url(#ma-" + id + ")" }),
        e("g", null, fils),
        e("path", { d: lp, fill: "none", stroke: T.accent, strokeWidth: chart.lineWidth, strokeLinecap: "round", opacity: 0.92 })));
  }

  /* ───────────────── 05 · FLORECER (teamLab) ───────────────── */
  function florecer({ G, T, chart, intro, id }) {
    const lp = linePath(G, chart.smooth), ap = areaPath(lp, G);
    return e(React.Fragment, null,
      e("defs", null, revealClip(id, intro),
        e("linearGradient", { id: "fl-" + id, x1: 0, y1: 0, x2: 1, y2: 0 },
          e("stop", { offset: "0%", stopColor: T.accent3 }),
          e("stop", { offset: "45%", stopColor: T.accent }),
          e("stop", { offset: "100%", stopColor: T.accent2 })),
        e("linearGradient", { id: "fa-" + id, x1: 0, y1: 0, x2: 0, y2: 1 },
          e("stop", { offset: "0%", stopColor: T.accent, stopOpacity: chart.areaTop }),
          e("stop", { offset: "100%", stopColor: T.accent3, stopOpacity: 0 })),
        e("filter", { id: "fg-" + id, x: "-30%", y: "-60%", width: "160%", height: "220%" },
          e("feGaussianBlur", { stdDeviation: 4, result: "b" }),
          e("feMerge", null, e("feMergeNode", { in: "b" }), e("feMergeNode", { in: "SourceGraphic" })))),
      e("g", { clipPath: "url(#rv-" + id + ")" },
        e("path", { d: ap, fill: "url(#fa-" + id + ")" }),
        e("path", { d: lp, fill: "none", stroke: "url(#fl-" + id + ")", strokeWidth: chart.lineWidth + 1, strokeLinecap: "round", filter: "url(#fg-" + id + ")" }),
        // destellos sobre la línea
        G.pts.filter((_, i) => i % 5 === 0).map((p) =>
          e("circle", { key: p.i, cx: p.x, cy: p.y, r: 1.4, fill: T.ink, opacity: 0.7 }))));
  }

  /* ───────────────── 06 · MATERIAL (Neri Oxman) ───────────────── */
  function material({ G, T, chart, intro, id }) {
    const lp = linePath(G, chart.smooth), ap = areaPath(lp, G);
    // estratos: copias de la línea descendidas (densidad de material)
    const strata = [];
    const N = 16;
    for (let k = 1; k <= N; k++) {
      const off = (k / N) * (plotH * 0.92) * intro;
      const d = "M" + G.pts.map((p) => p.x.toFixed(1) + "," + (p.y + off).toFixed(1)).join(" L");
      strata.push(e("path", { key: k, d, fill: "none", stroke: T.accent, strokeWidth: 0.7, opacity: 0.5 * (1 - k / N) + 0.05 }));
    }
    return e(React.Fragment, null,
      e("defs", null, revealClip(id, intro),
        e("linearGradient", { id: "mt-" + id, x1: 0, y1: 0, x2: 0, y2: 1 },
          e("stop", { offset: "0%", stopColor: T.accent, stopOpacity: chart.areaTop }),
          e("stop", { offset: "55%", stopColor: T.accent2, stopOpacity: 0.14 }),
          e("stop", { offset: "100%", stopColor: T.accent2, stopOpacity: 0.02 })),
        e("clipPath", { id: "mc-" + id }, e("path", { d: ap }))),
      e("g", { clipPath: "url(#rv-" + id + ")" },
        e("path", { d: ap, fill: "url(#mt-" + id + ")" }),
        e("g", { clipPath: "url(#mc-" + id + ")" }, strata),
        e("path", { d: lp, fill: "none", stroke: T.ink, strokeWidth: chart.lineWidth, strokeLinecap: "round", opacity: 0.9 })));
  }

  /* ───────────────── 07 · POLINIZADOR (A. D. Ginsberg) ───────────────── */
  function polinizador({ G, T, chart, intro, id }) {
    const lp = linePath(G, chart.smooth);
    const flower = (p, k) => {
      const pet = 5, R = 4.4;
      return e("g", { key: "fw" + k, transform: "translate(" + p.x + "," + p.y + ")", opacity: 0.9 },
        Array.from({ length: pet }).map((_, i) => {
          const a = (i / pet) * Math.PI * 2;
          return e("ellipse", { key: i, cx: Math.cos(a) * R, cy: Math.sin(a) * R, rx: 2.6, ry: 1.5, transform: "rotate(" + (a * 180 / Math.PI) + " " + Math.cos(a) * R + " " + Math.sin(a) * R + ")", fill: i % 2 ? T.accent2 : T.accent, opacity: 0.8 });
        }),
        e("circle", { r: 1.8, fill: T.accent3 }));
    };
    const flowers = G.peaks.concat([8, 40, 72]).map((i) => G.pts[i]).filter(Boolean);
    return e(React.Fragment, null,
      e("defs", null, revealClip(id, intro)),
      e("g", { clipPath: "url(#rv-" + id + ")" },
        // ruta de vuelo
        e("path", { d: lp, fill: "none", stroke: T.accent, strokeWidth: chart.lineWidth, strokeDasharray: "1 7", strokeLinecap: "round", opacity: 0.7 }),
        flowers.map((p, k) => flower(p, k)),
        // polinizador (punto que viaja)
        (() => { const idx = Math.min(G.n - 1, Math.round(intro * (G.n - 1))); const p = G.pts[idx]; return e("circle", { cx: p.x, cy: p.y, r: 2.6, fill: T.ink, opacity: 0.9 }); })()));
  }

  /* ───────────────── 08 · ESTRATOS (Quayola) ───────────────── */
  function estratos({ G, T, chart, intro, id }) {
    const lp = linePath(G, chart.smooth);
    const lineY = C.makeLineY(G.pts);
    // reconstrucción en columnas finas (escultura digital)
    const cols = [];
    const step = 6;
    for (let x = PAD.l; x <= VBW - PAD.r; x += step) {
      const nx = x / VBW, y = lineY(nx);
      const h = (G.baseY - y) * intro;
      cols.push(e("rect", { key: x, x: x, y: G.baseY - h, width: 2.2, height: h, fill: "url(#qg-" + id + ")", opacity: 0.55 }));
    }
    return e(React.Fragment, null,
      e("defs", null, revealClip(id, intro),
        e("linearGradient", { id: "qg-" + id, x1: 0, y1: 0, x2: 0, y2: 1 },
          e("stop", { offset: "0%", stopColor: T.accent, stopOpacity: 0.55 }),
          e("stop", { offset: "100%", stopColor: T.accent2, stopOpacity: 0.04 }))),
      e("g", { clipPath: "url(#rv-" + id + ")" },
        e("g", null, cols),
        // filo iridiscente refinado
        e("path", { d: lp, fill: "none", stroke: T.accent3, strokeWidth: chart.lineWidth + 0.8, strokeLinecap: "round", opacity: 0.5 }),
        e("path", { d: lp, fill: "none", stroke: T.ink, strokeWidth: chart.lineWidth, strokeLinecap: "round" })));
  }

  /* ───────────────── 09 · PAISAJE (Jakob Kudsk Steensen) ───────────────── */
  function paisaje({ G, T, chart, intro, id }) {
    const lp = linePath(G, chart.smooth);
    // capas de profundidad: siluetas desplazadas verticalmente
    const layers = [];
    const defs = [{ off: 70, op: 0.10, col: T.accent }, { off: 42, op: 0.14, col: T.accent3 }, { off: 18, op: 0.2, col: T.accent }];
    defs.forEach((L, k) => {
      const d = "M" + G.pts.map((p) => p.x.toFixed(1) + "," + (p.y + L.off).toFixed(1)).join(" L") +
        " L" + (VBW - PAD.r) + "," + G.baseY + " L" + PAD.l + "," + G.baseY + " Z";
      layers.push(e("path", { key: k, d, fill: L.col, opacity: L.op }));
    });
    // reflejo bajo el horizonte
    const refl = "M" + G.pts.map((p) => p.x.toFixed(1) + "," + (2 * G.baseY - p.y).toFixed(1)).join(" L");
    return e(React.Fragment, null,
      e("defs", null, revealClip(id, intro),
        e("linearGradient", { id: "sk-" + id, x1: 0, y1: 0, x2: 0, y2: 1 },
          e("stop", { offset: "0%", stopColor: T.accent, stopOpacity: 0.06 }),
          e("stop", { offset: "100%", stopColor: T.accent, stopOpacity: 0 }))),
      e("g", { clipPath: "url(#rv-" + id + ")" },
        e("path", { d: areaPath(lp, G), fill: "url(#sk-" + id + ")" }),
        e("g", null, layers),
        // horizonte luminoso
        e("path", { d: lp, fill: "none", stroke: T.accent, strokeWidth: chart.lineWidth, strokeLinecap: "round", opacity: 0.95 }),
        // reflejo tenue
        e("path", { d: refl, fill: "none", stroke: T.accent3, strokeWidth: chart.lineWidth, strokeLinecap: "round", opacity: 0.12 })));
  }

  /* ───────────────── 10 · TELARAÑA (Tomás Saraceno) ───────────────── */
  function telarana({ G, T, chart, intro, id }) {
    const lp = linePath(G, chart.smooth);
    const anchors = [{ x: PAD.l - 30, y: PAD.t - 30 }, { x: VBW - PAD.r + 30, y: PAD.t - 20 }, { x: VBW / 2, y: VBH - 6 }];
    const threads = [];
    const sample = G.pts.filter((_, i) => i % 2 === 0);
    sample.forEach((p, k) => {
      // hilo radial al ancla más cercana, revelado por intro
      const a = anchors[k % anchors.length];
      const tx = p.x + (a.x - p.x) * 1, ty = p.y + (a.y - p.y) * 1;
      threads.push(e("line", { key: "t" + p.i, x1: p.x, y1: p.y, x2: tx, y2: ty, stroke: T.accent, strokeWidth: 0.35, opacity: 0.12 * intro }));
    });
    // hilos entre nodos vecinos (estructura de la web)
    const web = [];
    for (let i = 0; i < sample.length - 1; i++) {
      const p = sample[i], q = sample[i + 1];
      web.push(e("line", { key: "w" + i, x1: p.x, y1: p.y, x2: q.x, y2: q.y, stroke: T.accent2, strokeWidth: 0.5, opacity: 0.4 }));
      if (i % 2 === 0 && sample[i + 2]) web.push(e("line", { key: "wd" + i, x1: p.x, y1: p.y, x2: sample[i + 2].x, y2: sample[i + 2].y, stroke: T.accent, strokeWidth: 0.3, opacity: 0.16 }));
    }
    return e(React.Fragment, null,
      e("defs", null, revealClip(id, intro)),
      e("g", null, threads),
      e("g", { clipPath: "url(#rv-" + id + ")" },
        e("g", null, web),
        e("path", { d: lp, fill: "none", stroke: T.ink, strokeWidth: chart.lineWidth, strokeLinecap: "round", opacity: 0.8 }),
        // nodos suspendidos
        sample.map((p) => e("circle", { key: "n" + p.i, cx: p.x, cy: p.y, r: 1.3, fill: T.accent3, opacity: 0.9 }))));
  }

  window.FZRENDERERS = {
    bioluminiscente, pigmento, enjambre, micelio, florecer,
    material, polinizador, estratos, paisaje, telaraña: telarana,
  };
})();
