// Story Weaver — stage panels + shared bits for the Auto-Weave pipeline.
// Exported on window.WV and consumed by weaver.jsx (the main PageWeaver).
(function () {
const { useState } = React;
const { wash } = LMTXUtil;
const I = window.LMTIcons;
const { CANDIDATES, ev } = window.WEAVER;
const leafFor = LMT.leafFor;

// ── shared: method badge ─────────────────────────────────────────────────────
const METHOD_C = { GET:'#2563eb', POST:'#16a34a', PATCH:'#d97706', DELETE:'#dc2626', SSE:'#9333ea' };
function MethodBadge({ m }) {
  return <span className="mono" style={{ fontSize:9.5, fontWeight:600, letterSpacing:'0.04em', color:'#fff', background:METHOD_C[m]||'#64748b', padding:'2px 6px', borderRadius:5, flex:'0 0 auto' }}>{m}</span>;
}

// ── shared: confidence ring ──────────────────────────────────────────────────
function Confidence({ value, size = 64, label = true, threshold = 0.6 }) {
  const r = size/2 - 5, C = 2*Math.PI*r;
  const ok = value >= threshold;
  const col = ok ? 'var(--accent)' : '#d97706';
  return (
    <div style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:4 }}>
      <div style={{ position:'relative', width:size, height:size }}>
        <svg width={size} height={size} style={{ transform:'rotate(-90deg)' }}>
          <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="var(--sunken)" strokeWidth="5"/>
          <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={col} strokeWidth="5" strokeLinecap="round"
            strokeDasharray={C} strokeDashoffset={C*(1-value)} style={{ transition:'stroke-dashoffset .9s cubic-bezier(.2,.8,.2,1)' }}/>
        </svg>
        <div style={{ position:'absolute', inset:0, display:'grid', placeItems:'center' }}>
          <span className="disp tnum" style={{ fontSize:size*0.26, fontWeight:650, color:col }}>{Math.round(value*100)}</span>
        </div>
      </div>
      {label && <span className="mono" style={{ fontSize:9, letterSpacing:'0.08em', textTransform:'uppercase', color:'var(--faint)' }}>confidence</span>}
    </div>
  );
}

// ── shared: a small leaf + short label chip ──────────────────────────────────
function LeafChip({ id, role, active, dim, onHover }) {
  const e = ev(id); if (!e) return null;
  const short = e.title.split(/[—·]/)[0].trim();
  return (
    <span onMouseEnter={() => onHover && onHover(id)} onMouseLeave={() => onHover && onHover(null)}
      style={{ display:'inline-flex', alignItems:'center', gap:7, padding:'5px 10px 5px 7px', borderRadius:9,
        background: active ? 'var(--accent-wash)' : 'var(--sunken)', border:'1px solid', borderColor: active ? 'var(--accent-line)' : 'var(--hair)',
        opacity: dim ? 0.5 : 1, transition:'all .2s', maxWidth:230, cursor:'default' }}>
      <LeafShape color={leafFor(e.daysSince)} size={15}/>
      {role && <span className="mono" style={{ fontSize:8.5, letterSpacing:'0.06em', textTransform:'uppercase', color:'var(--accent)' }}>{role}</span>}
      <span style={{ fontSize:11.5, color:'var(--ink)', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{short}</span>
    </span>
  );
}

// ── INBOX ────────────────────────────────────────────────────────────────────
function InboxPanel({ onStart }) {
  const incoming = LMTData.events.slice(0, 7);
  return (
    <div className="card-r rise" style={{ padding:'20px 22px' }}>
      <div style={{ display:'flex', alignItems:'center', gap:16, flexWrap:'wrap' }}>
        <div style={{ display:'flex', alignItems:'baseline', gap:9 }}>
          <span className="disp tnum" style={{ fontSize:46, fontWeight:650, letterSpacing:'-0.04em', color:'var(--ink)' }}>32</span>
          <div>
            <div className="disp" style={{ fontSize:15, fontWeight:600 }}>memories waiting</div>
            <div className="mono" style={{ fontSize:11, color:'var(--faint)' }}>last wove · 9 days ago</div>
          </div>
        </div>
        <div style={{ flex:1 }}/>
        <button onClick={onStart} className="btn btn-pri press" style={{ padding:'12px 18px', fontSize:14 }}>
          <I.Sparkles style={{ fontSize:16 }}/>Auto-weave these memories
        </button>
      </div>
      <div style={{ display:'flex', gap:9, marginTop:18, overflowX:'auto', paddingBottom:4 }} className="scroll">
        {incoming.map((e,i) => (
          <div key={e.id} className="pop" style={{ flex:'0 0 auto', width:200, padding:'10px 12px', borderRadius:11, background:'var(--sunken)', border:'1px solid var(--hair)', '--i':i, animationDelay:`${i*0.05}s` }}>
            <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:6 }}>
              <LeafShape color={leafFor(e.daysSince)} size={16}/>
              <span className="mono" style={{ fontSize:10, color:'var(--faint)' }}>{e.t} · {e.scene.split(' · ')[0]}</span>
            </div>
            <div style={{ fontSize:12.5, color:'var(--ink)', lineHeight:1.35, textWrap:'pretty' }}>{e.title}</div>
          </div>
        ))}
        <div style={{ flex:'0 0 auto', display:'grid', placeItems:'center', width:120, color:'var(--faint)', fontSize:12 }} className="mono">+25 more</div>
      </div>
    </div>
  );
}

