// components.jsx — shared UI for Heartbeat Kit pages

const { useEffect, useState, useRef, useMemo } = React;

// ─────────────────────────────────────────────────────────────────────────────
// Wordmark / logo
function Logo({ size = 22 }) {
  return (
    <a href="index.html" style={{ display: 'inline-flex', alignItems: 'center', gap: 10 }}>
      <svg width={size} height={size} viewBox="0 0 24 24" fill="none" aria-hidden>
        <circle cx="12" cy="12" r="10" stroke="#00E4A8" strokeWidth="1.4" opacity="0.4" />
        <path d="M2 12 H7 L9 6 L12 18 L15 9 L17 12 H22"
              stroke="#00E4A8" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" fill="none" />
      </svg>
      <span style={{
        fontFamily: 'var(--f-display)',
        fontWeight: 600, fontSize: 16, letterSpacing: '-0.01em', color: 'inherit'
      }}>
        Heartbeat Kit
      </span>
    </a>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Top nav
function Nav({ current = 'home' }) {
  const link = (id, href, label) => (
    <a href={href} style={{
      color: current === id ? 'var(--sand)' : 'var(--slate)',
      fontSize: 14, fontWeight: 500, padding: '8px 4px',
      borderBottom: current === id ? '1px solid var(--cyan)' : '1px solid transparent',
    }}>{label}</a>
  );
  return (
    <header style={{
      position: 'sticky', top: 0, zIndex: 50,
      background: 'rgba(10,11,16,0.78)',
      backdropFilter: 'blur(14px) saturate(140%)',
      WebkitBackdropFilter: 'blur(14px) saturate(140%)',
      borderBottom: '1px solid rgba(255,255,255,0.06)',
    }}>
      <div className="wrap" style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        height: 64,
      }}>
        <Logo />
        <nav style={{ display: 'flex', gap: 28, alignItems: 'center' }}>
          {link('docs', 'docs.html', 'Docs')}
          {link('pricing', 'pricing.html', 'Pricing')}
          <a href="#" style={{ color: 'var(--slate)', fontSize: 14, fontWeight: 500 }}>Changelog</a>
          <a href="#" style={{ color: 'var(--slate)', fontSize: 14, fontWeight: 500 }}>GitHub</a>
        </nav>
        <div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
          <a className="btn btn-ghost" href="#">Sign in</a>
          <a className="btn btn-primary" href="#">Start free →</a>
        </div>
      </div>
    </header>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Footer
function Footer() {
  const col = (title, links) => (
    <div className="col gap-12">
      <div className="eyebrow">{title}</div>
      {links.map(l => (
        <a key={l} href="#" style={{ color: 'var(--slate)', fontSize: 14 }}>{l}</a>
      ))}
    </div>
  );
  return (
    <footer style={{ background: 'var(--black)', borderTop: '1px solid rgba(255,255,255,0.06)', padding: '64px 0 40px' }}>
      <div className="wrap">
        <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1fr 1fr 1fr 1fr', gap: 48 }}>
          <div className="col gap-16">
            <Logo />
            <p className="muted" style={{ fontSize: 14, maxWidth: 280, margin: 0 }}>
              The accountability layer for autonomous systems. Built on Cloudflare's edge.
            </p>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, color: 'var(--slate)' }}>
              <span className="dot active" />
              <span className="mono">api.heartbeatkit.com — operational</span>
            </div>
          </div>
          {col('Product', ['Heartbeats', 'Channels', 'Policies', 'AI Responder', 'MCP Server'])}
          {col('Solutions', ['AI agents', 'DevOps & SRE', 'IoT fleets', 'Safety & care', 'Compliance'])}
          {col('Developers', ['Docs', 'API reference', 'SDK', 'Status', 'Changelog'])}
          {col('Company', ['About', 'Customers', 'Careers', 'Security', 'Contact'])}
        </div>
        <div style={{
          marginTop: 56, paddingTop: 24,
          borderTop: '1px solid rgba(255,255,255,0.06)',
          display: 'flex', justifyContent: 'space-between', fontSize: 13, color: 'var(--slate)',
        }}>
          <span>© 2026 Heartbeat Kit, Inc.</span>
          <span className="mono">If it goes silent, we respond.</span>
        </div>
      </div>
    </footer>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Animated ECG pulse line — real oscilloscope-style decay trail.
function PulseLine({ height = 220, color = '#00E4A8', state = 'active', strokeWidth = 1.6 }) {
  const W = 1600;
  const H = height;
  const mid = H / 2;
  const id = useMemo(() => 'ekg-' + Math.random().toString(36).slice(2, 9), []);

  const beats = useMemo(() => {
    const out = [];
    let x = 0;
    while (x < W + 200) { out.push({ x }); x += 220; }
    return out;
  }, []);

  const pathD = useMemo(() => {
    let d = `M -50 ${mid} `;
    beats.forEach(b => {
      const bx = b.x;
      d += `L ${bx - 60} ${mid} `;
      d += `Q ${bx - 50} ${mid - 6}, ${bx - 40} ${mid} `;
      d += `L ${bx - 22} ${mid} `;
      d += `L ${bx - 14} ${mid + 10} `;
      d += `L ${bx - 4} ${mid - 60} `;
      d += `L ${bx + 6} ${mid + 22} `;
      d += `L ${bx + 16} ${mid} `;
      d += `L ${bx + 30} ${mid} `;
      d += `Q ${bx + 50} ${mid - 12}, ${bx + 70} ${mid} `;
      d += `L ${bx + 150} ${mid} `;
    });
    d += `L ${W + 200} ${mid}`;
    return d;
  }, [beats, mid]);

  const isFiring = state === 'firing';
  const dur = isFiring ? 4.2 : 7.8;
  const trailLen = 520;          // length of the visible trail
  const totalLen = 8000;         // bigger than path

  return (
    <div style={{ position: 'relative', overflow: 'hidden' }}>
      <svg viewBox={`0 0 ${W} ${H}`} className="pulse-svg" preserveAspectRatio="xMidYMid slice"
           style={{ height }}>
        <defs>
          <pattern id={`grid-${id}`} width="40" height="40" patternUnits="userSpaceOnUse">
            <path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(255,255,255,0.035)" strokeWidth="0.5" />
          </pattern>
          {/* Linear gradient: opacity fades from 0 (left) → 1 (right). The dashed trace
              starts on the right and travels left into the fade, so old beats decay. */}
          <linearGradient id={`fade-${id}`} x1="0" x2="1" y1="0" y2="0">
            <stop offset="0"    stopColor="#000" stopOpacity="0" />
            <stop offset="0.55" stopColor="#000" stopOpacity="0.2" />
            <stop offset="0.85" stopColor="#000" stopOpacity="1" />
            <stop offset="1"    stopColor="#000" stopOpacity="1" />
          </linearGradient>
          <mask id={`mask-${id}`}>
            <rect width={W} height={H} fill={`url(#fade-${id})`} />
          </mask>
          <filter id={`glow-${id}`} x="-10%" y="-10%" width="120%" height="120%">
            <feGaussianBlur stdDeviation="3" result="b" />
            <feMerge>
              <feMergeNode in="b" />
              <feMergeNode in="SourceGraphic" />
            </feMerge>
          </filter>
        </defs>

        <rect width={W} height={H} fill={`url(#grid-${id})`} />
        <line x1="0" y1={mid} x2={W} y2={mid} stroke="rgba(255,255,255,0.05)" strokeWidth="1" />

        <g mask={`url(#mask-${id})`}>
          {/* Faint full trace (always there, very dim) */}
          <path d={pathD} fill="none" stroke={color} strokeOpacity="0.06" strokeWidth={strokeWidth - 0.4} />
          {/* Trailing trace — long dash that scans across, fades via the mask */}
          <path d={pathD} fill="none" stroke={color} strokeWidth={strokeWidth}
                strokeLinecap="round"
                style={{
                  strokeDasharray: `${trailLen} ${totalLen}`,
                  strokeDashoffset: 0,
                  animation: `ekg-${id} ${dur}s linear infinite`,
                }} />
          {/* Glow head — short bright dash at the leading edge */}
          <path d={pathD} fill="none" stroke={color} strokeWidth={strokeWidth + 0.6}
                strokeLinecap="round"
                filter={`url(#glow-${id})`}
                style={{
                  strokeDasharray: `60 ${totalLen}`,
                  animation: `ekg-${id} ${dur}s linear infinite`,
                }} />
        </g>
      </svg>
      <style>{`
        @keyframes ekg-${id} {
          from { stroke-dashoffset: 0; }
          to   { stroke-dashoffset: -${W + 200}; }
        }
      `}</style>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Terminal / streaming logs
function Terminal({ lines, height = 360, title = 'agent.log' }) {
  const [visible, setVisible] = useState(0);
  const ref = useRef(null);

  useEffect(() => {
    if (visible >= lines.length) {
      // restart after a beat
      const t = setTimeout(() => setVisible(0), 4500);
      return () => clearTimeout(t);
    }
    const t = setTimeout(() => setVisible(v => v + 1), lines[visible]?.delay ?? 380);
    return () => clearTimeout(t);
  }, [visible, lines]);

  useEffect(() => {
    if (ref.current) ref.current.scrollTop = ref.current.scrollHeight;
  }, [visible]);

  return (
    <div style={{
      background: '#06070b',
      border: '1px solid var(--border)',
      borderRadius: 12,
      overflow: 'hidden',
      fontFamily: 'var(--f-mono)',
      fontSize: 13.5,
      boxShadow: 'var(--shadow-soft)',
    }}>
      <div style={{
        display: 'flex', alignItems: 'center', gap: 8,
        padding: '10px 14px',
        borderBottom: '1px solid var(--border)',
        background: 'linear-gradient(180deg, #0d0e15, #0a0b10)',
      }}>
        <span style={{ width: 10, height: 10, borderRadius: 5, background: '#3a3b48' }} />
        <span style={{ width: 10, height: 10, borderRadius: 5, background: '#3a3b48' }} />
        <span style={{ width: 10, height: 10, borderRadius: 5, background: '#3a3b48' }} />
        <span className="mono" style={{ color: 'var(--slate)', fontSize: 12, marginLeft: 12 }}>
          {title}
        </span>
        <span style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 6,
                       color: 'var(--slate)', fontSize: 12 }}>
          <span className="dot active" /> live
        </span>
      </div>
      <div ref={ref} style={{
        height, overflow: 'auto', padding: '16px 18px',
        color: '#cdd0d8', lineHeight: 1.7,
      }}>
        {lines.slice(0, visible).map((ln, i) => (
          <div key={i} style={{ display: 'flex', gap: 12, opacity: 0, animation: 'fadein 220ms forwards' }}>
            <span style={{ color: '#5b6076', userSelect: 'none', minWidth: 80 }}>{ln.t}</span>
            <span style={{ color: ln.color || '#cdd0d8', whiteSpace: 'pre-wrap', flex: 1 }}>
              {ln.tag && <span style={{
                fontSize: 10.5, padding: '2px 6px', borderRadius: 4,
                background: ln.tagBg || 'rgba(0,228,168,0.12)',
                color: ln.tagColor || 'var(--cyan)', marginRight: 8,
                fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase',
              }}>{ln.tag}</span>}
              {ln.text}
            </span>
          </div>
        ))}
        {visible < lines.length && (
          <div style={{ display: 'inline-block', width: 7, height: 14,
                        background: 'var(--cyan)', verticalAlign: 'text-bottom',
                        animation: 'blink 1s steps(2) infinite' }} />
        )}
      </div>
      <style>{`
        @keyframes fadein { to { opacity: 1; } }
        @keyframes blink { 50% { opacity: 0; } }
      `}</style>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Code block with syntax-class spans
function Code({ children, lang = 'ts', label }) {
  return (
    <div style={{
      borderRadius: 12, overflow: 'hidden',
      border: '1px solid var(--border)',
      background: '#06070b',
      boxShadow: 'var(--shadow-soft)',
    }}>
      {label && (
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '10px 16px',
          borderBottom: '1px solid var(--border)',
          background: 'linear-gradient(180deg, #0d0e15, #0a0b10)',
          fontFamily: 'var(--f-mono)', fontSize: 12, color: 'var(--slate)',
        }}>
          <span>{label}</span>
          <span style={{ fontSize: 11, padding: '2px 8px', borderRadius: 4,
                         background: 'rgba(255,255,255,0.05)', color: 'var(--slate)' }}>{lang}</span>
        </div>
      )}
      <pre className="code" style={{ margin: 0, border: 0, borderRadius: 0 }}>
        {children}
      </pre>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Live dashboard mock — animated table of heartbeats with status changes
function DashboardMock() {
  const [rows, setRows] = useState(() => initialRows());
  const [tick, setTick] = useState(0);

  useEffect(() => {
    const i = setInterval(() => setTick(t => t + 1), 1400);
    return () => clearInterval(i);
  }, []);

  useEffect(() => {
    setRows(prev => prev.map((r, i) => {
      // advance "last check-in" timer
      if (r.status === 'active') {
        let nx = r.lastSec + 1;
        if (nx >= r.interval + 4) {
          // simulate a missed check-in occasionally
          if (i === 3 && tick % 14 === 13) return { ...r, status: 'firing', lastSec: nx, firingFor: 0 };
          nx = 0;
        }
        return { ...r, lastSec: nx };
      }
      if (r.status === 'firing') {
        const f = r.firingFor + 1;
        if (f > 8) return { ...r, status: 'active', lastSec: 0, firingFor: 0 };
        return { ...r, firingFor: f };
      }
      return r;
    }));
  }, [tick]);

  return (
    <div style={{
      background: '#0c0d14',
      border: '1px solid var(--border)',
      borderRadius: 14,
      overflow: 'hidden',
      boxShadow: 'var(--shadow-soft)',
    }}>
      {/* App chrome */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 16,
        padding: '14px 20px',
        borderBottom: '1px solid var(--border)',
        background: 'linear-gradient(180deg, #11121b, #0c0d14)',
      }}>
        <div style={{ display: 'flex', gap: 6 }}>
          <span style={{ width: 10, height: 10, borderRadius: 5, background: '#ff5f57' }} />
          <span style={{ width: 10, height: 10, borderRadius: 5, background: '#febc2e' }} />
          <span style={{ width: 10, height: 10, borderRadius: 5, background: '#28c840' }} />
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, fontFamily: 'var(--f-mono)', fontSize: 12, color: 'var(--slate)' }}>
          <Logo size={16} />
          <span style={{ color: '#3a3b48' }}>/</span>
          <span>Fleet</span>
          <span style={{ color: '#3a3b48' }}>/</span>
          <span style={{ color: 'var(--sand)' }}>production</span>
        </div>
        <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 14 }}>
          <span style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, color: 'var(--slate)', fontFamily: 'var(--f-mono)' }}>
            <span className="dot active" /> WS connected
          </span>
        </div>
      </div>

      {/* Stat strip */}
      <div style={{
        display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)',
        borderBottom: '1px solid var(--border)',
      }}>
        <Stat label="Active" value={rows.filter(r => r.status === 'active').length} accent="var(--cyan)" />
        <Stat label="Firing" value={rows.filter(r => r.status === 'firing').length} accent="var(--coral)" />
        <Stat label="Paused" value={rows.filter(r => r.status === 'paused').length} accent="var(--slate)" />
        <Stat label="Check-ins / hr" value={"4,218"} accent="var(--sand)" />
      </div>

      {/* Toolbar */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 12,
        padding: '14px 20px',
        borderBottom: '1px solid var(--border)',
        fontSize: 13, color: 'var(--slate)',
      }}>
        <Pill active>All</Pill>
        <Pill><span className="dot active" style={{ marginRight: 6 }} /> Active</Pill>
        <Pill><span className="dot firing" style={{ marginRight: 6 }} /> Firing</Pill>
        <Pill>Paused</Pill>
        <span style={{ marginLeft: 'auto', fontFamily: 'var(--f-mono)', fontSize: 12 }}>
          ⌘K  search heartbeats
        </span>
      </div>

      {/* Table */}
      <div>
        <div style={{
          display: 'grid', gridTemplateColumns: '24px 2.4fr 1fr 1fr 1.4fr 1fr 80px',
          padding: '12px 20px',
          fontFamily: 'var(--f-mono)', fontSize: 11, letterSpacing: '0.06em',
          textTransform: 'uppercase', color: 'var(--slate)',
          borderBottom: '1px solid var(--border)',
        }}>
          <span></span>
          <span>Heartbeat</span>
          <span>Mode</span>
          <span>Last check-in</span>
          <span>Next deadline</span>
          <span>Namespace</span>
          <span style={{ textAlign: 'right' }}>State</span>
        </div>
        {rows.map((r, i) => (
          <Row key={r.name} row={r} />
        ))}
      </div>
    </div>
  );
}

