// LongtailIQ app, chart + animation helpers
(function init(){
if(!window.LongtailIQDesignSystem_ae8f12){return setTimeout(init,30);}
const React = window.React;

// Count-up hook. Spring-driven when the physics engine (MV-4) is present:
// a value that updates mid-flight RETARGETS from the current displayed number
// (velocity-preserving glide) instead of snapping back to zero and re-counting.
// Critically damped (ζ=1) so a KPI never overshoots past its value. Falls back
// to the original cubic-ease rAF when the engine is absent (e.g. marketing).
function useCountUp(target, dur = 900, deps = []) {
  const tgt = typeof target === "number" && isFinite(target) ? target : 0;
  const [val, setVal] = React.useState(0);
  const springRef = React.useRef(null);
  // Stop the spring for good when the component unmounts (no setState-after-unmount).
  React.useEffect(() => () => { if (springRef.current) springRef.current.stop(); }, []);
  React.useEffect(() => {
    const S = window.LTQSpring;
    if (S && !S.reducedMotion()) {
      if (!springRef.current) {
        springRef.current = S.createSpring(Object.assign(
          S.fromResponse(0.6, 1), // ~0.6s settle, no overshoot
          { from: 0, onUpdate: (v) => setVal(v) }
        ));
      }
      const s = springRef.current;
      const mag = Math.max(1, Math.abs(tgt));
      s.restDelta = mag * 0.0015; // ~0.15% of the value → precise final frame
      s.restSpeed = mag * 0.05;
      s.set(tgt);
      return; // spring persists across renders; retargets on the next change
    }
    // fallback: cubic ease-out rAF, counting from the current value
    let raf, start, done = false;
    const from = val;
    const tick = (t) => {
      if (!start) start = t;
      const p = Math.min(1, (t - start) / dur);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(from + (tgt - from) * eased);
      if (p < 1) raf = requestAnimationFrame(tick); else done = true;
    };
    raf = requestAnimationFrame(tick);
    // Guarantee the final value even if rAF is throttled (preview/print/bg tab)
    const fb = setTimeout(() => { if (!done) setVal(tgt); }, dur + 250);
    return () => { cancelAnimationFrame(raf); clearTimeout(fb); };
  }, deps.length ? deps : [tgt]);
  return val;
}

// IntersectionObserver reveal, adds .in to .lt-reveal children
function useReveal(ref) {
  React.useEffect(() => {
    const root = ref.current; if (!root) return;
    const els = root.querySelectorAll('.lt-reveal');
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } });
    }, { threshold: 0.12 });
    els.forEach((el, i) => { el.style.transitionDelay = (i % 8) * 60 + 'ms'; io.observe(el); });
    return () => io.disconnect();
  });
}

/**
 * MultiLineChart, animated, multi-series line chart (Peec Overview style).
 * series: [{ name, color, points:[..] }]. xLabels optional.
 * Draws a soft grid, animates each line drawing in, and pins a floating
 * tooltip card at `markerIndex`.
 */
