// Shared Playground component — drives every "try the API" surface on the site.
// Reads from ENDPOINTS / GROUPS / playgroundEnabledIds() / priceFor / docsHrefFor.
// Replaces the hardcoded V1Playground / V2Console hero panels.

const PG_DARK = {
  bg:         '#0e1115',
  bgDeep:     '#0a0c0f',
  panel:      '#11151a',
  panelAlt:   '#0d1014',
  line:       '#1f2228',
  line2:      '#171a1f',
  ink:        '#e6e9ed',
  ink2:       '#cdd1d6',
  ink3:       '#8b929b',
  ink4:       '#5b6573',
};

// ───────────────────────────────────────────────────────────── primitives ──

const PgInput = ({ ctl, value, onChange }) => {
  const inputBase = {
    width: '100%', boxSizing: 'border-box',
    background: PG_DARK.bgDeep, border: `1px solid ${PG_DARK.line}`, borderRadius: 7,
    padding: '8px 10px', minHeight: 34,
    fontFamily: wf.mono, fontSize: 12.5, color: PG_DARK.ink,
    outline: 'none',
  };
  if (ctl.type === 'toggle') {
    return (
      <div onClick={() => onChange(!value)} style={{
        display: 'inline-flex', alignItems: 'center', gap: 8,
        padding: '6px 10px', borderRadius: 7, cursor: 'pointer',
        border: `1px solid ${PG_DARK.line}`, background: PG_DARK.bgDeep,
        fontFamily: wf.mono, fontSize: 12, color: PG_DARK.ink2,
      }}>
        <span style={{
          width: 24, height: 14, borderRadius: 999, position: 'relative',
          background: value ? 'var(--accent)' : '#2a2f37',
          transition: 'background .12s',
        }}>
          <span style={{
            position: 'absolute', top: 1, left: value ? 11 : 1,
            width: 12, height: 12, borderRadius: 999, background: '#fff',
            transition: 'left .12s',
          }} />
        </span>
        <span>{value ? 'true' : 'false'}</span>
      </div>
    );
  }
  if (ctl.type === 'select') {
    return (
      <select value={value} onChange={e => onChange(e.target.value)} style={{
        ...inputBase, appearance: 'none',
        backgroundImage: `linear-gradient(45deg, transparent 50%, ${PG_DARK.ink3} 50%), linear-gradient(135deg, ${PG_DARK.ink3} 50%, transparent 50%)`,
        backgroundPosition: `calc(100% - 14px) 14px, calc(100% - 9px) 14px`,
        backgroundSize: '5px 5px, 5px 5px',
        backgroundRepeat: 'no-repeat',
        paddingRight: 28,
      }}>
        {ctl.options.map(o => <option key={o} value={o} style={{ background: PG_DARK.bgDeep }}>{o}</option>)}
      </select>
    );
  }
  if (ctl.type === 'json') {
    const text = typeof value === 'string' ? value : JSON.stringify(value);
    return (
      <input type="text" value={text} onChange={e => {
        try { onChange(JSON.parse(e.target.value)); }
        catch { onChange(e.target.value); }
      }} style={inputBase} spellCheck={false} />
    );
  }
  // url / text / number
  return (
    <input
      type={ctl.type === 'number' ? 'number' : 'text'}
      value={value ?? ''}
      min={ctl.min} max={ctl.max} step={ctl.step}
      onChange={e => onChange(ctl.type === 'number' ? Number(e.target.value) : e.target.value)}
      style={inputBase}
      spellCheck={false}
    />
  );
};

