Q_ARTIFACT_VIEWER.jsx
Design Mockups/DOC20 Mockups/Q_ARTIFACT_VIEWER.jsx
import { useState, useRef, useCallback, useEffect } from "react";
const font={sans:"'Söhne','Helvetica Neue',-apple-system,BlinkMacSystemFont,sans-serif"};
const R={sm:"6px",md:"10px",lg:"14px",full:"9999px"};
const c={bgApp:"#F8F8F6",bgPanel:"#FFFFFF",bgPanelAlt:"#F9FAFB",bgCard:"#FFFFFF",bgInput:"#EFF1F3",textPri:"#1A1D21",textSec:"#5E6570",textTer:"#8B919A",accentBtn:"#31588c",warn:"#D97706",error:"#B04040",border:"#E0E2E5",borderLight:"#ECEEF0",green:"#2E8B57",agentAv:"#a1a7aa"};
const Ic=({d,size=18,color,sw=1.75})=><svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color||"currentColor"} strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round"><path d={d}/></svg>;
const I={Plus:p=><Ic {...p} d="M12 5v14 M5 12h14"/>,X:p=><Ic {...p} d="M18 6L6 18 M6 6l12 12"/>,Check:p=><Ic {...p} d="M20 6L9 17l-5-5"/>,Copy:p=><Ic {...p} d="M20 9h-9a2 2 0 00-2 2v9a2 2 0 002 2h9a2 2 0 002-2v-9a2 2 0 00-2-2z M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/>,FileText:p=><Ic {...p} d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z M14 2v6h6 M16 13H8 M16 17H8 M10 9H8"/>,ExtLink:p=><Ic {...p} d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6 M15 3h6v6 M10 14L21 3"/>,Spark:p=><Ic {...p} d="M12 2l1.5 4.5L18 8l-4.5 1.5L12 14l-1.5-4.5L6 8l4.5-1.5L12 2z M12 14l1 3 3 1-3 1-1 3-1-3-3-1 3-1 1-3z"/>,MsgCircle:p=><Ic {...p} d="M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z"/>,Clock:p=><Ic {...p} d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z M12 6v6l4 2"/>,ChevD:p=><Ic {...p} d="M6 9l6 6 6-6"/>,Search:p=><Ic {...p} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>,Pin:p=><Ic {...p} d="M12 17v5 M9 2l.5 5L7 10l1.5 3h7L17 10l-2.5-3L15 2"/>,Maximize:p=><Ic {...p} d="M8 3H5a2 2 0 00-2 2v3m18 0V5a2 2 0 00-2-2h-3m0 18h3a2 2 0 002-2v-3M3 16v3a2 2 0 002 2h3"/>,Printer:p=><Ic {...p} d="M6 9V2h12v7 M6 18H4a2 2 0 01-2-2v-5a2 2 0 012-2h16a2 2 0 012 2v5a2 2 0 01-2 2h-2 M6 14h12v8H6z"/>,Save:p=><Ic {...p} d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z M17 21v-8H7v8 M7 3v5h8"/>,Link:p=><Ic {...p} d="M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71 M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71"/>,Eye:p=><Ic {...p} d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z M12 9a3 3 0 100 6 3 3 0 000-6z"/>,Edit:p=><Ic {...p} d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7 M18.5 2.5a2.12 2.12 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/>,};
const Dot=({color,size=8})=><span style={{display:"inline-flex",width:size,height:size,flexShrink:0}}><span style={{display:"block",width:size,height:size,borderRadius:"50%",backgroundColor:color}}/></span>;
const Btn=({children,primary,ghost,small,onClick,disabled,style:s})=><button onClick={disabled?undefined:onClick} style={{padding:small?"2px 8px":"5px 14px",borderRadius:R.sm,border:primary?"none":ghost?`1.5px solid ${c.border}`:`1px solid ${c.border}`,backgroundColor:primary?(disabled?c.accentBtn+"60":c.accentBtn):"transparent",color:primary?"#fff":c.textSec,fontSize:small?11:12.5,fontWeight:primary?550:450,cursor:disabled?"not-allowed":"pointer",fontFamily:font.sans,display:"flex",alignItems:"center",gap:5,height:small?22:28,opacity:disabled?.5:1,...s}}>{children}</button>;
const Av=({letter,color,size=20})=><div style={{width:size,height:size,borderRadius:R.sm,backgroundColor:color||c.accentBtn,color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontSize:size*.42,fontWeight:700,flexShrink:0}}>{letter}</div>;
const TBtn=({icon,title,active,onClick,label,dropdown})=><button onClick={onClick} title={title} style={{padding:label?"3px 8px":"3px 6px",borderRadius:R.sm,border:`1px solid ${active?c.accentBtn+"40":c.borderLight}`,backgroundColor:active?c.accentBtn+"08":"transparent",cursor:"pointer",display:"flex",alignItems:"center",gap:4,color:active?c.accentBtn:c.textSec,fontFamily:font.sans,fontSize:10.5,fontWeight:active?600:450,height:26}} onMouseEnter={e=>{if(!active)e.currentTarget.style.backgroundColor=c.bgInput}} onMouseLeave={e=>{e.currentTarget.style.backgroundColor=active?c.accentBtn+"08":"transparent"}}>{icon}{label&&<span>{label}</span>}{dropdown&&<I.ChevD size={8}/>}</button>;
const Sep=()=><div style={{width:1,height:20,backgroundColor:c.borderLight,margin:"0 4px",flexShrink:0}}/>;
const Toast=({msg,onDone})=>{useEffect(()=>{const t=setTimeout(onDone,2200);return()=>clearTimeout(t)},[onDone]);return <div style={{position:"fixed",bottom:24,left:"50%",transform:"translateX(-50%)",backgroundColor:c.textPri,color:"#fff",padding:"8px 18px",borderRadius:R.md,fontSize:12,fontWeight:500,fontFamily:font.sans,zIndex:9999,boxShadow:"0 4px 16px rgba(0,0,0,0.2)"}}>{msg}</div>};
const DItem=({children,onClick,active})=><button onClick={onClick} style={{display:"flex",alignItems:"center",gap:6,width:"100%",padding:"6px 10px",border:"none",cursor:"pointer",backgroundColor:active?c.accentBtn+"08":"transparent",fontSize:11.5,color:c.textPri,textAlign:"left",borderRadius:R.sm,fontFamily:font.sans,fontWeight:active?600:400}} onMouseEnter={e=>{if(!active)e.currentTarget.style.backgroundColor=c.bgInput}} onMouseLeave={e=>{if(!active)e.currentTarget.style.backgroundColor=active?c.accentBtn+"08":"transparent"}}>{children}</button>;
const Radio=({selected,label,desc,onClick})=><button onClick={onClick} style={{display:"flex",alignItems:"center",gap:6,width:"100%",padding:"6px 10px",borderRadius:R.sm,border:`1px solid ${selected?c.accentBtn+"50":c.border}`,backgroundColor:selected?c.accentBtn+"06":"transparent",marginBottom:4,cursor:"pointer",textAlign:"left",fontFamily:font.sans}}><span style={{width:12,height:12,borderRadius:"50%",border:`2px solid ${selected?c.accentBtn:c.border}`,backgroundColor:selected?c.accentBtn:"transparent",display:"flex",alignItems:"center",justifyContent:"center",flexShrink:0}}>{selected&&<span style={{width:4,height:4,borderRadius:"50%",backgroundColor:"#fff"}}/>}</span><div style={{flex:1}}><div style={{fontSize:11,fontWeight:selected?600:450,color:selected?c.accentBtn:c.textPri}}>{label}</div>{desc&&<div style={{fontSize:9,color:c.textTer,marginTop:1}}>{desc}</div>}</div></button>;
const acDel="#B04040",acIns="#2E8B57";
const agents=[{name:"Elnor",color:c.agentAv,letter:"E"},{name:"Scout",color:"#5B5F97",letter:"S"},{name:"Reviewer",color:"#7C3AED",letter:"R"}];
const chats=[{name:"Henderson discovery",color:c.accentBtn,origin:true},{name:"Quark patent search",color:c.green},{name:"New chat",color:c.textTer}];
const initComments=[
{id:"ac1",author:"You",color:c.accentBtn,body:"Verify Vivendi citation — still good law in this circuit?",time:"10m ago",status:"open",anchor:"See In re Vivendi Universal",replies:[]},
{id:"ac2",author:"You",color:c.accentBtn,body:"Strengthen internal audit section — emphasize scienter.",time:"8m ago",status:"open",anchor:"Internal audit findings from June 2023",replies:[{id:"ar1",author:"Elnor",color:c.agentAv,body:"Can expand with specific audit findings. Cross-ref deposition?",time:"5m ago"}]},
{id:"ac3",author:"You",color:c.accentBtn,body:"Add Bloomberg market loss data.",time:"6m ago",status:"open",anchor:"wiping out approximately $2.3 billion",replies:[]},
];
const initDiffs=[
{id:"d1",match:"See In re Vivendi Universal",old:"See In re Vivendi Universal, S.A. Sec. Litig., 838 F.3d 223, 246 (2d Cir. 2016).",add:"; see also Omnicare, 575 U.S. 175, 186 (2015).",status:"pending"},
{id:"d2",match:"Internal audit findings from June 2023",old:"Internal audit findings from June 2023 identifying material weaknesses in DataCorp's revenue recognition practices, specifically related to the premature booking of multi-year service contracts.",add:" Audit report (Ex. 47): ~$134M prematurely recognized (15.8%). CFO Chen's note: \"Do NOT share outside exec team.\"",status:"pending"},
{id:"d3",match:"wiping out approximately $2.3 billion",old:"wiping out approximately $2.3 billion in market capitalization.",add:" Stock: $47.82→$31.56 on 12.4M shares (~8x avg).",status:"pending"},
];
const docLines=[
{t:"h1",v:"Motion for Summary Judgment — Response Brief"},
{t:"h2",v:"I. Introduction"},
{t:"p",v:"Plaintiff Henderson brings this action under Section 10(b) and Rule 10b-5, alleging DataCorp made materially false statements in Q2/Q3 2023 earnings."},
{t:"p",v:"Defendant moves for summary judgment. This motion should be denied."},
{t:"h2",v:"II. Statement of Facts"},
{t:"h3",v:"A. Q2 2023 Earnings"},
{t:"p",v:"On July 15, 2023, DataCorp stated \"revenue growth remains strong.\""},
{t:"h3",v:"B. Internal Documents"},
{t:"li",v:"1. CFO Chen email (June 28): \"Q3 numbers tracking significantly below guidance.\""},
{t:"li",v:"2. Board presentation (July 2): projected Q3 revenue $712M — 16% decline from Q2."},
{t:"li",v:"3. Internal audit findings from June 2023 identifying material weaknesses in DataCorp's revenue recognition practices, specifically related to the premature booking of multi-year service contracts."},
{t:"h3",v:"C. Market Response"},
{t:"p",v:"Actual Q3 results (Oct 15): revenue $698M, stock declined 34%, wiping out approximately $2.3 billion in market capitalization."},
{t:"h2",v:"IV. Argument"},
{t:"p",v:"\"Revenue growth remains strong\" is present fact, not forward-looking. See In re Vivendi Universal, S.A. Sec. Litig., 838 F.3d 223, 246 (2d Cir. 2016)."},
{t:"p",v:"PSLRA safe harbor does not protect statements made with actual knowledge of falsity."},
];
export default function ArtifactViewer(){
const idC=useRef(200);const nid=()=>"x"+(++idC.current);
const [comments,setComments]=useState(initComments);
const [diffs,setDiffs]=useState(initDiffs);
const [activeComment,setActiveComment]=useState(null);
const [bubbleMenu,setBubbleMenu]=useState(null);
const [rightOpen,setRightOpen]=useState(true);
const [rightTab,setRightTab]=useState("comments");
const [showMarkup,setShowMarkup]=useState(true);
const [findBar,setFindBar]=useState(false);
const [pinned,setPinned]=useState(false);
const [toast,setToast]=useState(null);
const [openDrop,setOpenDrop]=useState(null);
const [agentIdx,setAgentIdx]=useState(0);const [agentDrop,setAgentDrop]=useState(false);
const [chatIdx,setChatIdx]=useState(0);const [chatDrop,setChatDrop]=useState(false);
const [sendScope,setSendScope]=useState("full");
const [selectedCmIds,setSelectedCmIds]=useState(new Set());
const [outputMode,setOutputMode]=useState("respond_in_chat");
const [subMode,setSubMode]=useState("revise_artifact");
const [instruction,setInstruction]=useState("");
const [replyingTo,setReplyingTo]=useState(null);const [replyText,setReplyText]=useState("");
const [editingId,setEditingId]=useState(null);const [editText,setEditText]=useState("");
const [deletingId,setDeletingId]=useState(null);
const [newCm,setNewCm]=useState(null);const [newCmText,setNewCmText]=useState("");
const editorRef=useRef(null);const cmRef=useRef(null);
const agent=agents[agentIdx];
const openC=comments.filter(x=>x.status==="open");const resolvedC=comments.filter(x=>x.status==="resolved");
const pendingDiffs=diffs.filter(d=>d.status==="pending");
const flash=msg=>setToast(msg);
const handleMouseUp=useCallback(e=>{if(e.target.closest("[data-bubble]"))return;const sel=window.getSelection();if(!sel||sel.isCollapsed||!sel.toString().trim()){setBubbleMenu(null);return}const text=sel.toString().trim();if(text.length<3)return;const rect=sel.getRangeAt(0).getBoundingClientRect();const er=editorRef.current?.getBoundingClientRect();if(!er)return;setBubbleMenu({x:rect.left-er.left+rect.width/2,y:rect.top-er.top-8,text})},[]);
const dismissBubble=()=>{setBubbleMenu(null);window.getSelection()?.removeAllRanges()};
const startComment=()=>{if(!bubbleMenu)return;setNewCm({text:bubbleMenu.text});setBubbleMenu(null);setRightOpen(true);setRightTab("comments");setTimeout(()=>cmRef.current?.focus(),100)};
const addComment=()=>{if(!newCmText.trim())return;const cm={id:nid(),author:"You",color:c.accentBtn,body:newCmText.trim(),time:"just now",status:"open",anchor:newCm?.text||null,replies:[]};setComments(p=>[...p,cm]);setNewCm(null);setNewCmText("");setActiveComment(cm.id)};
const addReply=cmId=>{if(!replyText.trim())return;setComments(p=>p.map(cm=>cm.id===cmId?{...cm,replies:[...cm.replies,{id:nid(),author:"You",color:c.accentBtn,body:replyText.trim(),time:"just now"}]}:cm));setReplyingTo(null);setReplyText("")};
const saveEdit=id=>{if(!editText.trim())return;setComments(p=>p.map(cm=>cm.id===id?{...cm,body:editText.trim()}:cm));setEditingId(null)};
const doDelete=id=>{setComments(p=>p.filter(cm=>cm.id!==id));setDeletingId(null)};
const resolveComment=id=>{setComments(p=>p.map(cm=>cm.id===id?{...cm,status:"resolved"}:cm));if(activeComment===id)setActiveComment(null)};
const reopenComment=id=>setComments(p=>p.map(cm=>cm.id===id?{...cm,status:"open"}:cm));
const acceptDiff=id=>setDiffs(d=>d.map(x=>x.id===id?{...x,status:"accepted"}:x));
const rejectDiff=id=>setDiffs(d=>d.map(x=>x.id===id?{...x,status:"rejected"}:x));
const acceptAll=()=>{setDiffs(d=>d.map(x=>x.status==="pending"?{...x,status:"accepted"}:x));flash("All accepted")};
const rejectAll=()=>{setDiffs(d=>d.map(x=>x.status==="pending"?{...x,status:"rejected"}:x));flash("All rejected")};
const toggleCmSelect=id=>{const s=new Set(selectedCmIds);s.has(id)?s.delete(id):s.add(id);setSelectedCmIds(s)};
const handleSend=()=>{if(outputMode==="respond_in_chat"){flash(`Opening "${chats[chatIdx].name}" with ref:artifact:a1…`)}else{if(!instruction.trim())return;flash(`${agent.name}: ${subMode} (ref:artifact:a1)…`)}};
const renderLine=(line,i)=>{
const diff=diffs.find(d=>line.v.includes(d.match));
const hasCm=openC.some(cm=>cm.anchor&&line.v.includes(cm.anchor.slice(0,25)));
const isAct=activeComment&&comments.find(cm=>cm.id===activeComment)?.anchor&&line.v.includes((comments.find(cm=>cm.id===activeComment)?.anchor||"").slice(0,25));
const hl=isAct?{backgroundColor:c.accentBtn+"18",borderRadius:3,padding:"2px 4px",margin:"-2px -4px",boxShadow:`inset 0 0 0 1.5px ${c.accentBtn}40`}:hasCm?{backgroundColor:c.accentBtn+"08",borderRadius:3,padding:"1px 3px",margin:"-1px -3px"}:{};
if(diff&&diff.status==="pending"&&showMarkup){const idx=line.v.indexOf(diff.old);if(idx===-1)return plain(line,i,hl);const bef=line.v.slice(0,idx),aft=line.v.slice(idx+diff.old.length);const el=<span>{bef}<span style={{color:acDel,textDecoration:"line-through",opacity:.7}}>{diff.old}</span><span style={{color:acIns,textDecoration:"underline",fontWeight:500}}>{diff.old}{diff.add}</span>{" "}<span style={{display:"inline-flex",gap:3,verticalAlign:"middle"}}><button onClick={()=>acceptDiff(diff.id)} style={{width:17,height:17,borderRadius:3,border:`1px solid ${acIns}40`,backgroundColor:acIns+"10",cursor:"pointer",display:"inline-flex",alignItems:"center",justifyContent:"center",padding:0}}><I.Check size={10} color={acIns}/></button><button onClick={()=>rejectDiff(diff.id)} style={{width:17,height:17,borderRadius:3,border:`1px solid ${acDel}30`,backgroundColor:acDel+"08",cursor:"pointer",display:"inline-flex",alignItems:"center",justifyContent:"center",padding:0}}><I.X size={10} color={acDel}/></button><span style={{fontSize:8.5,color:acIns,fontStyle:"italic",fontWeight:600}}>{agent.name}</span></span>{aft}</span>;return line.t==="li"?<div key={i} style={{paddingLeft:20,marginBottom:8}}>{el}</div>:<p key={i} style={{marginBottom:8}}>{el}</p>}
if(diff&&(diff.status==="accepted"||(diff.status==="pending"&&!showMarkup)))return plain({...line,v:line.v.replace(diff.old,diff.old+diff.add)},i,hl);
return plain(line,i,hl);
};
const plain=(line,i,hl={})=>{const s={marginBottom:8,...hl};if(line.t==="h1")return <h1 key={i} style={{fontSize:20,fontWeight:700,color:c.textPri,margin:"20px 0 10px"}}>{line.v}</h1>;if(line.t==="h2")return <h2 key={i} style={{fontSize:17,fontWeight:700,color:c.textPri,margin:"18px 0 8px",paddingBottom:4,borderBottom:`1px solid ${c.borderLight}`}}>{line.v}</h2>;if(line.t==="h3")return <h3 key={i} style={{fontSize:14,fontWeight:700,color:c.textPri,margin:"14px 0 6px"}}>{line.v}</h3>;if(line.t==="li")return <div key={i} style={{paddingLeft:20,...s}}>{line.v}</div>;return <p key={i} style={s}>{line.v}</p>};
// Comment card with Spark icon in the action row (bottom), full opacity
const CmCard=({cm,resolved})=>{const isAct=activeComment===cm.id;return <div style={{margin:"0 8px 8px",borderRadius:R.md,border:`1px solid ${isAct?cm.color+"60":c.borderLight}`,backgroundColor:isAct?cm.color+"06":c.bgCard,overflow:"hidden",cursor:"pointer",opacity:resolved?.6:1}} onClick={()=>setActiveComment(isAct?null:cm.id)}>
<div style={{borderLeft:`3px solid ${cm.color}${resolved?"30":"40"}`,padding:"9px 11px"}}>
{cm.anchor&&<div style={{fontSize:10.5,color:c.accentBtn,backgroundColor:c.accentBtn+"08",padding:"3px 6px",borderRadius:3,marginBottom:6,fontStyle:"italic",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}}>"{cm.anchor.slice(0,55)}"</div>}
<div style={{display:"flex",alignItems:"center",gap:5,marginBottom:5}}><Av letter={cm.author[0]} color={cm.color} size={16}/><span style={{fontSize:11.5,fontWeight:650,color:cm.color}}>{cm.author}</span><span style={{fontSize:9.5,color:c.textTer}}>{cm.time}</span></div>
{editingId===cm.id?<div onClick={e=>e.stopPropagation()}><textarea value={editText} onChange={e=>setEditText(e.target.value)} autoFocus style={{width:"100%",padding:"5px 7px",borderRadius:R.sm,border:`1px solid ${c.accentBtn}40`,fontSize:12,fontFamily:font.sans,outline:"none",minHeight:40,resize:"vertical"}}/><div style={{display:"flex",gap:4,marginTop:4}}><Btn small primary onClick={()=>saveEdit(cm.id)}>Save</Btn><Btn small ghost onClick={()=>setEditingId(null)}>Cancel</Btn></div></div>
:<div style={{fontSize:12,lineHeight:1.5,color:c.textSec,marginBottom:5}}>{cm.body}</div>}
{editingId!==cm.id&&<div style={{display:"flex",alignItems:"center",gap:6,fontSize:10.5}} onClick={e=>e.stopPropagation()}>
{!resolved&&<span style={{color:c.textTer,cursor:"pointer",fontWeight:500}} onClick={()=>{setReplyingTo(replyingTo===cm.id?null:cm.id);setReplyText("")}}>Reply</span>}
{!resolved&&<span style={{color:c.textTer,cursor:"pointer",fontWeight:500}} onClick={()=>resolveComment(cm.id)}>Resolve</span>}
{resolved&&<span style={{color:c.accentBtn,cursor:"pointer",fontWeight:500}} onClick={()=>reopenComment(cm.id)}>Reopen</span>}
{cm.author==="You"&&<span style={{color:c.textTer,cursor:"pointer",fontWeight:500}} onClick={()=>{setEditingId(cm.id);setEditText(cm.body)}}>Edit</span>}
{cm.author==="You"&&deletingId!==cm.id&&<span style={{color:c.error,cursor:"pointer",fontWeight:500,opacity:.7}} onClick={()=>setDeletingId(cm.id)}>Delete</span>}
{deletingId===cm.id&&<><span style={{color:c.error,fontWeight:600,fontSize:10}}>Delete?</span><span style={{color:c.error,cursor:"pointer",fontWeight:700,textDecoration:"underline"}} onClick={()=>doDelete(cm.id)}>Yes</span><span style={{color:c.textTer,cursor:"pointer"}} onClick={()=>setDeletingId(null)}>No</span></>}
<span style={{flex:1}}/>
<span onClick={()=>flash(`Sent to ${agent.name}`)} title={`Send to ${agent.name}`} style={{cursor:"pointer",display:"flex",alignItems:"center",gap:2,color:c.textSec,fontWeight:600,fontSize:10}} onMouseEnter={e=>e.currentTarget.style.color=c.accentBtn} onMouseLeave={e=>e.currentTarget.style.color=c.textSec}><I.Spark size={11}/>Send</span>
</div>}
</div>
{cm.replies.map(r=><div key={r.id} style={{borderLeft:`3px solid ${r.color}40`,borderTop:`1px solid ${c.borderLight}`,padding:"7px 11px",backgroundColor:c.bgPanelAlt}}><div style={{display:"flex",alignItems:"center",gap:5,marginBottom:4}}><Av letter={r.author[0]} color={r.color} size={14}/><span style={{fontSize:10.5,fontWeight:650,color:r.color}}>{r.author}</span><span style={{fontSize:9,color:c.textTer}}>{r.time}</span></div><div style={{fontSize:11.5,lineHeight:1.5,color:c.textSec}}>{r.body}</div></div>)}
{replyingTo===cm.id&&<div style={{borderTop:`1px solid ${c.borderLight}`,padding:"8px 11px",backgroundColor:c.bgPanelAlt}} onClick={e=>e.stopPropagation()}><textarea value={replyText} onChange={e=>setReplyText(e.target.value)} placeholder="Reply… (Enter)" autoFocus onKeyDown={e=>{if(e.key==="Enter"&&!e.shiftKey){e.preventDefault();addReply(cm.id)}}} style={{width:"100%",padding:"5px 7px",borderRadius:R.sm,border:`1px solid ${c.border}`,fontSize:11.5,fontFamily:font.sans,outline:"none",minHeight:36,resize:"vertical"}}/><div style={{display:"flex",gap:4,marginTop:4}}><Btn small primary onClick={()=>addReply(cm.id)} disabled={!replyText.trim()}>Reply</Btn><Btn small ghost onClick={()=>setReplyingTo(null)}>Cancel</Btn></div></div>}
</div>};
return (
<div style={{display:"flex",width:"100%",height:"100vh",fontFamily:font.sans,color:c.textPri,backgroundColor:c.bgApp,overflow:"hidden"}} onClick={()=>{setOpenDrop(null);setAgentDrop(false);setChatDrop(false)}}>
<style>{`*{box-sizing:border-box;margin:0;padding:0}::-webkit-scrollbar{width:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:transparent;border-radius:2px}*:hover::-webkit-scrollbar-thumb{background:#d1d5db}::selection{background:${c.accentBtn}22}`}</style>
{toast&&<Toast msg={toast} onDone={()=>setToast(null)}/>}
<div style={{flex:1,display:"flex",flexDirection:"column",overflow:"hidden"}}>
{/* Title */}
<div style={{display:"flex",alignItems:"center",gap:8,padding:"7px 16px",borderBottom:`1px solid ${c.borderLight}`,backgroundColor:c.bgPanel}}><I.FileText size={15} color={c.accentBtn}/><span style={{fontSize:14,fontWeight:650,flex:1}}>Motion for Summary Judgment — Response Brief</span><span style={{fontSize:10,padding:"2px 8px",borderRadius:R.full,backgroundColor:c.bgInput,color:c.textTer,fontWeight:600}}>Markdown</span></div>
{/* Toolbar */}
<div style={{display:"flex",alignItems:"center",gap:3,padding:"4px 10px",borderBottom:`1px solid ${c.border}`,backgroundColor:c.bgPanel,flexWrap:"wrap"}}>
<TBtn icon={<I.Copy size={13}/>} title="Copy all" onClick={()=>flash("Copied")}/>
<div style={{position:"relative"}}><TBtn icon={<I.Save size={13}/>} label="Save As…" dropdown onClick={e=>{e.stopPropagation();setOpenDrop(openDrop==="save"?null:"save")}}/>
{openDrop==="save"&&<div onClick={e=>e.stopPropagation()} style={{position:"absolute",top:30,left:0,backgroundColor:c.bgCard,border:`1px solid ${c.border}`,borderRadius:R.sm,boxShadow:"0 8px 24px rgba(0,0,0,0.12)",zIndex:999,minWidth:190,padding:4}}>
<DItem onClick={()=>{flash("Save Markdown…");setOpenDrop(null)}}>Save As… (Markdown)</DItem>
<DItem onClick={()=>{flash("Export DOCX…");setOpenDrop(null)}}>Export as DOCX</DItem>
<DItem onClick={()=>{flash("Export PDF…");setOpenDrop(null)}}>Export as PDF</DItem>
<DItem onClick={()=>{flash("Export HTML…");setOpenDrop(null)}}>Export as HTML</DItem>
<div style={{borderTop:`1px solid ${c.borderLight}`,margin:"2px 0"}}/>
<DItem onClick={()=>{flash("Save as Prompt…");setOpenDrop(null)}}>Save as Prompt</DItem>
</div>}</div>
<Sep/><TBtn icon={<I.FileText size={13}/>} label="To Note" onClick={()=>flash("Converting…")}/>
<TBtn icon={<I.Link size={13}/>} label="Ref" onClick={()=>flash("Ref copied")}/>
<Sep/><TBtn icon={<I.Search size={13}/>} active={findBar} onClick={()=>setFindBar(!findBar)}/>
<TBtn icon={<I.ExtLink size={13}/>} title="Open" onClick={()=>flash("Opening…")}/>
<TBtn icon={<I.Printer size={13}/>} onClick={()=>flash("Print…")}/>
<TBtn icon={<I.Pin size={13}/>} active={pinned} onClick={()=>{setPinned(!pinned);flash(pinned?"Unpinned":"Pinned")}}/>
<TBtn icon={<I.Maximize size={13}/>}/>
<Sep/><TBtn icon={<I.MsgCircle size={13}/>} label={`${openC.length}`} active={rightOpen&&rightTab==="comments"} onClick={()=>{if(rightOpen&&rightTab==="comments")setRightOpen(false);else{setRightOpen(true);setRightTab("comments")}}}/>
{pendingDiffs.length>0&&<><Sep/><TBtn icon={<I.Eye size={13}/>} label={showMarkup?"Markup":"Clean"} active={showMarkup} onClick={()=>setShowMarkup(!showMarkup)}/>
<div style={{position:"relative"}}><TBtn icon={<I.Edit size={13}/>} label={`${pendingDiffs.length}`} dropdown active onClick={e=>{e.stopPropagation();setOpenDrop(openDrop==="review"?null:"review")}}/>
{openDrop==="review"&&<div onClick={e=>e.stopPropagation()} style={{position:"absolute",top:30,right:0,backgroundColor:c.bgCard,border:`1px solid ${c.border}`,borderRadius:R.sm,boxShadow:"0 8px 24px rgba(0,0,0,0.12)",zIndex:999,minWidth:170,padding:4}}>
<DItem onClick={()=>{acceptAll();setOpenDrop(null)}}>✓ Accept All ({pendingDiffs.length})</DItem>
<DItem onClick={()=>{rejectAll();setOpenDrop(null)}}>✗ Reject All ({pendingDiffs.length})</DItem>
</div>}</div></>}
<Sep/>
<div style={{position:"relative"}}><TBtn icon={<I.Clock size={13}/>} label="v3" dropdown onClick={e=>{e.stopPropagation();setOpenDrop(openDrop==="ver"?null:"ver")}}/>
{openDrop==="ver"&&<div onClick={e=>e.stopPropagation()} style={{position:"absolute",top:30,right:0,backgroundColor:c.bgCard,border:`1px solid ${c.border}`,borderRadius:R.sm,boxShadow:"0 8px 24px rgba(0,0,0,0.12)",zIndex:999,minWidth:240,padding:4}}>
<div style={{padding:"5px 10px",fontSize:10,fontWeight:600,color:c.textTer,borderBottom:`1px solid ${c.borderLight}`}}>Version History</div>
<DItem active onClick={()=>{setOpenDrop(null)}}>v3 — Review revision · 10m</DItem>
<DItem onClick={()=>{flash("Switched to v2");setOpenDrop(null)}}>v2 — Review revision · 2h</DItem>
<DItem onClick={()=>{flash("Switched to v1");setOpenDrop(null)}}>v1 — Original · 1d</DItem>
</div>}</div>
<div style={{flex:1}}/>
<button onClick={()=>{if(rightOpen&&rightTab==="send")setRightOpen(false);else{setRightOpen(true);setRightTab("send")}}} style={{padding:"4px 12px",borderRadius:R.sm,border:`1px solid ${c.accentBtn}40`,backgroundColor:(rightOpen&&rightTab==="send")?c.accentBtn:c.accentBtn+"08",cursor:"pointer",display:"flex",alignItems:"center",gap:5,color:(rightOpen&&rightTab==="send")?"#fff":c.accentBtn,fontFamily:font.sans,fontSize:11.5,fontWeight:600,height:28}}><I.Spark size={13}/>Send to Agent<I.ChevD size={10}/></button>
</div>
{findBar&&<div style={{display:"flex",alignItems:"center",gap:6,padding:"5px 12px",borderBottom:`1px solid ${c.borderLight}`,backgroundColor:c.bgPanelAlt}}><I.Search size={12} color={c.textTer}/><input placeholder="Find…" autoFocus style={{flex:1,border:"none",backgroundColor:"transparent",fontSize:12,fontFamily:font.sans,outline:"none",maxWidth:300}}/><span style={{fontSize:10,color:c.textTer}}>0/0</span><button onClick={()=>setFindBar(false)} style={{padding:2,border:"none",cursor:"pointer",backgroundColor:"transparent",display:"flex",color:c.textTer}}><I.X size={12}/></button></div>}
<div style={{flex:1,display:"flex",overflow:"hidden"}}>
<div ref={editorRef} style={{flex:1,overflowY:"auto",padding:"20px 32px 40px",position:"relative",fontSize:13.5,lineHeight:1.75,color:c.textSec,userSelect:"text"}} onMouseUp={handleMouseUp} onMouseDown={e=>{if(bubbleMenu&&!e.target.closest("[data-bubble]"))setBubbleMenu(null)}}>
{docLines.map((l,i)=>renderLine(l,i))}
{bubbleMenu&&<div data-bubble="1" style={{position:"absolute",left:Math.max(10,Math.min(bubbleMenu.x-140,(editorRef.current?.clientWidth||500)-320)),top:bubbleMenu.y-44,backgroundColor:c.bgCard,border:`1px solid ${c.border}`,borderRadius:R.md,boxShadow:"0 4px 16px rgba(0,0,0,0.12)",padding:"4px 6px",display:"flex",gap:2,zIndex:100,alignItems:"center"}}>
<button onClick={startComment} style={{padding:"5px 10px",borderRadius:R.sm,border:"none",cursor:"pointer",backgroundColor:c.accentBtn,color:"#fff",fontSize:11,fontWeight:600,fontFamily:font.sans,display:"flex",alignItems:"center",gap:4}}><I.MsgCircle size={11}/>Comment</button><Sep/>
<div style={{display:"flex",alignItems:"center",gap:2,padding:"2px 6px",borderRadius:R.sm,border:`1px solid ${c.borderLight}`,cursor:"pointer"}}><Dot color={agent.color} size={6}/><span style={{fontSize:10,fontWeight:500}}>{agent.name}</span></div>
<button onClick={()=>{flash(`Sent to ${agent.name}`);dismissBubble()}} style={{padding:"5px 8px",borderRadius:R.sm,border:"none",cursor:"pointer",backgroundColor:"transparent",color:c.textSec,fontSize:11,fontFamily:font.sans,display:"flex",alignItems:"center",gap:3}} onMouseEnter={e=>e.currentTarget.style.backgroundColor=c.bgInput} onMouseLeave={e=>e.currentTarget.style.backgroundColor="transparent"}><I.Spark size={11}/>Ask {agent.name}</button>
{["Rewrite","Expand","Shorten"].map(a=><button key={a} onClick={()=>{flash(`${agent.name}: ${a}…`);dismissBubble()}} style={{padding:"5px 8px",borderRadius:R.sm,border:"none",cursor:"pointer",backgroundColor:"transparent",color:c.textSec,fontSize:11,fontFamily:font.sans}} onMouseEnter={e=>e.currentTarget.style.backgroundColor=c.bgInput} onMouseLeave={e=>e.currentTarget.style.backgroundColor="transparent"}>{a}</button>)}
<button onClick={dismissBubble} style={{padding:3,border:"none",cursor:"pointer",backgroundColor:"transparent",color:c.textTer,display:"flex"}}><I.X size={11}/></button>
</div>}
</div>
{/* Right panel */}
{rightOpen&&<div style={{width:rightTab==="send"?300:260,flexShrink:0,borderLeft:`1px solid ${c.border}`,display:"flex",flexDirection:"column",backgroundColor:c.bgPanelAlt,overflow:"hidden"}}>
<div style={{display:"flex",borderBottom:`1px solid ${c.borderLight}`,flexShrink:0}}>
<button onClick={()=>setRightTab("comments")} style={{flex:1,padding:"7px 0",border:"none",cursor:"pointer",backgroundColor:rightTab==="comments"?c.bgPanelAlt:c.bgInput,color:rightTab==="comments"?c.accentBtn:c.textTer,fontSize:10.5,fontWeight:rightTab==="comments"?650:450,fontFamily:font.sans,borderBottom:rightTab==="comments"?`2px solid ${c.accentBtn}`:"2px solid transparent",display:"flex",alignItems:"center",justifyContent:"center",gap:3}}><I.MsgCircle size={10}/>Comments ({openC.length})</button>
<button onClick={()=>setRightTab("send")} style={{flex:1,padding:"7px 0",border:"none",cursor:"pointer",backgroundColor:rightTab==="send"?c.bgPanel:c.bgInput,color:rightTab==="send"?c.accentBtn:c.textTer,fontSize:10.5,fontWeight:rightTab==="send"?650:450,fontFamily:font.sans,borderBottom:rightTab==="send"?`2px solid ${c.accentBtn}`:"2px solid transparent",display:"flex",alignItems:"center",justifyContent:"center",gap:3}}><I.Spark size={10}/>Send to Agent</button>
<button onClick={()=>setRightOpen(false)} style={{width:26,border:"none",cursor:"pointer",backgroundColor:"transparent",display:"flex",alignItems:"center",justifyContent:"center",color:c.textTer}}><I.X size={11}/></button>
</div>
{rightTab==="comments"&&<div style={{flex:1,overflowY:"auto",padding:"8px 0"}}>
{newCm&&<div style={{margin:"0 8px 10px",borderRadius:R.md,border:`2px solid ${c.accentBtn}40`,backgroundColor:c.bgCard,overflow:"hidden"}}><div style={{padding:"8px 11px"}}>{newCm.text&&<div style={{fontSize:10.5,color:c.accentBtn,backgroundColor:c.accentBtn+"08",padding:"3px 6px",borderRadius:3,marginBottom:8,fontStyle:"italic"}}>"{newCm.text.slice(0,80)}"</div>}<textarea ref={cmRef} value={newCmText} onChange={e=>setNewCmText(e.target.value)} placeholder="Comment… (Enter)" onKeyDown={e=>{if(e.key==="Enter"&&!e.shiftKey){e.preventDefault();addComment()}}} style={{width:"100%",padding:"6px 8px",borderRadius:R.sm,border:`1px solid ${c.border}`,fontSize:12,fontFamily:font.sans,outline:"none",minHeight:42,resize:"vertical"}}/><div style={{display:"flex",gap:6,marginTop:6}}><Btn small primary onClick={addComment} disabled={!newCmText.trim()}>Add</Btn><Btn small ghost onClick={()=>{setNewCm(null);setNewCmText("")}}>Cancel</Btn></div></div></div>}
{openC.length>0&&<div style={{padding:"3px 10px 6px"}}><span style={{fontSize:9.5,fontWeight:650,color:c.textTer,textTransform:"uppercase",letterSpacing:".06em"}}>Open ({openC.length})</span></div>}
{openC.map(cm=><CmCard key={cm.id} cm={cm} resolved={false}/>)}
{resolvedC.length>0&&<div style={{padding:"8px 10px 4px"}}><span style={{fontSize:9.5,fontWeight:650,color:c.textTer,textTransform:"uppercase",letterSpacing:".06em"}}>Resolved ({resolvedC.length})</span></div>}
{resolvedC.map(cm=><CmCard key={cm.id} cm={cm} resolved={true}/>)}
<div style={{padding:"8px 10px"}}><div style={{display:"flex",alignItems:"center",gap:5,padding:"7px 9px",borderRadius:R.sm,border:`1px solid ${c.border}`,backgroundColor:c.bgCard,cursor:"text"}} onClick={()=>setNewCm({text:""})}><I.Plus size={11} color={c.textTer}/><span style={{fontSize:11.5,color:c.textTer}}>Add general comment…</span></div></div>
</div>}
{rightTab==="send"&&<><div style={{flex:1,overflowY:"auto",padding:12,backgroundColor:c.bgPanel}}>
<div style={{marginBottom:12}}><div style={{fontSize:11,fontWeight:600,color:c.textTer,marginBottom:5}}>Agent</div>
<div style={{position:"relative"}}><div onClick={e=>{e.stopPropagation();setAgentDrop(!agentDrop)}} style={{display:"flex",alignItems:"center",gap:6,padding:"7px 10px",borderRadius:R.sm,border:`1px solid ${c.border}`,backgroundColor:c.bgCard,cursor:"pointer"}}><Av letter={agent.letter} color={agent.color} size={20}/><span style={{flex:1,fontSize:12,fontWeight:600}}>{agent.name}</span><I.ChevD size={11} color={c.textTer}/></div>
{agentDrop&&<div style={{position:"absolute",top:"100%",left:0,right:0,marginTop:2,backgroundColor:c.bgCard,border:`1px solid ${c.border}`,borderRadius:R.sm,boxShadow:"0 4px 12px rgba(0,0,0,0.1)",zIndex:50,padding:3}}>{agents.map((a,i)=><DItem key={a.name} active={i===agentIdx} onClick={()=>{setAgentIdx(i);setAgentDrop(false)}}><Av letter={a.letter} color={a.color} size={18}/>{a.name}{i===agentIdx&&<I.Check size={12} color={c.accentBtn}/>}</DItem>)}</div>}</div></div>
<div style={{marginBottom:12}}><div style={{fontSize:11,fontWeight:600,color:c.textTer,marginBottom:5}}>Send in</div>
<div style={{position:"relative"}}><div onClick={e=>{e.stopPropagation();setChatDrop(!chatDrop)}} style={{display:"flex",alignItems:"center",gap:6,padding:"6px 10px",borderRadius:R.sm,border:`1px solid ${c.border}`,backgroundColor:c.bgCard,fontSize:11.5,cursor:"pointer"}}><Dot color={chats[chatIdx].color} size={5}/><span style={{flex:1,color:c.textSec}}>{chats[chatIdx].name}</span>{chats[chatIdx].origin&&<span style={{fontSize:9,color:c.textTer}}>origin</span>}<I.ChevD size={10} color={c.textTer}/></div>
{chatDrop&&<div style={{position:"absolute",top:"100%",left:0,right:0,marginTop:2,backgroundColor:c.bgCard,border:`1px solid ${c.border}`,borderRadius:R.sm,boxShadow:"0 4px 12px rgba(0,0,0,0.1)",zIndex:50,padding:3}}>{chats.map((ch,i)=><DItem key={ch.name} active={i===chatIdx} onClick={()=>{setChatIdx(i);setChatDrop(false)}}><Dot color={ch.color} size={5}/>{ch.name}{ch.origin&&<span style={{fontSize:9,color:c.textTer}}>origin</span>}{i===chatIdx&&<I.Check size={12} color={c.accentBtn}/>}</DItem>)}</div>}</div></div>
<div style={{marginBottom:12}}><div style={{fontSize:11,fontWeight:600,color:c.textTer,marginBottom:5}}>Context scope</div>
<Radio selected={sendScope==="full"} label="Full document + all comments" onClick={()=>setSendScope("full")}/>
<Radio selected={sendScope==="comments_only"} label="Comments & referenced portions" onClick={()=>setSendScope("comments_only")}/>
<Radio selected={sendScope==="select"} label="Select comments & references" onClick={()=>setSendScope("select")}/>
</div>
{openC.length>0&&<div style={{marginBottom:12}}><div style={{fontSize:11,fontWeight:600,color:c.textTer,marginBottom:5}}>Comments ({sendScope==="select"?`${selectedCmIds.size} selected`:openC.length})</div>
{openC.map(cm=><div key={cm.id} style={{display:"flex",alignItems:"center",gap:6,padding:"4px 8px",borderRadius:R.sm,border:`1px solid ${sendScope==="select"&&selectedCmIds.has(cm.id)?c.accentBtn+"50":c.borderLight}`,marginBottom:3,backgroundColor:sendScope==="select"&&selectedCmIds.has(cm.id)?c.accentBtn+"06":c.bgPanelAlt,cursor:sendScope==="select"?"pointer":"default"}} onClick={()=>{if(sendScope==="select")toggleCmSelect(cm.id)}}>
{sendScope==="select"&&<input type="checkbox" checked={selectedCmIds.has(cm.id)} readOnly style={{width:12,height:12,accentColor:c.accentBtn,flexShrink:0}}/>}
<div style={{flex:1,minWidth:0,fontSize:10,color:c.textSec,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}}>{cm.body}</div>
</div>)}</div>}
<div style={{marginBottom:12}}><div style={{fontSize:11,fontWeight:600,color:c.textTer,marginBottom:5}}>Output mode</div>
<Radio selected={outputMode==="respond_in_chat"} label="Respond in chat" desc="Opens chat with context." onClick={()=>setOutputMode("respond_in_chat")}/>
<Radio selected={outputMode==="send_with_instructions"} label="Send with instructions" desc="Agent processes and produces result." onClick={()=>setOutputMode("send_with_instructions")}/>
</div>
{outputMode==="send_with_instructions"&&<>
<div style={{marginBottom:12}}><div style={{fontSize:11,fontWeight:600,color:c.textTer,marginBottom:5}}>Result format</div>
<Radio selected={subMode==="revise_artifact"} label="Revise artifact (new version)" desc="Original preserved in history." onClick={()=>setSubMode("revise_artifact")}/>
<Radio selected={subMode==="convert_to_note"} label="Convert to note with changes" onClick={()=>setSubMode("convert_to_note")}/>
<Radio selected={subMode==="respond_in_comments"} label="Respond in comments" desc="Agent replies to each comment." onClick={()=>setSubMode("respond_in_comments")}/>
</div>
<div style={{marginBottom:12}}><div style={{fontSize:11,fontWeight:600,color:c.textPri,marginBottom:5}}>Instruction <span style={{color:c.error}}>*</span></div>
<textarea value={instruction} onChange={e=>setInstruction(e.target.value)} placeholder="Tell the agent what to do…" style={{width:"100%",padding:"8px 10px",borderRadius:R.sm,border:`1px solid ${instruction.trim()?c.border:c.warn+"60"}`,fontSize:12,fontFamily:font.sans,outline:"none",minHeight:50,resize:"vertical"}}/>
{!instruction.trim()&&<div style={{fontSize:9,color:c.warn,marginTop:3}}>Required</div>}</div>
</>}
{outputMode==="respond_in_chat"&&<div style={{marginBottom:12}}><div style={{fontSize:11,fontWeight:600,color:c.textTer,marginBottom:5}}>Message (optional)</div>
<textarea value={instruction} onChange={e=>setInstruction(e.target.value)} placeholder="Optional context…" style={{width:"100%",padding:"8px 10px",borderRadius:R.sm,border:`1px solid ${c.border}`,fontSize:12,fontFamily:font.sans,outline:"none",minHeight:44,resize:"vertical"}}/></div>}
<div style={{padding:"6px 0",fontSize:9,color:c.textTer}}>Reference: artifact:a1 will be included automatically.</div>
</div>
<div style={{padding:"10px 12px",borderTop:`1px solid ${c.borderLight}`,backgroundColor:c.bgPanel,display:"flex",gap:6}}>
<Btn primary disabled={outputMode==="send_with_instructions"&&!instruction.trim()} onClick={handleSend} style={{flex:1,justifyContent:"center"}}><I.Spark size={12} color="#fff"/>{outputMode==="respond_in_chat"?"Open in Chat":`Send to ${agent.name}`}</Btn>
<Btn ghost onClick={()=>setRightOpen(false)}>Cancel</Btn>
</div></>}
</div>}
</div>
{/* Metadata */}
<div style={{display:"flex",alignItems:"center",gap:10,padding:"5px 16px",borderTop:`1px solid ${c.border}`,backgroundColor:c.bgPanel,fontSize:10.5,color:c.textTer,flexWrap:"wrap"}}>
<span style={{display:"flex",alignItems:"center",gap:4}}><I.Spark size={10} color={c.agentAv}/><strong style={{color:c.textSec}}>{agent.name}</strong> in <span style={{color:c.accentBtn,cursor:"pointer"}}>Henderson discovery</span></span>
<span>·</span><span style={{display:"flex",alignItems:"center",gap:3}}><Dot color="#31588c" size={5}/>Henderson v. DataCorp</span>
<span>·</span><span>v3{pendingDiffs.length>0&&<span style={{color:c.warn,fontWeight:600}}> · {pendingDiffs.length} pending</span>}</span>
<span>·</span><span>1d ago</span>
{pinned&&<><span>·</span><span style={{color:c.accentBtn,fontWeight:600}}>📌</span></>}
</div>
</div>
</div>
);
}