function MultiLineChart({ series, xLabels = [], height = 240, markerIndex, tooltipTitle, animate = true }) {
  const W = 760, H = height, padL = 8, padR = 8, padT = 16, padB = 24;
  const n = series[0].points.length;
  const all = series.flatMap(s => s.points);
  const max = Math.max(...all) * 1.08, min = Math.min(...all, 0);
  const range = max - min || 1;
  const x = i => padL + (i / (n - 1)) * (W - padL - padR);
  const y = v => padT + (1 - (v - min) / range) * (H - padT - padB);
  const path = pts => pts.map((p, i) => `${i ? 'L' : 'M'}${x(i).toFixed(1)} ${y(p).toFixed(1)}`).join(' ');
  const area = pts => path(pts) + ` L ${x(n-1).toFixed(1)} ${H - padB} L ${x(0).toFixed(1)} ${H - padB} Z`;

  const mi = markerIndex == null ? n - 2 : markerIndex;
  const [drawn, setDrawn] = React.useState(!animate);
  const uid = React.useRef('c' + Math.random().toString(36).slice(2, 7)).current;
  React.useEffect(() => { const t = setTimeout(() => setDrawn(true), 60); return () => clearTimeout(t); }, []);

  return (
    <div style={{ position: "relative", width: "100%" }}>
      <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%", height: H, display: "block", overflow: "visible" }}>
        <defs>
          {series.map((s, si) => (
            <linearGradient key={si} id={`${uid}-${si}`} x1="0" y1="0" x2="0" y2="1">
              <stop offset="0" stopColor={s.color} stopOpacity="0.16" />
              <stop offset="0.7" stopColor={s.color} stopOpacity="0.03" />
              <stop offset="1" stopColor={s.color} stopOpacity="0" />
            </linearGradient>
          ))}
          <filter id={`${uid}-glow`} x="-60%" y="-60%" width="220%" height="220%">
            <feGaussianBlur stdDeviation="3" result="b" /><feMerge><feMergeNode in="b" /><feMergeNode in="SourceGraphic" /></feMerge>
          </filter>
        </defs>
        {/* horizontal grid */}
        {[0, 0.25, 0.5, 0.75, 1].map((g, i) => (
          <line key={i} x1={padL} x2={W - padR} y1={padT + g * (H - padT - padB)} y2={padT + g * (H - padT - padB)}
            stroke="var(--border-subtle)" strokeWidth="1" opacity={i === 4 ? 1 : 0.6} />
        ))}
        {/* area fills (only the primary 2 series, to keep it clean) */}
        {series.slice(0, 2).map((s, si) => (
          <path key={'a'+si} d={area(s.points)} fill={`url(#${uid}-${si})`}
            style={{ opacity: drawn ? 1 : 0, transition: `opacity 0.9s var(--ease-out) ${0.5 + si * 0.1}s` }} />
        ))}
        {/* marker vertical guide */}
        <line x1={x(mi)} x2={x(mi)} y1={padT} y2={H - padB} stroke="var(--ink-300)" strokeWidth="1" strokeDasharray="3 3"
          style={{ opacity: drawn ? 1 : 0, transition: "opacity .4s ease .9s" }} />
        {/* lines */}
        {series.map((s, si) => {
          const d = path(s.points);
          return (
            <g key={si}>
              <path d={d} fill="none" stroke={s.color} strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round"
                style={{ strokeDasharray: 2000, strokeDashoffset: drawn ? 0 : 2000, transition: `stroke-dashoffset 1.2s var(--ease-out) ${si * 0.1}s` }} />
              {si === 0 && <circle cx={x(mi)} cy={y(s.points[mi])} r="4.5" fill={s.color} filter={`url(#${uid}-glow)`}
                style={{ opacity: drawn ? 0.9 : 0, transition: "opacity .3s ease 1.05s" }} />}
              <circle cx={x(mi)} cy={y(s.points[mi])} r="3.5" fill="var(--paper)" stroke={s.color} strokeWidth="2"
                style={{ opacity: drawn ? 1 : 0, transition: "opacity .3s ease 1s" }} />
              {si === 0 && drawn && <circle cx={x(mi)} cy={y(s.points[mi])} r="3.5" fill="none" stroke={s.color} className="fx-svg-pulse" />}
            </g>
          );
        })}
        {/* x labels */}
        {xLabels.map((l, i) => (
          <text key={i} x={x(i)} y={H - 6} textAnchor={i === 0 ? "start" : i === n - 1 ? "end" : "middle"}
            fontSize="10.5" fontFamily="var(--font-mono)" fill="var(--text-faint)">{l}</text>
        ))}
      </svg>
      {/* floating tooltip card */}
      {tooltipTitle && (
        <div style={{ position: "absolute", left: `calc(${(mi / (n - 1)) * 100}% - 8px)`, top: 8, transform: "translateX(-50%)",
          background: "var(--ink-900)", color: "#fff", borderRadius: 10, padding: "10px 12px", minWidth: 150,
          boxShadow: "var(--shadow-lg)", pointerEvents: "none",
          opacity: drawn ? 1 : 0, transition: "opacity .4s ease 1s" }}>
          <div style={{ fontSize: 11, color: "rgba(255,255,255,0.55)", marginBottom: 7, fontWeight: 500 }}>{tooltipTitle}</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
            {series.map((s, i) => (
              <div key={i} style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 12 }}>
                <span style={{ width: 7, height: 7, borderRadius: 999, background: s.color }} />
                <span style={{ color: "rgba(255,255,255,0.8)", flex: 1 }}>{s.name}</span>
                <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontWeight: 600 }}>{s.points[mi]}{s.unit || ''}</span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// Compact number formatting for axes/tooltips: 1234 -> 1.2k, 1_200_000 -> 1.2M.
// The k/M suffix is a product convention, but the decimal separator follows the
// user's locale (1,2k for pl/de/nl/da) like every other number on screen.
function fmtCompact(n) {
  const dec = (v, digits) => {
    const fixed = Number(v.toFixed(digits));
    return window.LTQ.fmtNumber
      ? window.LTQ.fmtNumber(fixed, { maximumFractionDigits: digits })
      : String(fixed);
  };
  const a = Math.abs(n);
  if (a >= 1e6) return dec(n / 1e6, a >= 1e7 ? 0 : 1) + "M";
  if (a >= 1e3) return dec(n / 1e3, a >= 1e4 ? 0 : 1) + "k";
  return String(Math.round(n));
}

const prefersReducedMotion = () =>
  typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;

/**
 * InteractiveArea — a single-series area + line chart with a hover crosshair, a
 * moving tooltip, an animated draw-in, an optional previous-period overlay, and
 * full keyboard navigation. `data` is ascending [{date, [yKey]}].
 *   compare      optional previous-window series (dashed, faint) aligned by index
 *   format(v)    renders the value; lowerIsBetter flips the delta colour (avg pos)
 * The active point defaults to the latest and follows the mouse / arrow keys.
 */
function InteractiveArea({ data, yKey = "value", height = 240, color = "var(--accent-500)", format = fmtCompact, lowerIsBetter = false, compare = null }) {
  const W = 760, H = height, padL = 8, padR = 8, padT = 16, padB = 24;
  const n = data.length;
  const wrapRef = React.useRef(null);
  const [hover, setHover] = React.useState(null);
  const reduce = React.useMemo(prefersReducedMotion, []);
  const [drawn, setDrawn] = React.useState(reduce);
  const uid = React.useRef("ia" + Math.random().toString(36).slice(2, 7)).current;
  React.useEffect(() => { if (reduce) return; const t = setTimeout(() => setDrawn(true), 50); return () => clearTimeout(t); }, []);
  React.useEffect(() => { setHover(null); }, [yKey, n]);

  if (!n) return <div style={{ height, display: "grid", placeItems: "center", color: "var(--text-faint)", fontSize: 13 }}>{window.LTQ.t ? window.LTQ.t("chart.no_data") : "No data for this range yet."}</div>;

  const vals = data.map((d) => Number(d[yKey]) || 0);
  const cmp = Array.isArray(compare) && compare.length ? compare.map((d) => Number(d[yKey]) || 0) : null;
  const pool = cmp ? vals.concat(cmp) : vals; // shared scale so both series fit
  const hi = Math.max(...pool) * 1.08, lo = Math.min(...pool, 0);
  const range = hi - lo || 1;
  const x = (i) => padL + (n === 1 ? 0.5 : i / (n - 1)) * (W - padL - padR);
  const y = (v) => padT + (1 - (v - lo) / range) * (H - padT - padB);
  const line = vals.map((v, i) => `${i ? "L" : "M"}${x(i).toFixed(1)} ${y(v).toFixed(1)}`).join(" ");
  const area = line + ` L ${x(n - 1).toFixed(1)} ${H - padB} L ${x(0).toFixed(1)} ${H - padB} Z`;
  const cmpLine = cmp ? cmp.slice(0, n).map((v, i) => `${i ? "L" : "M"}${x(i).toFixed(1)} ${y(v).toFixed(1)}`).join(" ") : null;
  const active = hover == null ? n - 1 : Math.min(n - 1, Math.max(0, hover));

  const onMove = (e) => {
    const el = wrapRef.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const frac = Math.min(1, Math.max(0, (e.clientX - r.left) / r.width));
    setHover(Math.round(frac * (n - 1)));
  };
  const onKey = (e) => {
    if (e.key === "ArrowRight") { e.preventDefault(); setHover((h) => Math.min(n - 1, (h == null ? n - 1 : h) + 1)); }
    else if (e.key === "ArrowLeft") { e.preventDefault(); setHover((h) => Math.max(0, (h == null ? n - 1 : h) - 1)); }
    else if (e.key === "Home") { e.preventDefault(); setHover(0); }
    else if (e.key === "End") { e.preventDefault(); setHover(n - 1); }
  };
  const labelIdx = [0, Math.floor((n - 1) / 2), n - 1].filter((v, i, a) => a.indexOf(v) === i);
  const tipLeftPct = (x(active) / W) * 100;
  const prevVal = cmp && active < cmp.length ? cmp[active] : null;
  const delta = prevVal != null && prevVal !== 0 ? Math.round(((vals[active] - prevVal) / prevVal) * 1000) / 10 : null;
  const deltaGood = delta == null ? null : lowerIsBetter ? delta < 0 : delta > 0;

  return (
    <div ref={wrapRef} tabIndex={0} role="group"
      aria-label={`Trend chart, ${n} points. ${data[active].date}: ${format(vals[active])}. Use arrow keys to inspect points.`}
      style={{ position: "relative", width: "100%", outline: "none", borderRadius: 8 }}
      onMouseMove={onMove} onMouseLeave={() => setHover(null)} onKeyDown={onKey}
      onFocus={(e) => { e.currentTarget.style.boxShadow = "0 0 0 3px var(--accent-100)"; }}
      onBlur={(e) => { e.currentTarget.style.boxShadow = "none"; setHover(null); }}>
      <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%", height: H, display: "block", overflow: "visible" }}>
        <defs>
          <linearGradient id={uid} x1="0" y1="0" x2="0" y2="1">
            <stop offset="0" stopColor={color} stopOpacity="0.20" />
            <stop offset="0.7" stopColor={color} stopOpacity="0.04" />
            <stop offset="1" stopColor={color} stopOpacity="0" />
          </linearGradient>
        </defs>
        {[0, 0.25, 0.5, 0.75, 1].map((g, i) => (
          <line key={i} x1={padL} x2={W - padR} y1={padT + g * (H - padT - padB)} y2={padT + g * (H - padT - padB)}
            stroke="var(--border-subtle)" strokeWidth="1" opacity={i === 4 ? 1 : 0.55} />
        ))}
        <path d={area} fill={`url(#${uid})`} style={{ opacity: drawn ? 1 : 0, transition: "opacity .9s var(--ease-out) .35s" }} />
        {/* previous-period overlay (dashed, faint) */}
        {cmpLine && (
          <path d={cmpLine} fill="none" stroke="var(--ink-400)" strokeWidth="1.6" strokeDasharray="4 4" strokeLinecap="round"
            style={{ opacity: drawn ? 0.75 : 0, transition: "opacity .6s ease .5s" }} />
        )}
        <path d={line} fill="none" stroke={color} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"
          style={{ strokeDasharray: 2600, strokeDashoffset: drawn ? 0 : 2600, transition: "stroke-dashoffset 1.15s var(--ease-out)" }} />
        {/* crosshair + active dot */}
        <line x1={x(active)} x2={x(active)} y1={padT} y2={H - padB} stroke="var(--ink-300)" strokeWidth="1" strokeDasharray="3 3"
          style={{ opacity: drawn ? 1 : 0 }} />
        <circle cx={x(active)} cy={y(vals[active])} r="4.5" fill={color} stroke="var(--paper)" strokeWidth="2"
          style={{ opacity: drawn ? 1 : 0, transition: "opacity .3s ease" }} />
        {labelIdx.map((i) => (
          <text key={i} x={x(i)} y={H - 6} textAnchor={i === 0 ? "start" : i === n - 1 ? "end" : "middle"}
            fontSize="10.5" fontFamily="var(--font-mono)" fill="var(--text-faint)">{(data[i].date || "").slice(5)}</text>
        ))}
      </svg>
      <div style={{ position: "absolute", top: 6, left: `${tipLeftPct}%`, transform: `translateX(${tipLeftPct > 70 ? "-100%" : tipLeftPct < 30 ? "0" : "-50%"})`,
        background: "var(--ink-900)", color: "#fff", borderRadius: 9, padding: "8px 11px", minWidth: 110, pointerEvents: "none",
        boxShadow: "var(--shadow-lg)", opacity: drawn ? 1 : 0, transition: "opacity .3s ease .4s" }}>
        <div style={{ fontSize: 11, color: "rgba(255,255,255,0.55)", marginBottom: 3 }}>{data[active].date}</div>
        <div className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 15, fontWeight: 700 }}>{format(vals[active])}</div>
        {prevVal != null && (
          <div style={{ display: "flex", alignItems: "center", gap: 6, marginTop: 4, fontSize: 11 }}>
            <span style={{ color: "rgba(255,255,255,0.55)" }}>prev {format(prevVal)}</span>
            {delta != null && <span style={{ fontFamily: "var(--font-mono)", fontWeight: 700, color: delta === 0 ? "rgba(255,255,255,0.6)" : deltaGood ? "var(--viz-green)" : "var(--viz-red)" }}>{delta > 0 ? "+" : ""}{delta}%</span>}
          </div>
        )}
      </div>
    </div>
  );
}

window.LTQ = window.LTQ || {};
Object.assign(window.LTQ, { MultiLineChart, InteractiveArea, useCountUp, useReveal, fmtCompact });
})();