function Stat({ label, value, accent }) {
  return (
    <div style={{ padding: '18px 20px', borderRight: '1px solid var(--border)' }}>
      <div className="eyebrow" style={{ fontSize: 11 }}>{label}</div>
      <div style={{
        fontFamily: 'var(--f-display)', fontSize: 32, fontWeight: 600,
        letterSpacing: '-0.02em', color: accent, marginTop: 4,
        fontVariantNumeric: 'tabular-nums',
      }}>
        {value}
      </div>
    </div>
  );
}

function Pill({ children, active }) {
  return (
    <span style={{
      padding: '6px 12px', borderRadius: 999,
      border: `1px solid ${active ? 'var(--cyan)' : 'var(--border)'}`,
      color: active ? 'var(--cyan)' : 'var(--slate)',
      background: active ? 'rgba(0,228,168,0.08)' : 'transparent',
      fontSize: 13, display: 'inline-flex', alignItems: 'center',
    }}>{children}</span>
  );
}

function Row({ row }) {
  const stateLabel = row.status === 'active' ? 'OK'
                   : row.status === 'firing' ? 'FIRING'
                   : 'PAUSED';
  const stateColor = row.status === 'active' ? 'var(--cyan)'
                   : row.status === 'firing' ? 'var(--coral)'
                   : 'var(--slate)';
  return (
    <div style={{
      display: 'grid', gridTemplateColumns: '24px 2.4fr 1fr 1fr 1.4fr 1fr 80px',
      padding: '14px 20px',
      borderBottom: '1px solid rgba(255,255,255,0.04)',
      alignItems: 'center', fontSize: 14,
      background: row.status === 'firing' ? 'rgba(255,111,97,0.04)' : 'transparent',
      borderLeft: row.status === 'firing' ? '2px solid var(--coral)'
                  : row.status === 'active' ? '2px solid transparent'
                  : '2px solid transparent',
      transition: 'background 240ms ease',
    }}>
      <span className={`dot ${row.status}`} />
      <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
        <span style={{ fontFamily: 'var(--f-mono)', fontSize: 13.5, color: 'var(--sand)' }}>{row.name}</span>
        <span style={{ fontSize: 12, color: 'var(--slate)' }}>{row.desc}</span>
      </div>
      <span style={{ fontSize: 12.5, color: 'var(--slate)', fontFamily: 'var(--f-mono)' }}>{row.mode}</span>
      <span style={{ fontSize: 13, fontFamily: 'var(--f-mono)', color: row.status === 'firing' ? 'var(--coral)' : 'var(--sand)' }}>
        {row.status === 'firing' ? `${row.firingFor + row.lastSec - row.interval}s overdue` : `${row.lastSec}s ago`}
      </span>
      <span style={{ fontSize: 13, fontFamily: 'var(--f-mono)', color: 'var(--slate)' }}>
        {row.status === 'firing' ? '— escalating' : `in ${Math.max(0, row.interval - row.lastSec)}s`}
      </span>
      <span style={{ fontSize: 12.5, color: 'var(--slate)', fontFamily: 'var(--f-mono)' }}>{row.ns}</span>
      <span style={{ textAlign: 'right' }}>
        <span style={{
          fontFamily: 'var(--f-mono)', fontSize: 11, fontWeight: 600,
          padding: '3px 8px', borderRadius: 4,
          background: row.status === 'firing' ? 'rgba(255,111,97,0.15)'
                    : row.status === 'active' ? 'rgba(0,228,168,0.12)'
                    : 'rgba(163,166,171,0.12)',
          color: stateColor, letterSpacing: '0.06em',
        }}>{stateLabel}</span>
      </span>
    </div>
  );
}

