// Avakata — interactive demo: "Drive the engine."
// A real state machine the visitor runs: scan -> diagnose -> rewrite -> critic
// gate -> ship -> measure. Outcomes only (never agent internals). Themed via
// site.css variables so the Tweaks panel restyles it live.

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

/* ---------- content per industry ---------- */
const INDUSTRIES = {
  ecom: {
    tag: 'E-commerce · DTC',
    url: 'kickhaus.shop / vapor-strike-fg',
    title: 'Vapor Strike FG Cleats',
    meta: '$280',
    media: 'cleat',
    oldCopy: 'Firm-ground soccer cleats. Synthetic upper, molded studs. Sizes 7–13. Lightweight build suitable for all positions.',
    newCopy: 'The boot that turns your first step into your fastest. A second-skin upper welds your foot to the ball; the stud plate bites firm ground and fires you forward. 280 grams of pure acceleration — laced, lethal, ready for kickoff.',
    diagnosis: 'High CTR, strong add-to-cart — but checkout conversion is bottom-decile. The copy spec-sheets the boot; it never makes a player feel faster wearing it.',
    signals: [
    { label: 'Click-through', fill: 84, tone: 'green', val: 'High' },
    { label: 'Add to cart', fill: 52, tone: 'amber', val: 'Mid' },
    { label: 'Conversion', fill: 22, tone: 'red', val: 'Low' }],

    lift: 186, metric: 'PDP conversion'
  },
  legal: {
    tag: 'Legal · personal injury',
    url: 'hawthornereed.law / free-consultation',
    title: 'Injured? Talk to a lawyer.',
    meta: 'Free consult',
    media: 'art',
    art: '<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="dzLe" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#2a2410"/><stop offset="1" stop-color="#0a0b0d"/></linearGradient></defs><rect width="400" height="300" fill="url(#dzLe)"/><g stroke="#e8c66a" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="200" y1="66" x2="200" y2="236"/><line x1="116" y1="104" x2="284" y2="104"/><line x1="116" y1="104" x2="90" y2="160"/><line x1="116" y1="104" x2="142" y2="160"/><line x1="284" y1="104" x2="258" y2="160"/><line x1="284" y1="104" x2="310" y2="160"/><path d="M86 160a30 16 0 0 0 60 0"/><path d="M254 160a30 16 0 0 0 60 0"/><line x1="158" y1="236" x2="242" y2="236"/></g><circle cx="200" cy="66" r="7" fill="#e8c66a"/></svg>',
    oldCopy: 'We handle personal injury cases. Contact our office to schedule a consultation with one of our attorneys. We have years of experience.',
    newCopy: 'If someone else\u2019s mistake put you in pain, you have less time to act than you think. Talk to a senior injury attorney today — no fee unless we win. Most calls are returned within the hour.',
    diagnosis: 'Page ranks but is rarely cited by generative engines, and form-starts are low. Copy is generic and buries the only thing that matters: act now, no upfront fee.',
    signals: [
    { label: 'Impressions', fill: 78, tone: 'green', val: 'High' },
    { label: 'LLM citations', fill: 30, tone: 'red', val: 'Rare' },
    { label: 'Form starts', fill: 34, tone: 'amber', val: 'Low' }],

    lift: 74, metric: 'consultation starts'
  },
  saas: {
    tag: 'B2B · developer tools',
    url: 'nudge.dev / home',
    title: 'Ship reliable webhooks.',
    meta: 'Start free',
    media: 'art',
    art: '<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="dzSa" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#0b2036"/><stop offset="1" stop-color="#0a0b0d"/></linearGradient><linearGradient id="dzSl" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="#38e1ff"/><stop offset="1" stop-color="#b06bff"/></linearGradient></defs><rect width="400" height="300" fill="url(#dzSa)"/><g stroke="#38e1ff" stroke-width="1" opacity="0.16"><path d="M40 70H360M40 120H360M40 170H360M40 220H360"/></g><polyline points="40,214 100,182 150,192 210,120 270,140 330,72" fill="none" stroke="url(#dzSl)" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/><g fill="#38e1ff"><circle cx="100" cy="182" r="5"/><circle cx="210" cy="120" r="5"/><circle cx="330" cy="72" r="6"/></g></svg>',
    oldCopy: 'Nudge is a webhook infrastructure platform. It offers retries, logging, and a dashboard. Sign up to get started with our service today.',
    newCopy: 'Webhooks fail silently and take your weekend with them. Nudge retries, replays, and tells you the moment one breaks — so you find out before your customer does. Live in five minutes, free to 100k events.',
    diagnosis: 'Heavy hero traffic, thin demo-request rate. The copy lists features instead of naming the 2 a.m. pain a developer actually feels.',
    signals: [
    { label: 'Hero traffic', fill: 80, tone: 'green', val: 'High' },
    { label: 'Scroll depth', fill: 48, tone: 'amber', val: 'Mid' },
    { label: 'Demo requests', fill: 24, tone: 'red', val: 'Low' }],

    lift: 112, metric: 'demo requests'
  }
};