// ── SCAN ─────────────────────────────────────────────────────────────────────
function ScanPanel({ frac }) {
  const cats = [
    { label:'People',  total:8,  icon:I.People },
    { label:'Places',  total:6,  icon:I.Pin },
    { label:'Themes',  total:11, icon:I.Tag },
    { label:'Moods',   total:5,  icon:I.Heart },
  ];
  return (
    <div className="card-r rise" style={{ padding:'18px 20px' }}>
      <SectionHead title="Reading meaning" sub="Each memory becomes a vector — who, where, when, and how it felt."/>
      <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fit, minmax(150px, 1fr))', gap:12 }}>
        {cats.map((c,i) => {
          const n = Math.round(c.total * frac);
          return (
            <div key={c.label} style={{ padding:'13px 14px', borderRadius:12, background:'var(--sunken)', border:'1px solid var(--hair)' }}>
              <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:9 }}>
                <span style={{ fontSize:15, color:'var(--accent)' }}><c.icon/></span>
                <span className="disp tnum" style={{ fontSize:20, fontWeight:650 }}>{n}</span>
              </div>
              <div className="mono" style={{ fontSize:10, letterSpacing:'0.06em', textTransform:'uppercase', color:'var(--faint)', marginBottom:7 }}>{c.label}</div>
              <div className="meter"><i style={{ width:`${frac*100}%`, animation:'none', transition:'width .3s linear' }}/></div>
            </div>
          );
        })}
      </div>
      <div className="mono" style={{ fontSize:11, color:'var(--faint)', marginTop:14, display:'flex', alignItems:'center', gap:8 }}>
        <span className="breathe" style={{ width:7, height:7, borderRadius:'50%', background:'var(--accent)', flex:'0 0 auto' }}/>
        embedding · {Math.round(frac*32)} / 32 memories vectorized
      </div>
    </div>
  );
}

// ── CLUSTER ──────────────────────────────────────────────────────────────────
function ClusterPanel({ onPick, candId }) {
  const rows = CANDIDATES.map(c => ({ id:c.id, title:c.title, n:c.beats.length, coh:c.confidence, below:c.belowThreshold }));
  return (
    <div className="card-r rise" style={{ padding:'18px 20px' }}>
      <SectionHead title="Threads found" count={3} sub="Memories pulled together by similarity. Tap a thread to inspect it."/>
      <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
        {rows.map((r,i) => {
          const active = r.id === candId;
          return (
            <button key={r.id} onClick={() => onPick(r.id)} className="press" style={{ display:'flex', alignItems:'center', gap:13, padding:'11px 13px', borderRadius:12, textAlign:'left', cursor:'pointer',
              background: active ? 'var(--accent-wash)' : 'var(--sunken)', border:'1px solid', borderColor: active ? 'var(--accent-line)' : 'var(--hair)' }}>
              <span className="mono" style={{ fontSize:11, color:'var(--faint)', width:34, flex:'0 0 auto' }}>cl_{i+1}</span>
              <span style={{ flex:1, minWidth:0 }}>
                <span className="disp" style={{ display:'block', fontSize:14, fontWeight:600, color:'var(--ink)' }}>{r.title}</span>
                <span style={{ fontSize:11.5, color:'var(--muted)' }}>{r.n} memories{r.below ? ' · below weave threshold' : ''}</span>
              </span>
              <span className="mono tnum" style={{ fontSize:12.5, fontWeight:600, color: r.below ? '#d97706' : 'var(--accent)' }}>{r.coh.toFixed(2)}</span>
            </button>
          );
        })}
      </div>
      <div style={{ fontSize:11.5, color:'var(--faint)', marginTop:12, display:'flex', alignItems:'center', gap:7 }}>
        <I.Undo style={{ fontSize:13 }}/> 6 memories set aside — too far from any thread.
      </div>
    </div>
  );
}