function initialRows() {
  return [
    { name: 'order-processor', desc: 'Stripe order webhook consumer', mode: 'recurring · 60s', interval: 60, lastSec: 12, ns: 'commerce', status: 'active', firingFor: 0 },
    { name: 'nightly-backup',  desc: 'Postgres → R2 snapshot',       mode: 'scheduled · 03:00', interval: 90, lastSec: 41, ns: 'infra',   status: 'active', firingFor: 0 },
    { name: 'agent-research-7', desc: 'Long-running research agent',  mode: 'recurring · 30s', interval: 30, lastSec: 18, ns: 'agents',  status: 'active', firingFor: 0 },
    { name: 'embed-worker',    desc: 'Vector embedding pipeline',     mode: 'recurring · 45s', interval: 45, lastSec: 50, ns: 'agents',  status: 'firing', firingFor: 2 },
    { name: 'deploy-canary',   desc: 'Post-deploy health verification', mode: 'oneshot · 600s', interval: 80, lastSec: 30, ns: 'deploys', status: 'active', firingFor: 0 },
    { name: 'iot-fleet-eu-west', desc: '2,418 devices reporting',      mode: 'recurring · 120s', interval: 120, lastSec: 67, ns: 'iot',    status: 'active', firingFor: 0 },
    { name: 'lone-worker-tx',  desc: 'Field crew check-in',           mode: 'scheduled · hourly', interval: 100, lastSec: 22, ns: 'safety', status: 'paused', firingFor: 0 },
  ];
}

