// Display label for a node when referenced from another panel (member list,
// note parent link, edge endpoints, etc.). Branches get a "分岐ノード" prefix
// so they're never confused with regular cards even when nameless.
//   regular  → name OR "(無名)"
//   branch   → name ? `分岐ノード(${name})` : '分岐ノード'
function nodeDisplayLabel(n){
  if (!n) return '?';
  const name = (n.name || '').trim();
  if (n.variant === 'branch'){
    return name ? `分岐ノード(${name})` : '分岐ノード';
  }
  return name || '(無名)';
}

// Right-side inspector for either the selected node, edge, group or note
function Inspector({
  selection, setSelection,
  nodes, edges, groups, notes,
  customRelations, learnRelation, removeCustomRelation,
  updateNode, updateEdge,
  deleteNode, deleteEdge,
  duplicateNode,
  addNote, addNoteOnEdge, updateNote, deleteNote,
  addNode, clearAll,
  onLoadDemo,
  createGroupFromNode, updateGroup, deleteGroup, removeNodeFromGroup,
  composingGroupId, setComposingGroupId,
}){
  groups = groups || [];
  notes = notes || [];
  customRelations = customRelations || [];
  if (!selection){
    return (
      <div className="side">
        <div className="card">
          <h3>はじめかた</h3>
          <div style={{fontSize:13, lineHeight:1.7, color:'var(--ink-soft)'}}>
            ・<b style={{color:'var(--gold)'}}>＋カード</b>でカードを置く<br/>
            ・アイコン右上の<b style={{color:'#7FE3A9'}}>＋</b>をドラッグして別カードにドロップ → アクション矢印が引ける<br/>
            ・矢印やカードをクリックで細かく編集（色・スタイル・太さなど）<br/>
            ・<b style={{color:'var(--ink)'}}>● 分岐ノード</b>で 矢印 もしくは ゾーン内 に小さな中継点を追加 → 枝分かれや合流ルート、ゾーン内の状態表現に<br/>
            ・カードに <b style={{color:'var(--red)'}}>✨ エフェクト</b>（強化・ダメージ・回復 等）を付けて状態を可視化
          </div>
        </div>
        <div style={{display:'flex',gap:8, flexDirection:'column'}}>
          <button className="btn primary" onClick={addNode}>＋ カードを追加</button>
          <button className="btn" onClick={onLoadDemo}>💎 サンプル展開を読み込む</button>
          <button className="btn ghost" onClick={clearAll}>🧹 すべてクリア</button>
        </div>
        <div className="empty-hint">
          何も選択されていないよ。<br/>カードや矢印をクリックしてね。
        </div>
      </div>
    );
  }

  if (selection.type === 'node'){
    const node = nodes.find(n=>n.id===selection.id);
    if (!node) return null;
    const nodeGroups = groups.filter(g => (g.nodeIds || []).includes(node.id));
    const nodeNotes = notes.filter(n => n.nodeId === node.id);
    return (
      <div className="side">
        <NodeInspector node={node} updateNode={updateNode} deleteNode={deleteNode}
                       duplicateNode={duplicateNode}
                       nodeGroups={nodeGroups} nodeNotes={nodeNotes}
                       addNote={addNote} updateNote={updateNote} deleteNote={deleteNote}
                       createGroupFromNode={createGroupFromNode}
                       updateGroup={updateGroup} deleteGroup={deleteGroup}
                       removeNodeFromGroup={removeNodeFromGroup}
                       setComposingGroupId={setComposingGroupId}
                       setSelection={setSelection}/>
      </div>
    );
  }

  if (selection.type === 'note'){
    const note = notes.find(n => n.id === selection.id);
    if (!note) return null;
    const parentNode = note.nodeId ? nodes.find(n => n.id === note.nodeId) : null;
    const parentEdge = note.targetEdgeId ? edges.find(e => e.id === note.targetEdgeId) : null;
    return (
      <div className="side">
        <NoteInspector note={note} parentNode={parentNode} parentEdge={parentEdge}
                       nodes={nodes}
                       updateNote={updateNote} deleteNote={deleteNote}
                       setSelection={setSelection}/>
      </div>
    );
  }

  if (selection.type === 'edge'){
    const edge = edges.find(e=>e.id===selection.id);
    if (!edge) return null;
    const edgeNotes = notes.filter(n => n.targetEdgeId === edge.id);
    return (
      <div className="side">
        <EdgeInspector edge={edge} nodes={nodes} updateEdge={updateEdge} deleteEdge={deleteEdge}
                       customRelations={customRelations}
                       learnRelation={learnRelation}
                       removeCustomRelation={removeCustomRelation}
                       edgeNotes={edgeNotes}
                       addNoteOnEdge={addNoteOnEdge}
                       updateNote={updateNote}
                       deleteNote={deleteNote}/>
      </div>
    );
  }

  if (selection.type === 'group'){
    const group = groups.find(g=>g.id===selection.id);
    if (!group) return null;
    return (
      <div className="side">
        <GroupInspector group={group} nodes={nodes}
                        updateGroup={updateGroup} deleteGroup={deleteGroup}
                        removeNodeFromGroup={removeNodeFromGroup}
                        setComposingGroupId={setComposingGroupId}
                        setSelection={setSelection}/>
      </div>
    );
  }
  return null;
}