// JSON syntax-highlighter (very small)
const JsonBlock = ({ value, maxHeight = 280 }) => {
  const text = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
  return (
    <pre style={{
      margin: 0, padding: '12px 14px',
      fontFamily: wf.mono, fontSize: 12.5, lineHeight: 1.65,
      color: PG_DARK.ink2,
      background: PG_DARK.bgDeep,
      maxHeight, overflow: 'auto',
      whiteSpace: 'pre',
    }}>
      {text.split('\n').map((ln, i) => {
        // string highlighter
        const parts = []; let key = 0; let last = 0;
        const re = /"([^"]*)"(\s*:)?/g; let m;
        while ((m = re.exec(ln)) !== null) {
          if (m.index > last) parts.push(<span key={key++}>{ln.slice(last, m.index)}</span>);
          const isKey = !!m[2];
          parts.push(<span key={key++} style={{ color: isKey ? '#9bb0d4' : '#9bd17b' }}>"{m[1]}"</span>);
          if (isKey) parts.push(<span key={key++}>:</span>);
          last = m.index + m[0].length;
        }
        if (last < ln.length) {
          // number highlight in trailing chunk
          const tail = ln.slice(last);
          const nm = tail.split(/(-?\d+\.?\d*)/);
          parts.push(...nm.map((seg, i) =>
            /^-?\d+\.?\d*$/.test(seg)
              ? <span key={key++} style={{ color:'#e6c07b' }}>{seg}</span>
              : <span key={key++}>{seg}</span>
          ));
        }
        return <div key={i} style={{ minHeight: 18 }}>{parts.length ? parts : '\u00a0'}</div>;
      })}
    </pre>
  );
};

// ──────────────────────────────────────────────────────── response previews ──

const FrameTile = ({ t, score, label }) => (
  <div style={{ background: PG_DARK.panel, border: `1px solid ${PG_DARK.line}`, borderRadius: 8, overflow: 'hidden' }}>
    <div style={{
      aspectRatio: '16/9',
      background: `repeating-linear-gradient(45deg, #1a1f25 0 6px, #161a20 6px 12px)`,
      position: 'relative',
    }}>
      <span style={{
        position: 'absolute', top: 6, left: 6,
        fontFamily: wf.mono, fontSize: 10, padding: '1px 5px', borderRadius: 3,
        background: 'rgba(0,0,0,.55)', color: PG_DARK.ink,
      }}>t={t.toFixed(1)}s</span>
      {label && <span style={{
        position:'absolute', bottom: 6, left: 6, right: 6,
        fontFamily: wf.mono, fontSize: 10, color: PG_DARK.ink2,
        overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
      }}>{label}</span>}
    </div>
    {typeof score === 'number' && (
      <div style={{ padding: '6px 8px', display: 'flex', justifyContent: 'space-between', fontFamily: wf.mono, fontSize: 10.5, color: PG_DARK.ink3 }}>
        <span>score</span><span style={{ color: 'var(--accent)' }}>{score.toFixed(2)}</span>
      </div>
    )}
  </div>
);

const PreviewFrames = ({ ep, response }) => {
  const frames = response.frames || (response.frame ? [response.frame] : []);
  return (
    <div>
      <div style={{ fontFamily: wf.mono, fontSize: 10.5, letterSpacing: 1, color: PG_DARK.ink3, textTransform: 'uppercase', marginBottom: 8 }}>
        {frames.length} frame{frames.length === 1 ? '' : 's'}
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 1fr))', gap: 8 }}>
        {frames.slice(0, 6).map((f, i) => (
          <FrameTile key={i} t={f.t ?? 0} score={f.score ?? f.thumbnail_score} label={f.text || f.kind} />
        ))}
      </div>
    </div>
  );
};