// ─────────────────────────────────────────────────────────────────────────────
// Escalation flow visualizer — animated walk through steps
function EscalationFlow() {
  const steps = [
    { t: '00:00', label: 'Heartbeat misses deadline',  ch: 'firing',   icon: '!' },
    { t: '00:00', label: 'Webhook → Slack #incidents', ch: 'webhook',  icon: '→' },
    { t: '05:00', label: 'Email → on-call rotation',   ch: 'email',    icon: '@' },
    { t: '15:00', label: 'AI agent investigates',      ch: 'agent',    icon: 'AI' },
    { t: '15:42', label: 'Agent acks — incident logged', ch: 'resolved', icon: '✓' },
  ];
  const [active, setActive] = useState(0);
  useEffect(() => {
    const i = setInterval(() => setActive(a => (a + 1) % (steps.length + 1)), 1800);
    return () => clearInterval(i);
  }, []);

  return (
    <div style={{
      background: '#0c0d14',
      border: '1px solid var(--border)',
      borderRadius: 14,
      padding: '32px 28px',
      boxShadow: 'var(--shadow-soft)',
    }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 24 }}>
        <span className="eyebrow">Policy: critical-response</span>
        <span className="mono" style={{ fontSize: 12, color: 'var(--slate)' }}>step {Math.min(active, steps.length)} / {steps.length}</span>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 0, position: 'relative' }}>
        {/* vertical line */}
        <div style={{
          position: 'absolute', left: 19, top: 18, bottom: 18,
          width: 1, background: 'var(--border)',
        }} />
        <div style={{
          position: 'absolute', left: 19, top: 18,
          width: 1, background: 'linear-gradient(180deg, var(--coral), var(--cyan))',
          height: `calc(${Math.max(0, Math.min(active, steps.length) - 1) * (100 / (steps.length - 1))}%)`,
          transition: 'height 600ms ease-out',
        }} />
        {steps.map((s, i) => {
          const reached = i < active;
          const current = i === active - 1;
          const colorMap = {
            firing:   'var(--coral)',
            webhook:  '#9ec3ff',
            email:    'var(--rose)',
            agent:    'var(--cyan)',
            resolved: 'var(--cyan)',
          };
          const c = colorMap[s.ch];
          return (
            <div key={i} style={{
              display: 'flex', gap: 18, alignItems: 'center',
              padding: '14px 0',
              opacity: reached ? 1 : 0.3,
              transition: 'opacity 320ms ease',
            }}>
              <div style={{
                width: 38, height: 38, borderRadius: '50%',
                background: reached ? c : 'transparent',
                color: reached ? '#0a0b10' : 'var(--slate)',
                border: `1px solid ${reached ? c : 'var(--border)'}`,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontFamily: 'var(--f-mono)', fontSize: 12, fontWeight: 700,
                boxShadow: current ? `0 0 0 6px ${c}22` : 'none',
                transition: 'all 320ms ease',
                zIndex: 1,
              }}>{s.icon}</div>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 15, color: reached ? 'var(--sand)' : 'var(--slate)', fontWeight: 500 }}>
                  {s.label}
                </div>
                <div className="mono" style={{ fontSize: 12, color: 'var(--slate)', marginTop: 2 }}>
                  T+{s.t} · channel {s.ch}
                </div>
              </div>
              {current && <span className="dot firing" />}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// Export to window
Object.assign(window, {
  Logo, Nav, Footer,
  PulseLine, Terminal, Code,
  DashboardMock, EscalationFlow,
});
