// Shared utility components used across variations
(() => {
  const { useState, useEffect, useMemo, useRef } = React;

  // Sparkline / line chart
  function LineChart({ data, width = 400, height = 120, lines, yPad = 8, showAxis = true, bg = 'transparent', grid = true }) {
    const all = lines.flatMap(l => data.map(d => d[l.key]));
    const min = Math.min(...all);
    const max = Math.max(...all);
    const range = max - min || 1;
    const x = i => (i / (data.length - 1)) * (width - 30) + 25;
    const y = v => height - yPad - ((v - min) / range) * (height - yPad * 2);

    return (
      <svg width={width} height={height} style={{ background: bg, display: 'block' }}>
        {grid && [0.25, 0.5, 0.75].map(p => (
          <line key={p} x1={25} x2={width} y1={yPad + p * (height - yPad * 2)} y2={yPad + p * (height - yPad * 2)}
                stroke="currentColor" strokeOpacity="0.08" strokeDasharray="2 3" />
        ))}
        {lines.map(line => {
          const d = data.map((pt, i) => `${i === 0 ? 'M' : 'L'} ${x(i)} ${y(pt[line.key])}`).join(' ');
          return (
            <g key={line.key}>
              <path d={d} fill="none" stroke={line.color} strokeWidth={line.width || 1.5} />
              {line.fill && (
                <path d={`${d} L ${x(data.length - 1)} ${height - yPad} L ${x(0)} ${height - yPad} Z`}
                      fill={line.color} opacity="0.08" />
              )}
            </g>
          );
        })}
      </svg>
    );
  }

  // Small blinking dot
  function BlinkDot({ color = '#22c55e', size = 6 }) {
    return (
      <span style={{
        display: 'inline-block', width: size, height: size, borderRadius: '50%',
        background: color, boxShadow: `0 0 ${size * 1.2}px ${color}`,
        animation: 'tam-blink 1.4s ease-in-out infinite'
      }} />
    );
  }

  // Progress bar
  function Bar({ value, max, color = 'currentColor', height = 4, bg = 'currentColor', bgOp = 0.1 }) {
    const pct = Math.max(0, Math.min(1, value / max));
    return (
      <div style={{ position: 'relative', height, background: bg, opacity: 1, borderRadius: 1 }}>
        <div style={{ position: 'absolute', inset: 0, background: bg, opacity: bgOp, borderRadius: 1 }} />
        <div style={{ position: 'absolute', top: 0, left: 0, bottom: 0, width: `${pct * 100}%`, background: color, borderRadius: 1 }} />
      </div>
    );
  }

  // Number formatting
  const fmt = {
    usd: n => (n < 0 ? '-$' : '$') + Math.abs(n).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }),
    pct: n => (n >= 0 ? '+' : '') + (n * 100).toFixed(2) + '%',
    pct1: n => (n >= 0 ? '+' : '') + n.toFixed(2) + '%',
    compact: n => n >= 1000 ? '$' + (n / 1000).toFixed(1) + 'k' : '$' + n.toFixed(0),
  };

  // Hook: ticker (updates "now" every second)
  function useNow(interval = 1000) {
    const [t, setT] = useState(new Date('2026-04-22T15:32:00'));
    useEffect(() => {
      const id = setInterval(() => setT(prev => new Date(prev.getTime() + 1000)), interval);
      return () => clearInterval(id);
    }, [interval]);
    return t;
  }

  const clockStr = d => d.toTimeString().slice(0, 8);
  const dateStr = d => d.toISOString().slice(0, 10);

  Object.assign(window, { LineChart, BlinkDot, Bar, fmt, useNow, clockStr, dateStr });
})();