const PreviewTimelineSegments = ({ ep, response }) => {
  const segs = response.segments || response.chunks || response.ranges || response.timeline || [];
  const cuts = response.cuts || [];
  const totalEnd = Math.max(
    ...segs.map(s => s.end ?? (s.t ? s.t + (s.dur || 0) : 0)),
    ...cuts,
    response.media_seconds || 100,
  );
  const norm = (x) => `${(x / totalEnd) * 100}%`;
  const palette = ['var(--accent)', '#9bd17b', '#e6c07b', '#c678dd', '#56b6c2'];
  return (
    <div>
      <div style={{ fontFamily: wf.mono, fontSize: 10.5, letterSpacing: 1, color: PG_DARK.ink3, textTransform: 'uppercase', marginBottom: 8 }}>
        timeline · {segs.length || cuts.length} segment{(segs.length || cuts.length) === 1 ? '' : 's'}
      </div>
      <div style={{ position: 'relative', height: 56, borderRadius: 8, background: PG_DARK.bgDeep, border: `1px solid ${PG_DARK.line}`, overflow: 'hidden' }}>
        {segs.map((s, i) => {
          const start = s.start ?? s.t ?? 0;
          const end = s.end ?? (s.t ? s.t + (s.dur || 1) : start + 1);
          const c = palette[i % palette.length];
          return (
            <div key={i} style={{
              position: 'absolute', top: 8, bottom: 8,
              left: norm(start), width: norm(end - start),
              background: `color-mix(in oklch, ${c} 40%, transparent)`,
              borderLeft: `2px solid ${c}`, borderRight: `2px solid ${c}`,
              borderRadius: 3,
            }} title={`${start.toFixed(1)}–${end.toFixed(1)}s`} />
          );
        })}
        {cuts.map((t, i) => (
          <span key={`c${i}`} style={{
            position: 'absolute', top: 0, bottom: 0, left: norm(t),
            width: 2, background: 'var(--accent)',
          }} />
        ))}
      </div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 10 }}>
        {segs.slice(0, 5).map((s, i) => {
          const start = s.start ?? s.t ?? 0;
          const end = s.end ?? (s.t ? s.t + (s.dur || 1) : start + 1);
          return (
            <span key={i} style={{
              fontFamily: wf.mono, fontSize: 10.5, color: PG_DARK.ink2,
              padding: '3px 7px', borderRadius: 999,
              background: PG_DARK.panel, border: `1px solid ${PG_DARK.line}`,
            }}>
              {start.toFixed(1)}–{end.toFixed(1)}s {s.topic || s.reason || s.kind ? `· ${s.topic || s.reason || s.kind}` : ''}
            </span>
          );
        })}
      </div>
    </div>
  );
};

const PreviewClip = ({ ep, response }) => (
  <div>
    <div style={{ fontFamily: wf.mono, fontSize: 10.5, letterSpacing: 1, color: PG_DARK.ink3, textTransform: 'uppercase', marginBottom: 8 }}>
      clip rendered
    </div>
    <div style={{
      borderRadius: 10, border: `1px solid ${PG_DARK.line}`, background: PG_DARK.panel, overflow: 'hidden',
    }}>
      <div style={{
        aspectRatio: '16/9',
        background: `linear-gradient(135deg, #1a1f25 0%, #0f1318 100%),
                     repeating-linear-gradient(0deg, rgba(255,255,255,.02) 0 1px, transparent 1px 4px)`,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        position: 'relative',
      }}>
        <span style={{
          width: 56, height: 56, borderRadius: 999,
          background: 'rgba(255,255,255,.08)', border: `1px solid ${PG_DARK.line}`,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          color: 'var(--accent)', fontSize: 22, paddingLeft: 4,
        }}>▶</span>
        <span style={{
          position: 'absolute', bottom: 10, left: 12, right: 12, height: 4, borderRadius: 2,
          background: 'rgba(255,255,255,.08)',
        }}>
          <span style={{ display: 'block', height: '100%', width: '32%', borderRadius: 2, background: 'var(--accent)' }} />
        </span>
      </div>
      <div style={{ padding: '10px 12px', display: 'flex', justifyContent: 'space-between', fontFamily: wf.mono, fontSize: 11.5, color: PG_DARK.ink2 }}>
        <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '70%' }}>
          {response.url || 'https://cdn.momentiq.dev/clips/abc123.mp4'}
        </span>
        <span>
          {typeof response.duration === 'number' ? `${response.duration}s` :
           (response.start != null && response.end != null ? `${(response.end - response.start).toFixed(1)}s` : '32.5s')}
        </span>
      </div>
    </div>
  </div>
);