const PHASES = ['scan', 'diagnose', 'rewrite', 'gate', 'ship', 'measure', 'done'];
const PHASE_LABEL = {
  idle: 'Idle · press run',
  scan: 'Scanning traffic, heatmaps, search console…',
  diagnose: 'Diagnosing the friction…',
  rewrite: 'Rewriting copy…',
  gate: 'Critic gate · brand voice · factual · 3 negative controls…',
  ship: 'Shipping variant to 18% of traffic…',
  measure: 'Measuring the 48-hour test window…',
  done: 'Done · change live and logged'
};

/* ---------- small components ---------- */

function Dot({ tone = 'acc', size = 7 }) {
  const map = { acc: 'var(--accent)', cyan: 'var(--signal-cyan)', green: 'var(--signal-green)', amber: 'var(--signal-amber)', red: 'var(--signal-red)', warm: 'var(--signal-warm)' };
  const c = map[tone] || map.acc;
  return <span className="dd" style={{ width: size, height: size, background: c, boxShadow: `0 0 8px ${c}` }} />;
}

function Signal({ s, armed }) {
  const map = { green: 'var(--signal-green)', amber: 'var(--signal-amber)', red: 'var(--signal-red)' };
  return (
    <div className="dz-bar">
      <span className="dz-bar__l">{s.label}</span>
      <span className="dz-bar__t">
        <span className="dz-bar__f" style={{ width: (armed ? s.fill : 0) + '%', background: map[s.tone], boxShadow: `0 0 10px ${map[s.tone]}` }} />
      </span>
      <span className="dz-bar__v">{s.val}</span>
    </div>);

}