// ── PROPOSE (review & accept loop) ───────────────────────────────────────────
function ProposePanel({ cand, accepted, onAccept, onSelect, onAdvance, onHover }) {
  const others = CANDIDATES.filter(c => c.id !== cand.id);
  return (
    <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
      <div className="card-r rise" style={{ padding:'20px 22px', position:'relative', overflow:'hidden' }}>
        <div style={{ position:'absolute', inset:0, background:'radial-gradient(80% 60% at 100% 0%, var(--accent-wash), transparent 60%)', pointerEvents:'none' }}/>
        <div style={{ position:'relative', display:'flex', gap:18, alignItems:'flex-start', flexWrap:'wrap' }}>
          <div style={{ flex:1, minWidth:220 }}>
            <span className="pill pill-accent" style={{ marginBottom:10 }}><I.Branch style={{ fontSize:11 }}/>Proposed Playbook</span>
            <h2 className="serif" style={{ fontSize:24, fontWeight:600, margin:'0 0 6px', letterSpacing:'-0.01em', lineHeight:1.15 }}>{cand.title}</h2>
            <p style={{ margin:0, fontSize:13.5, color:'var(--muted)', lineHeight:1.5, maxWidth:430 }}>{cand.blurb}</p>
            <div style={{ display:'flex', gap:6, flexWrap:'wrap', marginTop:12 }}>
              {cand.threads.map(t => <span key={t} className="pill" style={{ fontSize:10.5 }}>{t}</span>)}
              <span className="pill mono" style={{ fontSize:10.5 }}>{cand.span}</span>
            </div>
          </div>
          <Confidence value={cand.confidence} size={76}/>
        </div>
        {/* member leaves */}
        <div style={{ position:'relative', display:'flex', gap:7, flexWrap:'wrap', marginTop:16 }}>
          {cand.beats.map(b => <LeafChip key={b.id} id={b.id} role={cand.belowThreshold ? null : b.role} onHover={onHover}/>)}
        </div>
        {/* actions */}
        <div style={{ position:'relative', display:'flex', gap:9, marginTop:18, flexWrap:'wrap', alignItems:'center' }}>
          {cand.belowThreshold ? (
            <div style={{ display:'flex', alignItems:'flex-start', gap:9, padding:'11px 13px', background:'var(--sunken)', border:'1px solid var(--hair)', borderRadius:11, fontSize:12.5, color:'var(--muted)', lineHeight:1.5 }}>
              <span style={{ color:'#d97706', flex:'0 0 auto', marginTop:1 }}><I.Clock style={{ fontSize:15 }}/></span>{cand.note}
            </div>
          ) : accepted ? (
            <>
              <span className="pill pill-accent" style={{ padding:'8px 13px', fontSize:13 }}><I.Check style={{ fontSize:14 }}/>Accepted · woven into a Playbook</span>
              <button onClick={onAdvance} className="btn btn-pri press"><I.ArrowRight style={{ fontSize:15 }}/>Arrange the arc</button>
            </>
          ) : (
            <>
              <button onClick={onAccept} className="btn btn-pri press" style={{ padding:'10px 16px' }}><I.Check style={{ fontSize:16 }}/>Accept &amp; weave</button>
              <button className="btn btn-out press"><I.Undo style={{ fontSize:15 }}/>Reshuffle</button>
              <button className="btn btn-ghost press"><I.Close style={{ fontSize:15 }}/>Dismiss</button>
            </>
          )}
        </div>
      </div>

      {/* reasoning: why these + set aside */}
      <div className="lx-cols-main">
        <div className="card-r" style={{ padding:'16px 18px', minWidth:0 }}>
          <SectionHead title="Why these memories?" sub="The signals behind the link"/>
          <div style={{ display:'flex', flexDirection:'column', gap:12 }}>
            {cand.signals.map(s => {
              const Ic = I[s.icon] || I.Sparkles;
              return (
                <div key={s.label}>
                  <div style={{ display:'flex', alignItems:'center', gap:9, marginBottom:5 }}>
                    <span style={{ fontSize:14, color:'var(--accent)', flex:'0 0 auto' }}><Ic/></span>
                    <span style={{ flex:1, fontSize:13, fontWeight:600, color:'var(--ink)' }}>{s.label}</span>
                    <span className="mono tnum" style={{ fontSize:11, color:'var(--faint)' }}>{s.weight.toFixed(2)}</span>
                  </div>
                  <div style={{ fontSize:12, color:'var(--muted)', lineHeight:1.45, marginLeft:23, marginBottom:6 }}>{s.detail}</div>
                  <div className="meter" style={{ marginLeft:23 }}><i style={{ width:`${s.weight*100}%` }}/></div>
                </div>
              );
            })}
          </div>
        </div>
        <div className="card-r" style={{ padding:'16px 18px', minWidth:0 }}>
          <SectionHead title="Set aside" sub="Memories the Weaver chose to leave out"/>
          {cand.excluded.length ? (
            <div style={{ display:'flex', flexDirection:'column', gap:9 }}>
              {cand.excluded.map(x => {
                const e = ev(x.id);
                return (
                  <div key={x.id} style={{ display:'flex', gap:10, alignItems:'flex-start', opacity:0.78 }}>
                    <LeafShape color={leafFor(e.daysSince)} size={16} style={{ filter:'grayscale(0.5)', flexShrink:0 }}/>
                    <div style={{ minWidth:0 }}>
                      <div style={{ fontSize:12.5, color:'var(--ink)', textDecoration:'line-through', textDecorationColor:'var(--ghost)' }}>{e.title}</div>
                      <div style={{ fontSize:11.5, color:'var(--muted)', marginTop:1 }}>{x.reason}</div>
                    </div>
                  </div>
                );
              })}
            </div>
          ) : <div style={{ fontSize:12.5, color:'var(--faint)' }}>Nothing excluded — this cluster is small and tight.</div>}
        </div>
      </div>

      {/* other candidates */}
      <div className="card-r" style={{ padding:'14px 16px' }}>
        <SectionHead title="Other threads the Weaver found" sub="The same memories can form different stories"/>
        <div style={{ display:'flex', gap:10, flexWrap:'wrap' }}>
          {others.map(o => (
            <button key={o.id} onClick={() => onSelect(o.id)} className="press lift" style={{ flex:'1 1 240px', textAlign:'left', cursor:'pointer', padding:'12px 14px', borderRadius:12, background:'var(--raised)', border:'1px solid var(--edge)' }}>
              <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', gap:10 }}>
                <span className="disp" style={{ fontSize:13.5, fontWeight:600, color:'var(--ink)' }}>{o.title}</span>
                <span className="mono tnum" style={{ fontSize:12, fontWeight:600, color: o.belowThreshold ? '#d97706' : 'var(--accent)', flex:'0 0 auto' }}>{o.confidence.toFixed(2)}</span>
              </div>
              <div style={{ fontSize:11.5, color:'var(--muted)', marginTop:4, lineHeight:1.4 }}>{o.beats.length} memories · {o.belowThreshold ? 'below threshold' : 'switch to inspect'}</div>
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

// ── ARC ──────────────────────────────────────────────────────────────────────
function ArcPanel({ cand, order, onMove, onReshuffle, onAdvance, onHover }) {
  const [tone, setTone] = useState('literary');
  const [len, setLen] = useState('chapter');
  return (
    <div className="card-r rise" style={{ padding:'18px 20px' }}>
      <SectionHead title="The arc" count={order.length} sub="The engine assigned each memory a role. Reorder to overrule it." right={
        <button onClick={onReshuffle} className="btn btn-ghost press" style={{ fontSize:12 }}><I.Undo style={{ fontSize:14 }}/>Re-propose</button>
      }/>
      <div style={{ display:'flex', flexDirection:'column', gap:0, position:'relative' }}>
        <div style={{ position:'absolute', left:15, top:16, bottom:16, width:2, background:'var(--accent-line)', borderRadius:2 }}/>
        {order.map((id,i) => {
          const beat = cand.beats.find(b => b.id === id);
          const e = ev(id);
          return (
            <div key={id} onMouseEnter={() => onHover(id)} onMouseLeave={() => onHover(null)} className="rise" style={{ display:'flex', gap:13, alignItems:'center', padding:'9px 8px', borderRadius:11, position:'relative', '--i':i }}>
              <span className="mono" style={{ flex:'0 0 auto', width:30, height:30, borderRadius:9, background:'var(--raised)', border:`2px solid ${leafFor(e.daysSince)}`, display:'grid', placeItems:'center', zIndex:1, color:'var(--muted)', fontWeight:700, fontSize:12 }}>{i+1}</span>
              <span style={{ flex:1, minWidth:0 }}>
                <span className="mono" style={{ fontSize:9, letterSpacing:'0.08em', textTransform:'uppercase', color:'var(--accent)' }}>{beat?.role}</span>
                <span className="disp" style={{ display:'block', fontSize:13.5, fontWeight:550, color:'var(--ink)', textWrap:'pretty' }}>{e.title}</span>
                <span style={{ display:'block', fontSize:11, color:'var(--muted)', marginTop:2 }}>{beat?.note}</span>
              </span>
              <span style={{ display:'flex', flexDirection:'column', gap:2, flex:'0 0 auto' }}>
                <button onClick={() => onMove(i,-1)} disabled={i===0} className="icon-btn press" style={{ width:26, height:22, fontSize:13, opacity:i===0?0.3:1 }}><I.ChevronDown style={{ transform:'rotate(180deg)' }}/></button>
                <button onClick={() => onMove(i,1)} disabled={i===order.length-1} className="icon-btn press" style={{ width:26, height:22, fontSize:13, opacity:i===order.length-1?0.3:1 }}><I.ChevronDown/></button>
              </span>
            </div>
          );
        })}
      </div>
      <div style={{ display:'flex', gap:10, flexWrap:'wrap', alignItems:'center', marginTop:14, paddingTop:14, borderTop:'1px solid var(--hair)' }}>
        <SegTabs size="sm" value={tone} onChange={setTone} tabs={[{id:'plain',label:'Plain'},{id:'literary',label:'Literary'},{id:'spare',label:'Spare'}]}/>
        <SegTabs size="sm" value={len} onChange={setLen} tabs={[{id:'note',label:'Note'},{id:'page',label:'Page'},{id:'chapter',label:'Chapter'}]}/>
        <div style={{ flex:1 }}/>
        <button onClick={onAdvance} className="btn btn-pri press"><I.Pen style={{ fontSize:15 }}/>Draft the prose</button>
      </div>
    </div>
  );
}

// ── DRAFT ────────────────────────────────────────────────────────────────────
function DraftPanel({ cand, shown, total, onAdvance, onHover }) {
  return (
    <div className="card-r rise" style={{ padding:'22px 26px' }}>
      <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:14, flexWrap:'wrap' }}>
        <span className="pill pill-accent"><I.Sparkles style={{ fontSize:11 }}/>Draft</span>
        <span className="mono" style={{ fontSize:11, color:'var(--faint)' }}>{shown} / {total} beats · every line cites a memory</span>
        {shown < total && <span className="breathe" style={{ marginLeft:'auto', width:7, height:7, borderRadius:'50%', background:'var(--accent)' }}/>}
      </div>
      <h2 className="serif" style={{ fontSize:25, fontWeight:600, margin:'0 0 16px', letterSpacing:'-0.01em', lineHeight:1.15 }}>{cand.title}</h2>
      <div className="serif" style={{ fontSize:16.5, lineHeight:1.72, color:'var(--ink)', display:'flex', flexDirection:'column', gap:13 }}>
        {cand.draft.slice(0, shown).map((frag,i) => {
          const e = ev(frag.beat);
          const beat = cand.beats.find(b => b.id === frag.beat);
          return (
            <p key={i} className="rise" onMouseEnter={() => onHover(frag.beat)} onMouseLeave={() => onHover(null)} style={{ margin:0, position:'relative', paddingLeft:14, borderLeft:'2px solid transparent', transition:'border-color .2s', cursor:'default' }}
              onMouseOver={ev2 => ev2.currentTarget.style.borderLeftColor = 'var(--accent-line)'}
              onMouseOut={ev2 => ev2.currentTarget.style.borderLeftColor = 'transparent'}>
              {i===0 && <span className="disp" style={{ float:'left', fontSize:50, lineHeight:0.82, fontWeight:600, marginRight:9, marginTop:5, color:'var(--accent)' }}>{frag.text[0]}</span>}
              {i===0 ? frag.text.slice(1) : frag.text}
              <span title={`from: ${e.title}`} style={{ display:'inline-flex', alignItems:'center', gap:4, marginLeft:7, padding:'1px 7px 1px 5px', borderRadius:7, background:'var(--sunken)', border:'1px solid var(--hair)', verticalAlign:'middle', whiteSpace:'nowrap' }}>
                <LeafShape color={leafFor(e.daysSince)} size={11}/>
                <span className="mono" style={{ fontSize:9, letterSpacing:'0.04em', textTransform:'uppercase', color:'var(--faint)' }}>{beat?.role || 'cited'}</span>
              </span>
            </p>
          );
        })}
        {shown < total && <p style={{ margin:0, paddingLeft:14 }}><span className="mono" style={{ fontSize:13, color:'var(--ghost)' }}>▍</span></p>}
      </div>
      {shown >= total && (
        <div style={{ display:'flex', gap:9, marginTop:20, flexWrap:'wrap', alignItems:'center' }}>
          <button className="btn btn-out press"><I.Pen style={{ fontSize:15 }}/>Edit prose</button>
          <button className="btn btn-ghost press"><I.Audio style={{ fontSize:15 }}/>Read aloud</button>
          <div style={{ flex:1 }}/>
          <button onClick={onAdvance} className="btn btn-pri press"><I.Check style={{ fontSize:16 }}/>Form the Playbook</button>
        </div>
      )}
    </div>
  );
}

// ── FORMED ───────────────────────────────────────────────────────────────────
function FormedPanel({ cand, onReplay }) {
  const [vis, setVis] = useState('scene');
  const words = cand.draft.reduce((a,f) => a + f.text.split(' ').length, 0);
  const scenes = new Set(cand.beats.map(b => ev(b.id).scene)).size;
  const stats = [
    { l:'Memories', v:cand.beats.length, ic:I.Leaf },
    { l:'Scenes linked', v:scenes, ic:I.Layers },
    { l:'Words', v:`~${words}`, ic:I.Pen },
    { l:'Confidence', v:`${Math.round(cand.confidence*100)}%`, ic:I.Sparkles },
  ];
  return (
    <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
      <div className="card-r rise" style={{ overflow:'hidden', position:'relative' }}>
        <div style={{ position:'absolute', inset:0, background:'radial-gradient(70% 90% at 50% 0%, var(--accent-wash), transparent 60%)' }}/>
        <div style={{ position:'relative', padding:'26px 28px', textAlign:'center' }}>
          <span className="pill pill-accent" style={{ marginBottom:12 }}><I.Check style={{ fontSize:12 }}/>Playbook formed</span>
          <h1 className="serif" style={{ fontSize:'clamp(26px,4.4vw,40px)', fontWeight:600, margin:0, letterSpacing:'-0.02em', lineHeight:1.05 }}>{cand.title}</h1>
          <div style={{ display:'flex', justifyContent:'center', gap:6, marginTop:18, flexWrap:'wrap' }}>
            {cand.beats.map((b,i) => <span key={b.id} className="floaty" style={{ animationDelay:`${i*0.3}s` }}><LeafShape color={leafFor(ev(b.id).daysSince)} size={20}/></span>)}
          </div>
        </div>
      </div>
      <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fit, minmax(140px,1fr))', gap:12 }}>
        {stats.map((s,i) => (
          <div key={s.l} className="card-r lift" style={{ padding:15, '--i':i }}>
            <span style={{ fontSize:16, color:'var(--accent)' }}><s.ic/></span>
            <div className="mono" style={{ fontSize:9.5, letterSpacing:'0.08em', textTransform:'uppercase', color:'var(--faint)', marginTop:8 }}>{s.l}</div>
            <div className="disp tnum" style={{ fontSize:22, fontWeight:650, marginTop:2 }}>{s.v}</div>
          </div>
        ))}
      </div>
      <div className="card-r" style={{ padding:'16px 18px', display:'flex', gap:14, alignItems:'center', flexWrap:'wrap' }}>
        <div style={{ flex:1, minWidth:180 }}>
          <SectionHead title="Publish" sub="You control who sees this chapter"/>
          <SegTabs size="sm" value={vis} onChange={setVis} tabs={[{id:'private',label:'Private'},{id:'scene',label:'Scene'},{id:'public',label:'Public'}]}/>
        </div>
        <div style={{ display:'flex', gap:9, flexWrap:'wrap' }}>
          <button onClick={onReplay} className="btn btn-out press"><I.Undo style={{ fontSize:15 }}/>Weave another</button>
          <button className="btn btn-pri press" style={{ padding:'11px 18px' }}><I.Check style={{ fontSize:16 }}/>Publish chapter</button>
        </div>
      </div>
    </div>
  );
}

window.WV = { MethodBadge, Confidence, LeafChip, InboxPanel, ScanPanel, ClusterPanel, ProposePanel, ArcPanel, DraftPanel, FormedPanel };
})();