const PreviewSignal = ({ ep, response }) => {
  // collect rows from any of: results, silences, peaks, music, mixed, cuts, speakers, overlaps, event
  const rows = []; const colorFor = (k) => ({
    laughter:'#9bd17b', silence:'#8b929b', music:'#c678dd', mixed:'#e6c07b',
    energy_peak:'var(--accent)', speaker:'var(--accent)', overlap:'#e08e6e',
    cut:'var(--accent)', signal:'var(--accent)',
  })[k] || 'var(--accent)';

  if (response.results) response.results.forEach(r => rows.push({ kind: ep.title.replace('detect-',''), t: r.t, dur: r.dur, conf: r.conf }));
  if (response.silences) response.silences.forEach(r => rows.push({ kind:'silence', t: r.start, dur: r.end - r.start }));
  if (response.peaks) response.peaks.forEach(r => rows.push({ kind:'energy_peak', t: r.t, conf: r.score }));
  if (response.music) response.music.forEach(r => rows.push({ kind:'music', t: r.start, dur: r.end - r.start, conf: r.confidence }));
  if (response.cuts && Array.isArray(response.cuts)) response.cuts.forEach(c => rows.push({ kind:'cut', t: c.t ?? c, conf: c.quality, reason: c.reason }));
  if (response.speakers) response.speakers.forEach(s => rows.push({ kind:`speaker ${s.id}`, t: 0, dur: 0, label: `${(s.share*100).toFixed(0)}% · ${s.segments} segments` }));
  if (response.overlaps) response.overlaps.forEach(o => rows.push({ kind:'overlap', t: o.t, dur: o.dur, label: (o.ids||[]).join('+') }));
  if (response.event) rows.push({ kind: response.event.kind, t: response.event.t, dur: response.event.dur, label: `Δ ${response.delta?.toFixed(2)}s` });

  if (rows.length === 0 && response.timeline) response.timeline.forEach(e => rows.push({ kind: e.kind, t: e.t, dur: e.dur }));

  return (
    <div>
      <div style={{ fontFamily: wf.mono, fontSize: 10.5, letterSpacing: 1, color: PG_DARK.ink3, textTransform: 'uppercase', marginBottom: 8 }}>
        {rows.length} event{rows.length === 1 ? '' : 's'}
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
        {rows.slice(0, 6).map((r, i) => (
          <div key={i} style={{
            display: 'grid', gridTemplateColumns: '110px 90px 70px 1fr',
            alignItems: 'center', gap: 10,
            padding: '8px 10px', borderRadius: 8,
            background: PG_DARK.panel, border: `1px solid ${PG_DARK.line}`,
            fontFamily: wf.mono, fontSize: 12, color: PG_DARK.ink2,
          }}>
            <span style={{ display:'inline-flex', alignItems:'center', gap:6, color: PG_DARK.ink }}>
              <span style={{ width:6, height:6, borderRadius:999, background: colorFor(r.kind) }} />
              {r.kind}
            </span>
            <span style={{ color: PG_DARK.ink3 }}>t={r.t?.toFixed(2)}s</span>
            <span style={{ color: PG_DARK.ink3 }}>{r.dur ? `${r.dur.toFixed(2)}s` : '—'}</span>
            <span style={{ color: 'var(--accent)' }}>
              {r.conf != null ? `conf ${r.conf.toFixed(2)}` : (r.label || r.reason || '')}
            </span>
          </div>
        ))}
      </div>
    </div>
  );
};

const PREVIEWS = {
  'frames':             PreviewFrames,
  'timeline-segments':  PreviewTimelineSegments,
  'clip':               PreviewClip,
  'signal':             PreviewSignal,
};

// ───────────────────────────────────────────────────────── grouped selector ──

