import { useState, useEffect } from 'react' const ACTION_META = { fetch_user_history: { tag: 'INV', color: '#60a5fa', border: 'rgba(96,165,250,0.3)', bg: 'rgba(59,130,246,0.06)' }, fetch_thread_context: { tag: 'INV', color: '#60a5fa', border: 'rgba(96,165,250,0.3)', bg: 'rgba(59,130,246,0.06)' }, check_policy_clause: { tag: 'INV', color: '#60a5fa', border: 'rgba(96,165,250,0.3)', bg: 'rgba(59,130,246,0.06)' }, mark_violation_type: { tag: 'CLS', color: '#fbbf24', border: 'rgba(251,191,36,0.3)', bg: 'rgba(251,191,36,0.06)' }, allow: { tag: 'END', color: '#4ade80', border: 'rgba(74,222,128,0.4)', bg: 'rgba(74,222,128,0.07)' }, flag: { tag: 'END', color: '#fb923c', border: 'rgba(251,146,60,0.4)', bg: 'rgba(251,146,60,0.07)' }, remove: { tag: 'END', color: '#f87171', border: 'rgba(248,113,113,0.4)', bg: 'rgba(248,113,113,0.07)' }, escalate: { tag: 'END', color: '#c084fc', border: 'rgba(192,132,252,0.4)', bg: 'rgba(192,132,252,0.07)' }, } const INV_LABELS = { fetch_user_history: 'Fetched user violation history', fetch_thread_context: 'Retrieved conversation thread context', check_policy_clause: 'Loaded applicable policy clause', mark_violation_type: 'Classified violation type', allow: 'Decision: Allow content', flag: 'Decision: Flag for review', remove: 'Decision: Remove content', escalate: 'Decision: Escalate to senior moderator', } function RewardSign({ value }) { const isPos = value >= 0 return ( {isPos ? '+' : ''}{value.toFixed(2)} ) } export default function StepTimeline({ trajectory, animKey }) { const [visibleCount, setVisibleCount] = useState(0) // Reveal steps one by one whenever trajectory/animKey changes useEffect(() => { setVisibleCount(0) if (!trajectory?.length) return let count = 0 const id = setInterval(() => { count += 1 setVisibleCount(count) if (count >= trajectory.length) clearInterval(id) }, 380) return () => clearInterval(id) }, [animKey, trajectory]) if (!trajectory?.length) return null const visible = trajectory.slice(0, visibleCount) return (