function Sparkline({ progress, lift }) {
  const data = [12, 13, 13, 14, 13, 15, 18, 24, 33, 46, 62, 80, 100];
  const w = 280,h = 84,max = 100,min = 10;
  const pts = data.map((v, i) => [i / (data.length - 1) * w, h - (v - min) / (max - min) * (h - 8) - 4]);
  const n = Math.max(2, Math.floor(progress * data.length));
  const sub = pts.slice(0, n);
  const d = sub.map((p, i) => i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`).join(' ');
  const last = sub[sub.length - 1];
  return (
    <svg viewBox={`0 0 ${w} ${h}`} style={{ width: '100%', height: h, overflow: 'visible' }}>
      <defs>
        <linearGradient id="dz-spark" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.32" />
          <stop offset="100%" stopColor="var(--accent)" stopOpacity="0" />
        </linearGradient>
      </defs>
      <path d={`${d} L${last[0]},${h} L0,${h} Z`} fill="url(#dz-spark)" />
      <path d={d} fill="none" stroke="var(--accent)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
      <circle cx={last[0]} cy={last[1]} r="4" fill="var(--accent)" />
    </svg>);

}

/* ---------- main app ---------- */

function CleatVisual() {
  return (
    <svg className="dz-cleat" viewBox="0 0 420 200" preserveAspectRatio="xMidYMid meet" aria-hidden="true">
      <defs>
        <linearGradient id="dz-boot" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor="#24272e" />
          <stop offset="100%" stopColor="#0d0e12" />
        </linearGradient>
        <linearGradient id="dz-boot-acc" x1="0" x2="1" y1="0" y2="0">
          <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.95" />
          <stop offset="100%" stopColor="var(--signal-cyan)" stopOpacity="0.9" />
        </linearGradient>
      </defs>
      {/* sole plate */}
      <path d="M92 158 C 200 162 300 158 372 150 L 372 163 C 300 171 200 173 96 169 Z" fill="#0a0b0d" stroke="rgba(196,255,61,0.25)" strokeWidth="1" />
      {/* studs */}
      <g fill="#14171c" stroke="var(--accent)" strokeWidth="1.2">
        {[112, 154, 200, 250, 300, 348].map((x, i) =>
          <path key={i} d={`M${x - 8} 168 L${x + 8} 168 L${x} 186 Z`} />
        )}
      </g>
      {/* upper */}
      <path d="M78 88 C 70 60 104 50 134 52 C 160 54 176 62 198 78 C 252 106 322 130 372 150 L 372 160 C 300 162 160 164 96 162 L 88 160 C 74 150 72 114 78 88 Z" fill="url(#dz-boot)" stroke="rgba(196,255,61,0.55)" strokeWidth="1.5" />
      {/* heel collar */}
      <path d="M86 92 C 80 70 104 62 128 64" fill="none" stroke="rgba(232,234,237,0.22)" strokeWidth="2" />
      {/* dynamic accent stripe */}
      <path d="M150 150 C 210 120 292 118 348 138 C 292 132 210 136 158 156 Z" fill="url(#dz-boot-acc)" />
      {/* laces */}
      <g stroke="rgba(232,234,237,0.62)" strokeWidth="2.2" strokeLinecap="round">
        <line x1="132" y1="74" x2="156" y2="64" />
        <line x1="140" y1="84" x2="166" y2="74" />
        <line x1="150" y1="94" x2="178" y2="84" />
        <line x1="162" y1="103" x2="192" y2="93" />
      </g>
      {/* toe highlight */}
      <path d="M300 132 C 330 140 352 146 366 150" fill="none" stroke="rgba(61,217,255,0.5)" strokeWidth="2" />
    </svg>);

}

function EngineDemo() {
  const [industry, setIndustry] = useState('ecom');
  const [phase, setPhase] = useState('idle');
  const [log, setLog] = useState([]);
  const [typed, setTyped] = useState(0); // chars of new copy revealed
  const [spark, setSpark] = useState(0); // 0..1
  const [liftNum, setLiftNum] = useState(0);
  const [heat, setHeat] = useState(false);
  const [accepted, setAccepted] = useState(null); // null | 'accepted' | 'rolledback'
  const timers = useRef([]);

  const data = INDUSTRIES[industry];
  const running = phase !== 'idle' && phase !== 'done';
  const rewritten = PHASES.indexOf(phase) >= PHASES.indexOf('rewrite');
  const showResults = phase === 'measure' || phase === 'done';

  const clearTimers = () => {timers.current.forEach(clearTimeout);timers.current = [];};
  const after = (ms, fn) => {const id = setTimeout(fn, ms);timers.current.push(id);};

  const pushLog = useCallback((icon, tone, text) => {
    setLog((l) => [...l.slice(-7), { icon, tone, text, t: Date.now() }]);
  }, []);

  const reset = () => {
    clearTimers();
    setPhase('idle');setLog([]);setTyped(0);setSpark(0);setLiftNum(0);setAccepted(null);
  };

  const switchIndustry = (k) => {if (k === industry) return;reset();setIndustry(k);};

  const run = () => {
    clearTimers();
    setLog([]);setTyped(0);setSpark(0);setLiftNum(0);setAccepted(null);
    setPhase('scan');
    pushLog('⟳', 'cyan', 'Connected · GA4 · Search Console · heatmaps');

    after(1400, () => {setPhase('diagnose');pushLog('⚠', 'warm', 'Friction found · ' + data.signals[data.signals.length - 1].label + ' bottom-decile');});
    after(2900, () => {setPhase('rewrite');pushLog('✎', 'acc', 'Drafting variant · brand voice locked');});
    // typewriter handled by effect on phase==='rewrite'
    after(6400, () => {setPhase('gate');pushLog('◈', 'cyan', 'Critic gate · 3 negative controls');});
    after(7800, () => {setPhase('gate');pushLog('✓', 'green', 'Critic gate passed · confidence 0.94');});
    after(8600, () => {setPhase('ship');pushLog('◈', 'cyan', 'Variant live · 18% of traffic');});
    after(10000, () => {setPhase('measure');pushLog('▲', 'acc', 'Measuring 48h window…');});
    after(13200, () => {setPhase('done');pushLog('✓', 'green', '+' + data.lift + '% ' + data.metric + ' · kept & logged');});
  };

  // typewriter during rewrite
  useEffect(() => {
    if (phase !== 'rewrite') return;
    setTyped(0);
    const full = data.newCopy.length;
    const dur = 3200;const t0 = performance.now();
    let raf;
    const tick = (now) => {
      const p = Math.min(1, (now - t0) / dur);
      setTyped(Math.floor(p * full));
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [phase, industry, data.newCopy]);

  // sparkline + lift counter during measure
  useEffect(() => {
    if (phase !== 'measure' && phase !== 'done') return;
    const dur = 3000;const t0 = performance.now();
    let raf;
    const tick = (now) => {
      const p = Math.min(1, (now - t0) / dur);
      const e = 1 - Math.pow(1 - p, 3);
      setSpark(e);setLiftNum(Math.floor(e * data.lift));
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [phase, data.lift]);

  useEffect(() => () => clearTimers(), []);

  const phaseIdx = PHASES.indexOf(phase);
  const progressPct = phase === 'idle' ? 0 : Math.min(100, (phaseIdx + 1) / PHASES.length * 100);

  return (
    <div className="dz">
      {/* toolbar */}
      <div className="dz-toolbar">
        <div className="dz-tabs">
          {Object.keys(INDUSTRIES).map((k) =>
          <button key={k} className={'dz-tab' + (k === industry ? ' is-on' : '')} onClick={() => switchIndustry(k)}>
              {INDUSTRIES[k].tag.split(' · ')[0]}
            </button>
          )}
        </div>
        <button className={'dz-heat' + (heat ? ' is-on' : '')} onClick={() => setHeat((h) => !h)}>
          <span className="dd" style={{ width: 6, height: 6, background: heat ? 'var(--signal-warm)' : 'var(--text-dim)' }} />
          {heat ? 'Hide heatmap' : 'Show heatmap'}
        </button>
      </div>

      <div className="dz-stage">
        {/* LEFT — the page */}
        <div className="dz-browser">
          <div className="dz-chrome">
            <span className="dz-light" /><span className="dz-light" /><span className="dz-light" />
            <span className="dz-url">{data.url}</span>
          </div>
          <div className="dz-page">
            <div className="dz-media">
              {data.media === 'cleat' &&
              <div className="cleat-gallery">
                <img src="assets/img/cleats/cleat-1.jpeg" alt="Nike Mercurial soccer cleat — side" />
                <img src="assets/img/cleats/cleat-2.jpeg" alt="Nike Mercurial soccer cleat — pair" />
                <img src="assets/img/cleats/cleat-3.jpeg" alt="Nike Mercurial soccer cleat — profile" />
                <img src="assets/img/cleats/cleat-4.jpeg" alt="Nike Mercurial soccer cleat — top" />
                <img src="assets/img/cleats/cleat-5.jpeg" alt="Nike Mercurial soccer cleat — heel" />
                <img src="assets/img/cleats/cleat-6.jpeg" alt="Nike Mercurial soccer cleat — sole" />
              </div>
              }
              {data.media === 'art' && data.art &&
              <div className="dz-art" dangerouslySetInnerHTML={{ __html: data.art }} />
              }
              {heat &&
              <div className="dz-heatmap">
                  <span className="dz-blob" style={{ top: '24%', left: '40%', background: 'rgba(255,107,61,0.9)' }} />
                  <span className="dz-blob" style={{ top: '55%', left: '26%', width: 120, height: 120, background: 'rgba(255,180,61,0.7)' }} />
                  <span className="dz-blob" style={{ top: '30%', left: '60%', width: 110, height: 110, background: 'rgba(255,69,102,0.7)' }} />
                </div>
              }
            </div>
            <div className="dz-head">
              <span className="dz-title">{data.title}</span>
              <span className="dz-meta">{data.meta}</span>
            </div>
            <div className="dz-copy">
              <div className={'dz-old' + (rewritten ? ' is-struck' : '')}>{data.oldCopy}</div>
              {rewritten &&
              <div className="dz-new-wrap">
                  <div className="dz-new-label"><Dot /> Engine rewrite · auto-rewritten just now</div>
                  <div className="dz-new">
                    {data.newCopy.slice(0, phase === 'rewrite' ? typed : data.newCopy.length)}
                    {phase === 'rewrite' && typed < data.newCopy.length && <span className="dz-caret">▍</span>}
                  </div>
                </div>
              }
            </div>
            {accepted &&
            <div className={'dz-verdict ' + accepted}>
                {accepted === 'accepted' ?
              '✓ Variant kept · promoted to 100% of traffic' :
              '↺ Rolled back · original restored · logged'}
              </div>
            }
          </div>
        </div>

        {/* RIGHT — the control room */}
        <div className="dz-control">
          {/* run / status */}
          <div className="dz-run">
            <button className="dz-run__btn" onClick={running ? undefined : run} disabled={running} data-running={running}>
              {running ? 'Engine running…' : phase === 'done' ? 'Run again' : 'Run the engine ▸'}
            </button>
            <div className="dz-phase">
              <span>{PHASE_LABEL[phase]}</span>
              <span className="dz-phase__pct">{Math.round(progressPct)}%</span>
            </div>
            <div className="dz-progress"><span style={{ width: progressPct + '%' }} /></div>
          </div>

          {/* signals OR results */}
          {!showResults ?
          <div className="dz-card">
              <div className="dz-card__h">Live signals <span className="dz-src">GA4 · Hotjar</span></div>
              {data.signals.map((s, i) => <Signal key={i} s={s} armed={phase !== 'idle'} />)}
              {phase === 'diagnose' && <div className="dz-diag"><Dot tone="warm" /> {data.diagnosis}</div>}
            </div> :

          <div className="dz-card dz-card--accent">
              <div className="dz-result">
                <span className="dz-result__num">+{liftNum}%</span>
                <span className="dz-result__lbl">{data.metric}<br />48h since rewrite</span>
              </div>
              <Sparkline progress={spark} lift={data.lift} />
            </div>
          }

          {/* console log */}
          <div className="dz-log">
            {log.length === 0 && <div className="dz-log__empty">Engine idle. Press <b>Run the engine</b> to watch a self-heal, end to end.</div>}
            {log.map((l, i) =>
            <div key={l.t + '-' + i} className={'dz-log__line dz-log__line--' + l.tone} style={{ opacity: i < log.length - 5 ? 0.35 : 1 }}>
                <span className="dz-log__ic">{l.icon}</span>
                <span className="dz-log__tx">{l.text}</span>
              </div>
            )}
          </div>

          {/* accept / rollback — only when done */}
          {phase === 'done' && !accepted &&
          <div className="dz-actions">
              <button className="dz-accept" onClick={() => {setAccepted('accepted');pushLog('✓', 'green', 'Operator accepted · promoted to 100%');}}>Keep it →</button>
              <button className="dz-rollback" onClick={() => {setAccepted('rolledback');pushLog('↺', 'warm', 'Operator rolled back · original restored');}}>Roll back</button>
            </div>
          }
          <p className="dz-foot">Outcomes only. The engine\u2019s operators, prompts, and orchestration stay under NDA — a black box on purpose.</p>
        </div>
      </div>
    </div>);

}

ReactDOM.createRoot(document.getElementById('demo-app')).render(<EngineDemo />);