const GroupedSelect = ({ value, onChange }) => {
  return (
    <select value={value} onChange={e => onChange(e.target.value)} style={{
      width: '100%', boxSizing: 'border-box',
      background: PG_DARK.bgDeep, border: `1px solid ${PG_DARK.line}`, borderRadius: 7,
      padding: '8px 10px', minHeight: 36,
      fontFamily: wf.mono, fontSize: 13, color: PG_DARK.ink,
      appearance: 'none',
      backgroundImage: `linear-gradient(45deg, transparent 50%, ${PG_DARK.ink3} 50%), linear-gradient(135deg, ${PG_DARK.ink3} 50%, transparent 50%)`,
      backgroundPosition: `calc(100% - 14px) 16px, calc(100% - 9px) 16px`,
      backgroundSize: '5px 5px, 5px 5px',
      backgroundRepeat: 'no-repeat',
      paddingRight: 28,
    }}>
      {Object.values(GROUPS).map(g => {
        const ids = endpointIdsByGroup(g.id).filter(id => ENDPOINTS[id].playgroundEnabled !== false);
        if (ids.length === 0) return null;
        return (
          <optgroup key={g.id} label={g.name + '  (' + g.pathPrefix + ')'} style={{ background: PG_DARK.bgDeep }}>
            {ids.map(id => (
              <option key={id} value={id} style={{ background: PG_DARK.bgDeep }}>
                {ENDPOINTS[id].path}
              </option>
            ))}
          </optgroup>
        );
      })}
    </select>
  );
};

// ───────────────────────────────────────────────────────────────── main ────