function NodeInspector({node, updateNode, deleteNode, duplicateNode, nodeGroups, nodeNotes, addNote, updateNote, deleteNote, createGroupFromNode, updateGroup, deleteGroup, removeNodeFromGroup, setComposingGroupId, setSelection}){
  nodeGroups = nodeGroups || [];
  nodeNotes = nodeNotes || [];
  const isBranch = node.variant === 'branch';
  const isEdgeAttached  = isBranch && !!node.attachedEdgeId;
  const fileRef = React.useRef(null);
  const onFile = async (e) => {
    const f = e.target.files?.[0];
    if (!f) return;
    try {
      const dataUrl = await compressImage(f);
      updateNode(node.id, { image: dataUrl });
    } catch(err) {
      console.error('Image compression failed', err);
    }
    e.target.value = '';
  };

  return (
    <>
      <h3>{isBranch ? '分岐ノード編集' : 'カード編集'}</h3>
      <div className="card">
        <div className="field">
          <label>カード名</label>
          <input type="text" value={node.name||''} maxLength={40}
                 onChange={e=>updateNode(node.id,{name:e.target.value})}
                 placeholder="カード名"/>
        </div>
        <div className="field">
          <label>サブ情報（種族・効果メモなど）</label>
          <input type="text" value={node.subtitle||''} maxLength={60}
                 onChange={e=>updateNode(node.id,{subtitle:e.target.value})}
                 placeholder="例: 通常モンスター / 手札 / 場"/>
        </div>
      </div>

      <div className="card mint">
        <h3 style={{marginTop:0}}>{isBranch ? '見た目' : 'カード画像'}</h3>
        {!isBranch && (
          <div style={{display:'flex',gap:6,marginBottom:10, flexWrap:'wrap'}}>
            <button className="btn sm" onClick={()=>fileRef.current.click()}>📷 カード画像をアップ</button>
            {node.image && <button className="btn sm ghost" onClick={()=>updateNode(node.id,{image:null})}>画像クリア</button>}
            <input ref={fileRef} className="file-hidden" type="file" accept="image/*" onChange={onFile}/>
          </div>
        )}
        <div className="field">
          <label>絵文字</label>
          <div className="emoji-row">
            <button className={!node.emoji?'on':''} onClick={()=>updateNode(node.id,{emoji:null})}>—</button>
            {EMOJI_PRESETS.map(em=>(
              <button key={em} className={node.emoji===em?'on':''}
                      onClick={()=>updateNode(node.id,{emoji:em, image:null})}>{em}</button>
            ))}
          </div>
        </div>
        <div className="field">
          <label>カード枠色</label>
          <div className="swatches">
            {NODE_COLORS.map(c=>(
              <button key={c} style={{background:c}}
                      className={node.color===c?'on':''}
                      onClick={()=>updateNode(node.id,{color:c})}/>
            ))}
            <label className={'rainbow-swatch' + (NODE_COLORS.indexOf(node.color)<0 ? ' on' : '')}
                   title="カスタム色を選ぶ"
                   style={{
                     width:30, height:30, borderRadius:'50%',
                     background:'conic-gradient(from 0deg, #f56a92, #ffd97a, #7dd8b4, #8ccbf0, #b7a0ee, #f56a92)',
                     border:'2px solid var(--bg-card)', boxShadow:'0 2px 4px rgba(0,0,0,.4)',
                     cursor:'pointer', position:'relative', display:'inline-block', flexShrink:0,
                   }}>
              <input type="color" value={node.color || '#E9C46A'}
                     onChange={e=>updateNode(node.id,{color:e.target.value},{skipHistory:true})}
                     onBlur={e=>updateNode(node.id,{color:e.target.value})}
                     style={{position:'absolute', inset:0, width:'100%', height:'100%', opacity:0, cursor:'pointer', border:'none', padding:0}}/>
            </label>
          </div>
        </div>
        {!isBranch && (
          <div className="field">
            <label>形</label>
            <div className="seg">
              {[['square','カード型'],['circle','まる'],['bubble','吹き出し']].map(([v,l])=>(
                <button key={v} className={node.shape===v || (!node.shape && v==='circle')?'on':''}
                        onClick={()=>updateNode(node.id,{shape:v})}>{l}</button>
              ))}
            </div>
          </div>
        )}
        {/* Name/subtitle placement — default is below the card. Useful for dense
            layouts where the label would collide with another card or arrow. */}
        <div className="field">
          <label>名前の位置</label>
          <div className="seg">
            {[['top','上'], ['left','左'], ['bottom','下'], ['right','右']].map(([v,l])=>(
              <button key={v} className={(node.labelPos || 'bottom')===v?'on':''}
                      onClick={()=>updateNode(node.id,{labelPos:v})}>{l}</button>
            ))}
          </div>
        </div>
        {/* Effect: rising particles that convey buff / damage / status on the card.
            Migrated from legacy node.aura (boolean) — if aura is true treat it as 'buff'.
            Hidden for branch nodes — the small avatar can't host visible particles. */}
        {!isBranch && (
        <div className="field" style={{borderTop:'1px solid var(--line)', paddingTop:10, marginTop:6, marginBottom:4}}>
          <label>✨ エフェクト</label>
          <div className="seg">
            {[
              ['none',   'なし'],
              ['buff',   '✨ 強化'],
              ['damage', '💥 ダメージ'],
              ['status', '🌀 状態異常'],
              ['heal',   '💚 回復'],
              ['shield', '🛡 防御'],
            ].map(([v,l])=>{
              const cur = node.effect || (node.aura ? 'buff' : 'none');
              return (
                <button key={v} className={cur===v?'on':''}
                        onClick={()=>updateNode(node.id,{effect:v, aura:undefined})}>{l}</button>
              );
            })}
          </div>
        </div>
        )}
      </div>

      <div style={{display:'flex', gap:8}}>
        <button className="btn" style={{flex:1}}
                onClick={()=>duplicateNode && duplicateNode(node.id)}
                title={isBranch ? 'この分岐ノードと同じ設定で新しい分岐ノードを追加' : 'このカードと同じ設定で新しいカードを追加'}>📋 複製</button>
        <button className="btn" style={{flex:1, background:'var(--bg-card)', color:'#FF4545', border:'1px solid #FF4545'}}
                onClick={()=>deleteNode(node.id)}>🗑 削除</button>
      </div>

      {/* Annotations attached to this card. Multi-OK, each is independently
          draggable on the canvas and editable here. */}
      <div className="card" style={{background:'var(--bg-card-soft, #242536)', border:'1px dashed var(--line-strong)'}}>
        <div style={{fontSize:12, fontWeight:800, color:'var(--ink-soft)', marginBottom:8}}>
          💬 解説 {nodeNotes.length > 0 && `(${nodeNotes.length})`}
        </div>
        {nodeNotes.map(n => (
          <div key={n.id} style={{marginBottom:8}}>
            <textarea
              value={n.text}
              placeholder="このカードの役割・注意点などを書く"
              onChange={(e)=>updateNote && updateNote(n.id, {text: e.target.value}, {skipHistory:true})}
              onBlur={(e)=>updateNote && updateNote(n.id, {text: e.target.value})}
              rows={3}
              style={{width:'100%', resize:'vertical', border:'1px solid var(--line)', background:'var(--bg)', color:'var(--ink)', borderRadius:8, padding:'6px 8px', fontSize:12, fontFamily:'inherit', outline:'none', lineHeight:1.5}}/>
            <button className="btn sm ghost"
                    style={{marginTop:4, color:'var(--red)', fontSize:11}}
                    onClick={()=>deleteNote && deleteNote(n.id)}>× この解説を削除</button>
          </div>
        ))}
        <button className="btn sm block" style={{marginTop:4}}
                onClick={()=>addNote && addNote(node.id)}>＋ 解説を追加</button>
      </div>

      {/* Edge-attached branches: surface their arrow anchor explicitly. */}
      {isEdgeAttached && (
        <div className="card" style={{background:'var(--bg-card-soft, #242536)', border:'1px dashed var(--line-strong)'}}>
          <div style={{fontSize:12, fontWeight:800, color:'var(--ink-soft)', marginBottom:8}}>
            🔗 アタッチ先
          </div>
          <div style={{fontSize:12, color:'var(--ink)', lineHeight:1.7}}>
            矢印上に固定(ドラッグで矢印に沿ってスライド)
          </div>
        </div>
      )}

      {/* Group membership — branches are exclusive (single group), regular
          cards can be multi-member. Branches show membership info but the
          inline ✎ / × actions are hidden because removal would orphan them. */}
      {nodeGroups.length > 0 ? (
        <div className="card" style={{background:'var(--bg-card-soft, #242536)', border:'1px dashed var(--line-strong)'}}>
          <div style={{fontSize:12, fontWeight:800, color:'var(--ink-soft)', marginBottom:8}}>
            🫧 所属ゾーン ({nodeGroups.length})
          </div>
          {nodeGroups.map(ng => (
            <div key={ng.id} style={{display:'flex', alignItems:'center', gap:8, marginBottom:6, background:'var(--bg)', color:'var(--ink)', padding:'6px 10px', borderRadius:10, border:'1px solid var(--line)'}}>
              <span style={{width:14, height:14, borderRadius:'50%', background:ng.color, display:'inline-block', border:'2px solid var(--bg-card)', boxShadow:'0 1px 0 rgba(0,0,0,.4)', flexShrink:0}}/>
              <span style={{flex:1, fontWeight:900, fontSize:12, color:'var(--ink)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{ng.name || '(無名)'}</span>
              <button className="btn sm ghost" title="編集"
                      style={{padding:'2px 8px', fontSize:12}}
                      onClick={()=>setSelection && setSelection({type:'group', id:ng.id})}>✎</button>
              {/* Branches can't be removed from their group (single-existence
                  not allowed). Hide the × so the user only deletes the branch
                  itself if they want it gone. */}
              {!isBranch && (
                <button className="btn sm ghost" title="このグループから外す"
                        style={{padding:'2px 8px', fontSize:12, color:'var(--red)'}}
                        onClick={()=>removeNodeFromGroup && removeNodeFromGroup(ng.id, node.id)}>×</button>
              )}
            </div>
          ))}
          {!isBranch && (
            <button className="btn sm block" style={{marginTop:8}}
                    onClick={()=>{
                      const gid = createGroupFromNode && createGroupFromNode(node.id);
                      if (gid) { setComposingGroupId && setComposingGroupId(gid); }
                    }}>
              ＋ 別のゾーンにも入れる
            </button>
          )}
        </div>
      ) : !isBranch ? (
        <button className="btn" style={{background:'var(--bg-lav)', color:'var(--ink)'}}
                onClick={()=>{
                  const gid = createGroupFromNode && createGroupFromNode(node.id);
                  if (gid) { setComposingGroupId && setComposingGroupId(gid); }
                }}>
          🫧 このカードをゾーンにまとめる
        </button>
      ) : null}
    </>
  );
}

function GroupInspector({group, nodes, updateGroup, deleteGroup, removeNodeFromGroup, setComposingGroupId, setSelection}){
  const members = (group.nodeIds || [])
    .map(nid => nodes.find(n => n.id === nid))
    .filter(Boolean);
  const presets = ['#E9C46A','#F4A261','#6DD3F0','#7FE3A9','#FF6B9D','#C77DFF','#B39DDB'];

  return (
    <>
      <h3>🫧 ゾーン / グループ編集</h3>
      <div className="card">
        <div className="field">
          <label>ゾーン / グループ名</label>
          <input type="text" value={group.name||''} maxLength={40}
                 onChange={e=>updateGroup(group.id, {name:e.target.value})}
                 placeholder="手札 / 場 / 墓地 / 最終盤面 など"/>
        </div>
        <label className="pop-check" style={{borderTop:'none', padding:0}}>
          <input type="checkbox" checked={group.showName !== false}
                 onChange={e=>updateGroup(group.id, {showName:e.target.checked})}/>
          名前をキャンバスに表示
        </label>
      </div>

      <div className="card mint">
        <h3 style={{marginTop:0}}>色</h3>
        <div className="swatches">
          {presets.map(c=>(
            <button key={c} style={{background:c}}
                    className={group.color===c?'on':''}
                    onClick={()=>updateGroup(group.id,{color:c})}/>
          ))}
          <label className={'rainbow-swatch' + (presets.indexOf(group.color)<0 ? ' on' : '')}
                 title="カスタム色を選ぶ"
                 style={{
                   width:30, height:30, borderRadius:'50%',
                   background:'conic-gradient(from 0deg, #f56a92, #ffd97a, #7dd8b4, #8ccbf0, #b7a0ee, #f56a92)',
                   border:'2px solid var(--bg-card)', boxShadow:'0 2px 4px rgba(0,0,0,.4)',
                   cursor:'pointer', position:'relative', display:'inline-block', flexShrink:0,
                 }}>
            <input type="color" value={group.color || '#E9C46A'}
                   onChange={e=>updateGroup(group.id,{color:e.target.value},{skipHistory:true})}
                   onBlur={e=>updateGroup(group.id,{color:e.target.value})}
                   style={{position:'absolute', inset:0, width:'100%', height:'100%', opacity:0, cursor:'pointer', border:'none', padding:0}}/>
          </label>
        </div>
      </div>

      <div className="card lav">
        <h3 style={{marginTop:0}}>メンバー ({members.length})</h3>
        <div style={{display:'flex', flexDirection:'column', gap:6}}>
          {members.map(n => {
            const isBranchMember = n.variant === 'branch';
            return (
              <div key={n.id} style={{display:'flex', alignItems:'center', gap:8, background:'var(--bg)', color:'var(--ink)', padding:'6px 10px', borderRadius:10, border:'1px solid var(--line)'}}>
                <span style={{width:22, height:22, borderRadius:'50%', background:n.color, display:'grid', placeItems:'center', fontSize:14, color:'#0F1115', fontWeight:900}}>
                  {n.emoji || (n.name||'?').charAt(0).toUpperCase()}
                </span>
                <span style={{flex:1, fontSize:13, fontWeight:700, color:'var(--ink)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{nodeDisplayLabel(n)}</span>
                {/* Branches can't be removed from a group — single-existence
                    not allowed. Hide the × so the only path is to delete the
                    branch itself. */}
                {!isBranchMember && (
                  <button className="btn sm ghost" title="グループから外す"
                          onClick={()=>removeNodeFromGroup && removeNodeFromGroup(group.id, n.id)}>×</button>
                )}
              </div>
            );
          })}
        </div>
        <button className="btn sm block" style={{marginTop:10}}
                onClick={()=>{ setComposingGroupId && setComposingGroupId(group.id); }}>
          ＋ メンバーを追加・編集
        </button>
      </div>

      <button className="btn" style={{background:'var(--bg-card)', color:'#FF4545', border:'1px solid #FF4545'}}
              onClick={()=>deleteGroup(group.id)}>🗑 グループを削除</button>
    </>
  );
}

function EdgeInspector({edge, nodes, updateEdge, deleteEdge, customRelations, learnRelation, removeCustomRelation, edgeNotes, addNoteOnEdge, updateNote, deleteNote}){
  edgeNotes = edgeNotes || [];
  const from = nodes.find(n=>n.id===edge.from);
  const to = nodes.find(n=>n.id===edge.to);
  const rel = RELATIONS.find(r=>r.id===edge.relation) || RELATIONS[0];
  customRelations = customRelations || [];
  // Track which chip is "just added" so it can pulse — the silent confirmation
  // that a new term got registered.
  const prevIdsRef = React.useRef(new Set(customRelations.map(r=>r.id)));
  const [justAddedId, setJustAddedId] = React.useState(null);
  React.useEffect(()=>{
    const currentIds = new Set(customRelations.map(r=>r.id));
    const newIds = [...currentIds].filter(id => !prevIdsRef.current.has(id));
    prevIdsRef.current = currentIds;
    if (newIds.length > 0) {
      const id = newIds[newIds.length - 1];
      setJustAddedId(id);
      const t = setTimeout(()=>setJustAddedId(null), 1300);
      return () => clearTimeout(t);
    }
  }, [customRelations]);

  // When a custom preset is clicked we bake its label/color/stamp into the edge
  // directly — this keeps customRelations as pure UI shortcuts. Deleting a
  // custom later never breaks edges that used it.
  const applyCustomPreset = (c) => {
    updateEdge(edge.id, { label: c.label, color: c.color, stamp: c.stamp || '', relation: 'custom' });
  };
  // Explicit "register this term" action — replaces the old auto-learn-on-blur
  // behavior because users couldn't tell when learning was happening.
  const onRegisterClick = () => {
    if (!learnRelation) return;
    const v = edge.label ?? rel.label;
    learnRelation(v, edge.color || rel.color, edge.stamp ?? rel.stamp);
  };

  return (
    <>
      <h3>矢印 / アクション編集</h3>
      <div className="card lav">
        <div style={{fontSize:13, fontWeight:800, marginBottom:8}}>
          <span style={{color:'var(--ink-soft)'}}>from</span> {nodeDisplayLabel(from)}
          <span style={{margin:'0 6px'}}>→</span>
          <span style={{color:'var(--ink-soft)'}}>to</span> {nodeDisplayLabel(to)}
          {' '}
          <button className="btn sm ghost" title="向きを入れ替え"
                  onClick={()=>updateEdge(edge.id,{from:edge.to,to:edge.from})}>⇄</button>
        </div>

        <div className="field">
          <label>アクション</label>
          <div className="seg">
            {RELATIONS.map(r=>(
              <button key={r.id} className={edge.relation===r.id?'on':''}
                      onClick={()=>updateEdge(edge.id,{relation:r.id, color:r.color, label:r.label})}>
                {r.stamp} {r.label}
              </button>
            ))}
          </div>
          {/* User-learned vocabulary: chips populated automatically when the
              label field is edited. × removes only the chip, never the edges
              that used it (label/color/stamp are baked in at apply time). */}
          {customRelations.length > 0 && (
            <>
              <div style={{fontSize:10, fontWeight:800, color:'var(--ink-dim)', letterSpacing:'.08em', margin:'8px 0 4px'}}>
                ─── 覚えた用語 ───
              </div>
              <div className="seg">
                {customRelations.map(c => (
                  <span key={c.id} className={`custom-rel-chip${justAddedId===c.id?' just-added':''}`} style={{position:'relative'}}>
                    <button
                      className={edge.label===c.label && edge.color===c.color ? 'on' : ''}
                      onClick={()=>applyCustomPreset(c)}
                      title={`${c.stamp?c.stamp+' ':''}${c.label}`}
                      style={{paddingRight:20}}>
                      {c.stamp} {c.label}
                    </button>
                    <button className="custom-rel-x"
                            title="この用語を削除"
                            onClick={(e)=>{e.stopPropagation(); removeCustomRelation && removeCustomRelation(c.id);}}>×</button>
                  </span>
                ))}
              </div>
            </>
          )}
        </div>

        <div className="field">
          <label>ラベル（自由入力 / 手順番号など）</label>
          <div style={{display:'flex', gap:6}}>
            <input type="text" value={edge.label ?? rel.label} maxLength={40}
                   style={{flex:1}}
                   onChange={e=>updateEdge(edge.id,{label:e.target.value})}/>
            <button className="btn sm" title="このラベル＋色＋スタンプを用語として登録"
                    onClick={onRegisterClick}
                    style={{whiteSpace:'nowrap'}}>🏷 用語登録</button>
          </div>
        </div>

        <div className="field">
          <label>スタンプ</label>
          <div className="stamp-row">
            <button className={!edge.stamp?'on':''}
                    onClick={()=>updateEdge(edge.id,{stamp:''})}>なし</button>
            {STAMPS.map(s=>(
              <button key={s} className={edge.stamp===s?'on':''}
                      onClick={()=>updateEdge(edge.id,{stamp:s})}>{s}</button>
            ))}
          </div>
        </div>
      </div>

      <div className="card sky">
        <h3 style={{marginTop:0}}>矢印のスタイル</h3>
        <div className="seg">
          {ARROW_STYLES.map(s=>(
            <button key={s.id} className={edge.style===s.id?'on':''}
                    onClick={()=>updateEdge(edge.id,{style:s.id})}>{s.label}</button>
          ))}
        </div>
        {/* Thickness — "thick" also makes the energy flow faster for visual weight */}
        <div className="field" style={{marginTop:10}}>
          <label>線の太さ</label>
          <div className="seg">
            {[['normal','標準'], ['thick','太い']].map(([v,l])=>(
              <button key={v} className={(edge.thickness||'normal')===v?'on':''}
                      onClick={()=>updateEdge(edge.id,{thickness:v})}>{l}</button>
            ))}
          </div>
        </div>
        <div className="field" style={{marginTop:10}}>
          <label>線の色</label>
          <div className="swatches">
            {(() => {
              const presets = ['#F4A261','#6DD3F0','#E9C46A','#7FE3A9','#FF6B9D','#FF4545','#8B8E9C','#B39DDB','#C77DFF'];
              return (
                <>
                  {presets.map(c=>(
                    <button key={c} style={{background:c}}
                            className={edge.color===c?'on':''}
                            onClick={()=>updateEdge(edge.id,{color:c})}/>
                  ))}
                  <label className={'rainbow-swatch' + (presets.indexOf(edge.color)<0 ? ' on' : '')}
                         title="カスタム色を選ぶ"
                         style={{
                           width:30, height:30, borderRadius:'50%',
                           background:'conic-gradient(from 0deg, #f56a92, #ffd97a, #7dd8b4, #8ccbf0, #b7a0ee, #f56a92)',
                           border:'2px solid var(--bg-card)', boxShadow:'0 2px 4px rgba(0,0,0,.4)',
                           cursor:'pointer', position:'relative', display:'inline-block', flexShrink:0,
                         }}>
                    <input type="color" value={edge.color || '#E9C46A'}
                           onChange={e=>updateEdge(edge.id,{color:e.target.value},{skipHistory:true})}
                           onBlur={e=>updateEdge(edge.id,{color:e.target.value})}
                           style={{position:'absolute', inset:0, width:'100%', height:'100%', opacity:0, cursor:'pointer', border:'none', padding:0}}/>
                  </label>
                </>
              );
            })()}
          </div>
        </div>
        {edge.customOffset !== undefined && edge.customOffset !== 0 && (
          <button className="btn sm" style={{marginTop:8}}
                  onClick={()=>updateEdge(edge.id,{customOffset:undefined})}>↺ 曲げをリセット</button>
        )}
      </div>

      {/* Annotations attached to this arrow. Multi-OK, each is independently
          draggable on the canvas and editable here. Mirrors the node-side
          解説 UI for consistency. */}
      <div className="card" style={{background:'var(--bg-card-soft, #242536)', border:'1px dashed var(--line-strong)'}}>
        <div style={{fontSize:12, fontWeight:800, color:'var(--ink-soft)', marginBottom:8}}>
          💬 解説 {edgeNotes.length > 0 && `(${edgeNotes.length})`}
        </div>
        {edgeNotes.map(n => (
          <div key={n.id} style={{marginBottom:8}}>
            <textarea
              value={n.text}
              placeholder="この矢印アクションの意図・タイミング・条件などを書く"
              onChange={(e)=>updateNote && updateNote(n.id, {text: e.target.value}, {skipHistory:true})}
              onBlur={(e)=>updateNote && updateNote(n.id, {text: e.target.value})}
              rows={3}
              style={{width:'100%', resize:'vertical', border:'1px solid var(--line)', background:'var(--bg)', color:'var(--ink)', borderRadius:8, padding:'6px 8px', fontSize:12, fontFamily:'inherit', outline:'none', lineHeight:1.5}}/>
            <button className="btn sm ghost"
                    style={{marginTop:4, color:'var(--red)', fontSize:11}}
                    onClick={()=>deleteNote && deleteNote(n.id)}>× この解説を削除</button>
          </div>
        ))}
        <button className="btn sm block" style={{marginTop:4}}
                onClick={()=>addNoteOnEdge && addNoteOnEdge(edge.id)}>＋ 解説を追加</button>
      </div>

      <button className="btn" style={{background:'var(--bg-card)', color:'#FF4545', border:'1px solid #FF4545'}}
              onClick={()=>deleteEdge(edge.id)}>🗑 この矢印を削除</button>

      <div style={{fontSize:11, color:'var(--ink-soft)', textAlign:'center', marginTop:4, lineHeight:1.6}}>
        💡 別の矢印を <b>Shift+クリック</b> で重なり順を入れ替え
      </div>
    </>
  );
}

// Shown when a note itself is selected (either by clicking it on the canvas or
// right after creating one). Lets the user edit the text directly and jump back
// to the owning card or arrow.
function NoteInspector({ note, parentNode, parentEdge, nodes, updateNote, deleteNote, setSelection }){
  // For edge-attached notes, surface the from/to nodes so the user can see
  // which arrow this note describes without having to click around.
  const edgeFrom = parentEdge ? nodes.find(n => n.id === parentEdge.from) : null;
  const edgeTo   = parentEdge ? nodes.find(n => n.id === parentEdge.to)   : null;
  const placeholder = parentEdge
    ? 'この矢印アクションの意図・タイミング・条件などを自由に'
    : 'このカードの役割・注意点・代替案などを自由に';

  return (
    <>
      <div className="card">
        <h3 style={{marginTop:0}}>💬 解説</h3>
        {parentNode && (
          <div style={{fontSize:12, color:'var(--ink-soft)', marginBottom:10}}>
            対象カード：
            <a href="#" onClick={(e)=>{e.preventDefault(); setSelection && setSelection({type:'node', id:parentNode.id});}}
               style={{color:'var(--gold)', fontWeight:700}}>
              {nodeDisplayLabel(parentNode)}
            </a>
          </div>
        )}
        {parentEdge && (
          <div style={{fontSize:12, color:'var(--ink-soft)', marginBottom:10}}>
            対象矢印：
            <a href="#" onClick={(e)=>{e.preventDefault(); setSelection && setSelection({type:'edge', id:parentEdge.id});}}
               style={{color:'var(--gold)', fontWeight:700}}>
              {nodeDisplayLabel(edgeFrom)} → {nodeDisplayLabel(edgeTo)}
              {parentEdge.label ? <span style={{color:'var(--ink-dim)'}}> （{parentEdge.label}）</span> : null}
            </a>
          </div>
        )}
        {!parentNode && !parentEdge && (
          <div style={{fontSize:12, color:'var(--red)', marginBottom:10}}>対象が見つかりません</div>
        )}
        <div className="field">
          <label>本文</label>
          <textarea
            value={note.text}
            placeholder={placeholder}
            onChange={(e)=>updateNote && updateNote(note.id, {text: e.target.value}, {skipHistory:true})}
            onBlur={(e)=>updateNote && updateNote(note.id, {text: e.target.value})}
            rows={6}
            autoFocus
            style={{width:'100%', resize:'vertical', border:'1px solid var(--line)', background:'var(--bg)', color:'var(--ink)', borderRadius:8, padding:'8px 10px', fontSize:13, fontFamily:'inherit', outline:'none', lineHeight:1.6}}/>
        </div>
        <div style={{fontSize:11, color:'var(--ink-dim)', marginTop:8}}>
          ※ ドラッグで位置移動、ツールバー「背景・装飾」から一括で表示 ON/OFF 可能
        </div>
      </div>
      <button className="btn" style={{background:'var(--bg-card)', color:'#FF4545', border:'1px solid #FF4545'}}
              onClick={()=>deleteNote && deleteNote(note.id)}>🗑 この解説を削除</button>
    </>
  );
}

window.Inspector = Inspector;