// ── analytics helpers ─────────────────────────────────────────────────────
// Safe metadata only — never includes the media_url or full request body.
const pgEpMeta = (id) => {
  const ep = ENDPOINTS[id] || {};
  return {
    endpoint_id:        id,
    endpoint_path:      ep.path,
    endpoint_group:     ep.group,
    pricing_unit:       (typeof pricingUnit === 'function') ? pricingUnit(id) : undefined,
    fake_response_type: ep.fakeResponseType,
  };
};
const safeFileExt = (s) => {
  if (typeof s !== 'string') return undefined;
  const m = s.match(/\.([a-z0-9]{2,5})(?:[?#]|$)/i);
  return m ? m[1].toLowerCase() : undefined;
};
const pgTrack = (name, props) => {
  if (window.MIQAnalytics) window.MIQAnalytics.track(name, props || {});
};

const Playground = ({
  initialId = 'audio/detect-speakers',
  defaultMinutes = 18,
  height = 'auto',
}) => {
  const [id, setId]               = React.useState(initialId);
  const ep                         = ENDPOINTS[id];
  const [params, setParams]       = React.useState(() => ({ ...(ep.defaultRequest || ep.requestExample) }));
  const [minutes, setMinutes]     = React.useState(defaultMinutes);
  const [copied, setCopied]       = React.useState('');
  const [running, setRunning]     = React.useState(false);
  const [liveResponse, setLiveResponse] = React.useState(null);
  const [liveError, setLiveError]       = React.useState(null);

  // playground_viewed — once per mount
  React.useEffect(() => {
    pgTrack('playground_viewed', pgEpMeta(id));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  React.useEffect(() => {
    setParams({ ...(ep.defaultRequest || ep.requestExample) });
    setLiveResponse(null);
    setLiveError(null);
  }, [id]);

  // Wrapped setter so every endpoint switch becomes a tracked event.
  const selectEndpoint = (next) => {
    if (next === id) return;
    pgTrack('playground_endpoint_selected', { ...pgEpMeta(next), from_endpoint_id: id });
    setId(next);
  };

  const setParam = (k, v) => {
    setParams(p => ({ ...p, [k]: v }));
    const ctl = (ep.playgroundControls || []).find(c => c.key === k);
    if (!ctl) return;
    const base = pgEpMeta(id);
    if (ctl.type === 'url' && k.toLowerCase().indexOf('url') >= 0) {
      // Never the URL itself — only the fact + safe extension.
      pgTrack('playground_media_url_entered', {
        ...base,
        media_url_entered: !!v,
        media_source_type: 'url',
        file_extension:    safeFileExt(v),
      });
    } else if (k === 'start' || k === 'end') {
      const start = k === 'start' ? v : params.start;
      const end   = k === 'end'   ? v : params.end;
      pgTrack('playground_time_window_changed', {
        ...base, start, end,
        window_seconds: (Number.isFinite(start) && Number.isFinite(end)) ? Math.max(0, end - start) : undefined,
      });
    } else if (k === 'quality') {
      pgTrack('playground_param_changed', { ...base, param: 'quality', quality: v });
    }
  };

  const requestBody = React.useMemo(() => {
    const out = {};
    (ep.playgroundControls || []).forEach(c => { out[c.key] = params[c.key] ?? c.default; });
    return out;
  }, [ep, params]);

  const cost = priceFor(id) * minutes;
  const isFixedPrice = pricingUnit(id) === 'request';
  const Preview = PREVIEWS[ep.fakeResponseType] || PreviewSignal;
  const liveEnabled = id === 'video/clip-window' && window.MomentIQ && typeof window.MomentIQ.clipWindow === 'function';
  const previewResponse = liveResponse?.clip || liveResponse || ep.responseExample;

  // playground_price_estimate_viewed — fire when the estimated cost actually
  // changes (debounced through React state, not on every keystroke).
  React.useEffect(() => {
    pgTrack('playground_price_estimate_viewed', {
      ...pgEpMeta(id),
      estimated_price_usd:  +cost.toFixed(4),
      media_length_seconds: minutes * 60,
      is_fixed_price:       isFixedPrice,
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [id, minutes]);

  const copy = (label, text) => {
    try { navigator.clipboard?.writeText(text); } catch (e) {}
    setCopied(label); setTimeout(() => setCopied(''), 1400);
  };

  // Fake demo runner — toggles a brief loading flag and "completes" with
  // the canned ep.responseExample. Funnel events fire even before there's
  // a real backend, so the visitor→demo funnel works today.
  const runDemo = async () => {
    const base = pgEpMeta(id);
    pgTrack('playground_demo_run_clicked', {
      ...base,
      media_length_seconds: minutes * 60,
      estimated_price_usd:  +cost.toFixed(4),
      start: params.start, end: params.end,
      quality: params.quality,
    });
    setRunning(true);
    if (liveEnabled) {
      setLiveResponse(null);
      setLiveError(null);
      try {
        const response = await window.MomentIQ.clipWindow({
          ...requestBody,
          mode: requestBody.mode || 'transcode',
        });
        setLiveResponse(response);
        pgTrack('playground_demo_completed', {
          ...base,
          media_length_seconds: minutes * 60,
          estimated_price_usd: response?.job?.estimated_price_usd ?? +cost.toFixed(4),
          success: true,
        });
      } catch (err) {
        const payload = err?.payload || { error: { code: 'client_error', message: err?.message || String(err) } };
        setLiveError(payload);
        pgTrack('playground_demo_completed', {
          ...base,
          media_length_seconds: minutes * 60,
          estimated_price_usd: +cost.toFixed(4),
          success: false,
          error_code: payload?.error?.code,
        });
      } finally {
        setRunning(false);
      }
      return;
    }
    setTimeout(() => {
      setRunning(false);
      pgTrack('playground_demo_completed', {
        ...base,
        media_length_seconds: minutes * 60,
        estimated_price_usd:  +cost.toFixed(4),
        success: true,
      });
    }, 650);
  };

  return (
    <div style={{
      background: PG_DARK.bg, border: `1px solid ${PG_DARK.line}`, borderRadius: 14,
      overflow: 'hidden', color: PG_DARK.ink,
      boxShadow: '0 30px 60px -22px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.02)',
    }}>
      {/* chrome */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 10,
        padding: '10px 14px',
        borderBottom: `1px solid ${PG_DARK.line}`, background: PG_DARK.panelAlt,
      }}>
        <span style={{ width: 10, height: 10, borderRadius: 999, background: '#3a3f47' }} />
        <span style={{ width: 10, height: 10, borderRadius: 999, background: '#3a3f47' }} />
        <span style={{ width: 10, height: 10, borderRadius: 999, background: '#3a3f47' }} />
        <span style={{ fontFamily: wf.mono, fontSize: 11.5, color: PG_DARK.ink3, marginLeft: 8 }}>
          MomentIQ Playground
        </span>
        <span style={{ flex: 1 }} />
        <span style={{ fontFamily: wf.mono, fontSize: 11, color: PG_DARK.ink3 }}>
          Authorization: Bearer miq_live_••••
        </span>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)' }}>
        {/* LEFT: selector + controls + request JSON */}
        <div style={{ padding: 18, borderRight: `1px solid ${PG_DARK.line}`, minWidth: 0 }}>
          <div style={{ fontFamily: wf.mono, fontSize: 10.5, letterSpacing: 1.2, color: PG_DARK.ink3, textTransform: 'uppercase', marginBottom: 8 }}>Endpoint</div>
          <GroupedSelect value={id} onChange={selectEndpoint} />

          <div style={{
            marginTop: 14,
            display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap',
          }}>
            <Method kind={ep.method} dark />
            <span style={{ fontFamily: wf.mono, fontSize: 13, color: PG_DARK.ink }}>{ep.path}</span>
            <span style={{
              fontFamily: wf.mono, fontSize: 10.5, padding: '1px 6px', borderRadius: 4,
              background: 'var(--accent-soft)', color: 'var(--accent)',
              textTransform: 'uppercase', letterSpacing: 0.5,
            }}>{ep.group}</span>
          </div>
          <div style={{ fontFamily: wf.sans, fontSize: 13, color: PG_DARK.ink2, lineHeight: 1.55, marginTop: 10 }}>
            {ep.playgroundDescription || ep.short}
          </div>

          {/* Controls */}
          <div style={{ marginTop: 18, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
            {(ep.playgroundControls || []).map(ctl => {
              const span = (ctl.type === 'url' || ctl.type === 'json' || ctl.type === 'text') ? '1 / -1' : 'auto';
              return (
                <div key={ctl.key} style={{ gridColumn: span, minWidth: 0 }}>
                  <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 5 }}>
                    <span style={{ fontFamily: wf.mono, fontSize: 11.5, color: PG_DARK.ink2 }}>{ctl.label}</span>
                    {ctl.hint && <span style={{ fontFamily: wf.mono, fontSize: 10.5, color: PG_DARK.ink4 }}>{ctl.hint}</span>}
                  </div>
                  <PgInput ctl={ctl} value={params[ctl.key] ?? ctl.default} onChange={v => setParam(ctl.key, v)} />
                </div>
              );
            })}
          </div>

          {/* Request JSON */}
          <div style={{ marginTop: 18 }}>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
              <span style={{ fontFamily: wf.mono, fontSize: 10.5, letterSpacing: 1.2, color: PG_DARK.ink3, textTransform: 'uppercase' }}>Request body</span>
              <span style={{ fontFamily: wf.mono, fontSize: 11, color: 'var(--accent)' }}>
                POST {ep.path}
              </span>
            </div>
            <div style={{ borderRadius: 10, border: `1px solid ${PG_DARK.line}`, overflow: 'hidden' }}>
              <JsonBlock value={requestBody} maxHeight={220} />
            </div>
          </div>
        </div>

        {/* RIGHT: response + cost + actions */}
        <div style={{ padding: 18, background: PG_DARK.panelAlt, minWidth: 0 }}>
          {/* Cost row */}
          <div style={{
            display: 'flex', alignItems: 'center', gap: 12,
            padding: '12px 14px',
            borderRadius: 10, background: PG_DARK.panel, border: `1px solid ${PG_DARK.line}`,
            marginBottom: 14,
          }}>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontFamily: wf.mono, fontSize: 10.5, letterSpacing: 1.2, color: PG_DARK.ink3, textTransform: 'uppercase' }}>Estimated cost</div>
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginTop: 4 }}>
                <span style={{ fontFamily: wf.sans, fontSize: 22, fontWeight: 600, color: PG_DARK.ink }}>
                  {isFixedPrice ? priceLabel(id) : `$${cost.toFixed(2)}`}
                </span>
                {!isFixedPrice && (
                  <span style={{ fontFamily: wf.mono, fontSize: 11, color: PG_DARK.ink3 }}>
                    = {priceLabel(id)} × {minutes}m
                  </span>
                )}
              </div>
            </div>
            {!isFixedPrice && (
              <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                <span style={{ fontFamily: wf.mono, fontSize: 10.5, color: PG_DARK.ink3 }}>media</span>
                <input type="range" min={1} max={120} value={minutes}
                  onChange={e => setMinutes(Number(e.target.value))}
                  style={{ width: 96, accentColor: 'var(--accent)' }} />
                <span style={{ fontFamily: wf.mono, fontSize: 11, color: PG_DARK.ink2, minWidth: 36, textAlign: 'right' }}>{minutes}m</span>
              </div>
            )}
          </div>

          {/* Response preview */}
          <div style={{
            borderRadius: 10, background: PG_DARK.bgDeep, border: `1px solid ${PG_DARK.line}`,
            padding: 14, marginBottom: 14,
          }}>
            <Preview ep={ep} response={previewResponse} />
          </div>
          {liveError && (
            <div style={{
              borderRadius: 10, background: '#2a1518', border: '1px solid #5f242c',
              padding: 12, marginBottom: 14,
              fontFamily: wf.mono, fontSize: 11.5, color: '#ffd5d9',
            }}>
              {liveError?.error?.code || 'error'}: {liveError?.error?.message || 'MomentIQ request failed'}
            </div>
          )}

          {/* Actions */}
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
            <button onClick={runDemo} disabled={running} style={pgBtn(true)}>
              {running ? '…  Running' : '▶  Run example'}
            </button>
            <button onClick={() => {
              copy('json', JSON.stringify(requestBody, null, 2));
              pgTrack('playground_copy_request_json_clicked', pgEpMeta(id));
            }} style={pgBtn(false)}>
              {copied === 'json' ? '✓  Copied' : 'Copy request JSON'}
            </button>
            <a href={docsHrefFor(id)}
              onClick={() => pgTrack('playground_view_docs_clicked', pgEpMeta(id))}
              style={{ ...pgBtn(false), textDecoration: 'none', display: 'inline-flex' }}>
              View docs →
            </a>
          </div>

          {/* Response JSON (collapsed peek) */}
          <details style={{ marginTop: 14 }}>
            <summary style={{ fontFamily: wf.mono, fontSize: 11, color: PG_DARK.ink3, cursor: 'pointer', userSelect: 'none' }}>
              Show raw response JSON
            </summary>
            <div style={{ marginTop: 8, borderRadius: 10, border: `1px solid ${PG_DARK.line}`, overflow: 'hidden' }}>
              <JsonBlock value={liveError || liveResponse || ep.responseExample} maxHeight={240} />
            </div>
          </details>
        </div>
      </div>
    </div>
  );
};

const pgBtn = (primary) => ({
  fontFamily: wf.sans, fontSize: 13, fontWeight: 500,
  padding: '8px 14px', borderRadius: 8,
  border: `1px solid ${primary ? 'var(--accent)' : PG_DARK.line}`,
  background: primary ? 'var(--accent)' : PG_DARK.panel,
  color: primary ? '#fff' : PG_DARK.ink,
  cursor: 'pointer',
});

window.Playground = Playground;
window.pgEpMeta   = pgEpMeta;
window.pgTrack    = pgTrack;
