/* learn.jsx v2 — YouTube embeds, inline term highlighting, more terms */

// Feature flag — AI tutor (Claude) is disabled in self-hosted builds.
// To enable as a Pro/paid feature, see docs/AI_TUTOR_PAID_FEATURE.md
const AI_TUTOR_ENABLED = false;

// ===== INLINE TERM HIGHLIGHTER (also wraps user notes as <mark>) =====
function highlightTerms(text, termsMap, onTerm, usedTerms, notes, onNoteClick) {
  if (!text) return text;
  const terms = Object.keys(termsMap||{})
    .filter(t => !usedTerms.has(t.toLowerCase()))
    .sort((a,b) => b.length - a.length);
  if (!terms.length && !(notes && notes.length)) return text;

  const parts = []; let remaining = text; let key = 0;
  const usedHere = new Set();

  while (remaining.length > 0) {
    let bestIdx = -1, bestLen = 0, bestKind = null, bestPayload = null;
    const lower = remaining.toLowerCase();
    for (const t of terms) {
      if (usedHere.has(t.toLowerCase())) continue;
      const idx = lower.indexOf(t.toLowerCase());
      if (idx !== -1 && (bestIdx === -1 || idx < bestIdx || (idx === bestIdx && t.length > bestLen))) {
        bestIdx = idx; bestLen = t.length; bestKind = 'term'; bestPayload = t;
      }
    }
    if (notes && notes.length) {
      for (const n of notes) {
        const idx = lower.indexOf(n.selectedText.toLowerCase());
        if (idx !== -1 && (bestIdx === -1 || idx < bestIdx || (idx === bestIdx && n.selectedText.length > bestLen))) {
          bestIdx = idx; bestLen = n.selectedText.length; bestKind = 'note'; bestPayload = n;
        }
      }
    }
    if (bestIdx === -1) { parts.push(remaining); break; }
    if (bestIdx > 0) parts.push(remaining.slice(0, bestIdx));
    const matched = remaining.slice(bestIdx, bestIdx + bestLen);
    if (bestKind === 'term') {
      usedHere.add(bestPayload.toLowerCase());
      usedTerms.add(bestPayload.toLowerCase());
      parts.push(<button key={key++} className="term" onClick={()=>onTerm({term:bestPayload,def:termsMap[bestPayload]})}>{matched}</button>);
    } else {
      const n = bestPayload;
      parts.push(<span key={key++} className="note-mark" onClick={e=>{e.stopPropagation();onNoteClick&&onNoteClick(n,e);}}>{matched}</span>);
    }
    remaining = remaining.slice(bestIdx + bestLen);
  }
  return <>{parts}</>;
}

// ===== NOTES INFRASTRUCTURE =====
const NOTES_KEY='ccna_notes_v1';
function readNotes(){try{return JSON.parse(localStorage.getItem(NOTES_KEY))||[];}catch{return[];}}
function writeNotes(n){localStorage.setItem(NOTES_KEY,JSON.stringify(n));window.dispatchEvent(new Event('ccna-notes-change'));}
function useNotes(){
  const[notes,setNotes]=React.useState(readNotes);
  React.useEffect(()=>{
    const h=()=>setNotes(readNotes());
    window.addEventListener('ccna-notes-change',h);
    return()=>window.removeEventListener('ccna-notes-change',h);
  },[]);
  return notes;
}
function addNote(topicId,topicTitle,selectedText,noteText){
  const all=readNotes();
  all.push({id:Date.now()+'_'+Math.random().toString(36).slice(2,8),topicId,topicTitle,selectedText,note:noteText,createdAt:new Date().toISOString()});
  writeNotes(all);
}
function updateNote(id,noteText){
  const all=readNotes();
  const i=all.findIndex(n=>n.id===id);
  if(i<0)return;
  all[i].note=noteText; all[i].updatedAt=new Date().toISOString();
  writeNotes(all);
}
function deleteNote(id){writeNotes(readNotes().filter(n=>n.id!==id));}
Object.assign(window,{useNotes,addNote,updateNote,deleteNote});

// ===== READ-TRACKING =====
const READ_KEY_PREFIX='ccna_read_';
function readReadTopics(examId){try{return JSON.parse(localStorage.getItem(READ_KEY_PREFIX+examId))||[];}catch{return [];}}
function writeReadTopics(examId,list){localStorage.setItem(READ_KEY_PREFIX+examId,JSON.stringify(list));window.dispatchEvent(new Event('ccna-read-change'));}
function useReadTopics(examId){
  const[list,setList]=React.useState(()=>readReadTopics(examId));
  React.useEffect(()=>{setList(readReadTopics(examId));const h=()=>setList(readReadTopics(examId));window.addEventListener('ccna-read-change',h);return()=>window.removeEventListener('ccna-read-change',h);},[examId]);
  return list;
}
function markRead(examId,topicId){const cur=readReadTopics(examId);if(!cur.includes(topicId))writeReadTopics(examId,[...cur,topicId]);}
function unmarkRead(examId,topicId){writeReadTopics(examId,readReadTopics(examId).filter(id=>id!==topicId));}

// ===== SELECTION BUBBLE + NOTE POPOVERS =====
function useSelectionBubble(containerSel){
  const[bubble,setBubble]=React.useState(null);
  React.useEffect(()=>{
    let t;
    const onMU=()=>{
      clearTimeout(t);
      t=setTimeout(()=>{
        const sel=window.getSelection();
        if(!sel||sel.isCollapsed){setBubble(null);return;}
        const text=sel.toString().trim();
        if(!text||text.length<3||text.length>500){setBubble(null);return;}
        const range=sel.getRangeAt(0);
        const container=document.querySelector(containerSel);
        if(!container||!container.contains(range.commonAncestorContainer)){setBubble(null);return;}
        const r=range.getBoundingClientRect();
        setBubble({text,x:r.left+r.width/2,y:r.bottom+6});
      },100);
    };
    document.addEventListener('mouseup',onMU);
    return()=>{document.removeEventListener('mouseup',onMU);clearTimeout(t);};
  },[containerSel]);
  return[bubble,()=>{setBubble(null);window.getSelection&&window.getSelection().removeAllRanges();}];
}

function SelectionBubble({bubble,onAdd}){
  if(!bubble)return null;
  return(<div className="sel-bubble" style={{left:bubble.x,top:bubble.y}}
    onMouseDown={e=>e.preventDefault()} onClick={onAdd}>✎ Add note</div>);
}

function NoteInput({selectedText,initialText,onSave,onCancel,title}){
  const[text,setText]=React.useState(initialText||'');
  const ref=React.useRef(null);
  React.useEffect(()=>{ref.current?.focus();},[]);
  return(<div className="modal-overlay" onClick={onCancel}>
    <div className="modal-card" onClick={e=>e.stopPropagation()} style={{maxWidth:'560px'}}>
      <div className="modal-title">{title||'Add Note'}</div>
      <div className="modal-sub">Highlighted text:</div>
      <div style={{background:'oklch(0.82 0.14 80 / 0.18)',color:'var(--amber)',padding:'8px 11px',borderRadius:'var(--r)',fontSize:'12.5px',marginBottom:'14px',maxHeight:'90px',overflow:'auto',lineHeight:1.5,fontStyle:'italic'}}>“{selectedText}”</div>
      <textarea ref={ref} className="chat-in" rows={5} value={text}
        onChange={e=>setText(e.target.value)}
        onKeyDown={e=>{if(e.key==='Enter'&&(e.metaKey||e.ctrlKey)){e.preventDefault();text.trim()&&onSave(text.trim());}}}
        placeholder="Type your note… (Cmd/Ctrl+Enter to save)" style={{width:'100%',fontSize:'13.5px',padding:'10px 12px'}}/>
      <div style={{display:'flex',gap:'8px',marginTop:'14px',justifyContent:'flex-end'}}>
        <button className="btn btn-ghost btn-sm" onClick={onCancel}>Cancel</button>
        <button className="btn btn-primary" onClick={()=>text.trim()&&onSave(text.trim())} disabled={!text.trim()}>Save Note</button>
      </div>
    </div>
  </div>);
}

function NotePopover({note,pos,onEdit,onDelete,onClose}){
  return(<>
    <div onClick={onClose} style={{position:'fixed',inset:0,zIndex:65}}/>
    <div className="note-pop" style={{left:pos.x,top:pos.y+6}}>
      <div style={{fontSize:'10px',color:'var(--purple)',textTransform:'uppercase',letterSpacing:'0.08em',marginBottom:'6px',fontWeight:600}}>Note</div>
      <div style={{fontSize:'13px',color:'var(--fg-hi)',lineHeight:1.55,marginBottom:'10px',whiteSpace:'pre-wrap'}}>{note.note}</div>
      <div style={{fontSize:'10px',color:'var(--fg-mute)',marginBottom:'10px'}}>{new Date(note.createdAt).toLocaleDateString()}{note.updatedAt?' · edited':''}</div>
      <div style={{display:'flex',gap:'6px',justifyContent:'flex-end'}}>
        <button className="btn btn-ghost btn-sm" onClick={onEdit}>Edit</button>
        <button className="btn btn-ghost btn-sm btn-danger" onClick={onDelete}>Delete</button>
      </div>
    </div>
  </>);
}

// Build termsMap from concept sections
function buildTermsMap(sections) {
  const map = {};
  (sections||[]).forEach(s => {
    if (s.type === 'terms') s.items.forEach(t => { map[t.term] = t.def; });
  });
  return map;
}

// ===== INTERACTIVE DIAGRAMS — inline expansion =====
function OsiDiagram(){
  const[exp,setExp]=React.useState(null);
  const layers=[
    {n:7,name:"Application",pdu:"Data",ex:"HTTP, DNS, SMTP",c:"oklch(0.78 0.10 200)",def:"User-facing protocols that produce or consume data. HTTP serves web pages, DNS resolves names, SMTP sends email, FTP transfers files. PDU = Data."},
    {n:6,name:"Presentation",pdu:"Data",ex:"TLS, JPEG",c:"oklch(0.87 0.26 148)",def:"Formats, compresses, and encrypts data. TLS/SSL provides encryption. JPEG/MPEG/ASCII are encoding standards. PDU = Data."},
    {n:5,name:"Session",pdu:"Data",ex:"NetBIOS, RPC",c:"oklch(0.72 0.12 245)",def:"Manages dialog control and session establishment between applications. Handles checkpointing and graceful close. PDU = Data."},
    {n:4,name:"Transport",pdu:"Segment/Datagram",ex:"TCP, UDP",c:"oklch(0.82 0.14 80)",def:"End-to-end reliable delivery. TCP = connection-oriented with 3-way handshake, ACKs, sequencing, and retransmits. UDP = connectionless and fast but unreliable. Uses port numbers. PDU = Segment (TCP) / Datagram (UDP)."},
    {n:3,name:"Network",pdu:"Packet",ex:"IP, OSPF, BGP",c:"oklch(0.68 0.18 30)",def:"Logical addressing and routing. IP delivers packets across networks. OSPF, EIGRP, BGP are routing protocols. Routers operate here. PDU = Packet."},
    {n:2,name:"Data Link",pdu:"Frame",ex:"Ethernet, 802.1Q",c:"oklch(0.72 0.12 295)",def:"Frame delivery between adjacent nodes on a single link. Uses MAC addresses for hop-by-hop forwarding. Switches operate here. 802.1Q adds VLAN tagging. PDU = Frame."},
    {n:1,name:"Physical",pdu:"Bit",ex:"Cables, Hubs",c:"oklch(0.55 0.06 148)",def:"Physical media — copper cables, fiber optics, connectors, hubs, signal voltage levels, light pulses. Just bits on the wire. PDU = Bit."}
  ];
  return(
    <div style={{display:'flex',flexDirection:'column',gap:'2px'}}>
      {layers.map(l=>(
        <React.Fragment key={l.n}>
          <button className="osi-row" onClick={()=>setExp(exp===l.n?null:l.n)}
            style={{display:'grid',gridTemplateColumns:'40px 1fr 140px 150px',alignItems:'stretch',padding:0,background:'var(--raised)',border:'1px solid var(--border)',borderRadius:'3px',cursor:'pointer',font:'inherit',color:'inherit',textAlign:'left',transition:'all 100ms'}}>
            <span style={{background:l.c,color:'var(--bg)',padding:'9px 0',textAlign:'center',fontWeight:700,fontSize:'14px'}}>{l.n}</span>
            <span style={{color:'var(--fg-hi)',fontSize:'12.5px',fontWeight:600,padding:'9px 12px'}}>{l.name}</span>
            <span style={{color:l.c,fontSize:'11px',padding:'9px 4px'}}>{l.pdu}</span>
            <span style={{color:'var(--fg-mute)',fontSize:'11px',padding:'9px 12px 9px 4px'}}>{l.ex}</span>
          </button>
          {exp===l.n&&(
            <div style={{padding:'10px 14px',marginLeft:'24px',marginRight:'2px',marginBottom:'2px',background:'var(--surface)',borderLeft:`3px solid ${l.c}`,borderRadius:'0 var(--r) var(--r) 0',color:'var(--fg-dim)',fontSize:'12.5px',lineHeight:1.6,animation:'expand-in 180ms ease-out'}}>
              {l.def}
            </div>
          )}
        </React.Fragment>
      ))}
    </div>
  );
}

function DiagramExpansion({exp,onClose}){
  if(!exp)return null;
  return(
    <div style={{marginTop:'10px',padding:'10px 14px',background:'var(--surface)',borderLeft:`3px solid ${exp.color||'var(--green)'}`,borderRadius:'0 var(--r) var(--r) 0',animation:'expand-in 180ms ease-out',position:'relative'}}>
      <button onClick={onClose} style={{position:'absolute',top:6,right:8,background:'transparent',border:0,color:'var(--fg-mute)',fontSize:'14px',cursor:'pointer'}}>✕</button>
      <div style={{color:exp.color||'var(--green)',fontWeight:600,marginBottom:'5px',fontSize:'12px',letterSpacing:'0.02em'}}>{exp.label}</div>
      <div style={{color:'var(--fg-dim)',fontSize:'12.5px',lineHeight:1.6,paddingRight:'20px'}}>{exp.def}</div>
    </div>
  );
}

function VlanDiagram(){
  const[exp,setExp]=React.useState(null);
  const W=500,H=230;
  const vlans=[
    {id:10,y:50,color:"oklch(0.78 0.10 200)",label:"VLAN 10",def:"VLAN 10 — Engineering broadcast domain. Hosts only see broadcasts from other VLAN 10 members. To reach VLAN 20/30, traffic must route through a Layer 3 device."},
    {id:20,y:115,color:"oklch(0.82 0.14 80)",label:"VLAN 20",def:"VLAN 20 — Finance broadcast domain. Isolated from VLAN 10 and 30 at Layer 2. Configured via `switchport access vlan 20` on each access port."},
    {id:30,y:180,color:"oklch(0.68 0.18 30)",label:"VLAN 30",def:"VLAN 30 — Guest broadcast domain. Often paired with restrictive ACLs to limit guest access to corporate resources."}
  ];
  return(<div>
    <svg viewBox={`0 0 ${W} ${H}`} style={{fontFamily:'var(--font)',maxWidth:'100%'}}>
      <g className="diagram-node" onClick={()=>setExp({label:"Layer 2 Switch",def:"Switches forward Ethernet frames using MAC addresses. Each VLAN is a separate broadcast domain. The switch maintains a CAM table mapping MAC → port. Inter-VLAN traffic needs a Layer 3 device.",color:"var(--green)"})} style={{cursor:'pointer'}}>
        <rect x={200} y={90} width={100} height={40} rx={3} fill="var(--surface)" stroke="var(--green)" strokeWidth={1.5}/>
        <text x={250} y={115} textAnchor="middle" fill="var(--green)" fontSize={12} fontWeight={600}>Switch</text>
      </g>
      {vlans.map(v=>(<g key={v.id} className="diagram-node"
          onClick={()=>setExp({label:v.label,def:v.def,color:v.color})} style={{cursor:'pointer'}}>
        <line x1={200} y1={110} x2={130} y2={v.y} stroke={v.color} strokeWidth={1.5}/>
        <rect x={50} y={v.y-13} width={76} height={25} rx={3} fill="var(--raised)" stroke={v.color} strokeWidth={1}/>
        <text x={88} y={v.y+5} textAnchor="middle" fill={v.color} fontSize={11} fontWeight={600}>{v.label}</text>
        <line x1={300} y1={110} x2={380} y2={v.y} stroke={v.color} strokeWidth={1} strokeDasharray="3,2"/>
        {[0,1].map(ci=><circle key={ci} cx={420+ci*22} cy={v.y} r={7} fill="var(--raised)" stroke={v.color} strokeWidth={1}/>)}
      </g>))}
      <text x={250} y={16} textAnchor="middle" fill="var(--fg-mute)" fontSize={10} textTransform="uppercase">VLAN Segmentation — click any element</text>
    </svg>
    <DiagramExpansion exp={exp} onClose={()=>setExp(null)}/>
  </div>);
}

function StpDiagram(){
  const[exp,setExp]=React.useState(null);
  const W=500,H=230;
  const switches=[
    {id:"SW1",x:190,y:20,label:"Root",c:"var(--green)",def:"SW1 — Root Bridge. Elected because it has the LOWEST Bridge ID (priority + MAC). All its ports are designated (forwarding). All other switches compute paths toward it."},
    {id:"SW2",x:40,y:155,label:"",c:"var(--fg-dim)",def:"SW2 — Non-root switch. Has a root port toward SW1 and a designated port on the SW2–SW3 segment."},
    {id:"SW3",x:350,y:155,label:"",c:"var(--fg-dim)",def:"SW3 — Non-root switch. Has a root port toward SW1. The SW2–SW3 link is BLOCKED on this side to break the loop."}
  ];
  return(<div>
    <svg viewBox={`0 0 ${W} ${H}`} style={{fontFamily:'var(--font)',maxWidth:'100%'}}>
      <line x1={100} y1={172} x2={240} y2={58} stroke="var(--green)" strokeWidth={1.5}/>
      <line x1={290} y1={42} x2={390} y2={152} stroke="var(--green)" strokeWidth={1.5}/>
      <g className="diagram-node" onClick={()=>setExp({label:"Blocked Port (BP)",def:"A port placed in BLOCKING state by STP to prevent a loop. It receives BPDUs but does NOT forward data frames. Unblocks if a forwarding link in the path to root fails.",color:"var(--red)"})} style={{cursor:'pointer'}}>
        <line x1={145} y1={172} x2={355} y2={172} stroke="var(--red)" strokeWidth={1.5} strokeDasharray="6,3"/>
        <text x={225} y={165} fill="var(--red)" fontSize={10}>BP ✗</text>
      </g>
      <text x={120} y={125} fill="var(--green)" fontSize={10}>DP</text>
      <text x={55} y={145} fill="var(--green)" fontSize={10}>RP</text>
      <text x={395} y={145} fill="var(--green)" fontSize={10}>RP</text>
      <text x={355} y={165} fill="var(--green)" fontSize={10}>DP</text>
      <text x={165} y={165} fill="var(--red)" fontSize={10}>AP</text>
      {switches.map(s=>(<g key={s.id} className="diagram-node"
          onClick={()=>setExp({label:s.id+(s.label?` — ${s.label} Bridge`:''),def:s.def,color:s.c==='var(--green)'?'var(--green)':'var(--fg)'})} style={{cursor:'pointer'}}>
        <rect x={s.x} y={s.y} width={100} height={34} rx={3} fill="var(--surface)" stroke={s.c} strokeWidth={1.5}/>
        <text x={s.x+50} y={s.y+14} textAnchor="middle" fill={s.c} fontSize={12} fontWeight={600}>{s.id}</text>
        {s.label&&<text x={s.x+50} y={s.y+27} textAnchor="middle" fill="var(--green)" fontSize={9}>Root Bridge</text>}
      </g>))}
      <text x={250} y={215} textAnchor="middle" fill="var(--fg-mute)" fontSize={10}>Click any element — DP=Designated  RP=Root Port  BP=Blocked</text>
    </svg>
    <DiagramExpansion exp={exp} onClose={()=>setExp(null)}/>
  </div>);
}

function OspfDiagram(){
  const[exp,setExp]=React.useState(null);
  const W=500,H=200;
  const click=(label,def,color)=>setExp({label,def,color});
  return(<div>
    <svg viewBox={`0 0 ${W} ${H}`} style={{fontFamily:'var(--font)',maxWidth:'100%'}}>
      <g className="diagram-node" onClick={()=>click('Area 0 — Backbone','The OSPF backbone area. Required in any multi-area OSPF deployment. ALL other areas must connect to Area 0. Carries all LSA types between areas.','var(--green)')} style={{cursor:'pointer'}}>
        <rect x={175} y={25} width={150} height={140} rx={4} fill="var(--input)" stroke="var(--green)" strokeWidth={1.5} strokeDasharray="5,2"/>
        <text x={250} y={17} textAnchor="middle" fill="var(--green)" fontSize={11} fontWeight={700}>Area 0 (Backbone)</text>
        <rect x={195} y={55} width={48} height={22} rx={2} fill="var(--surface)" stroke="var(--green)" strokeWidth={1}/>
        <text x={219} y={70} textAnchor="middle" fill="var(--green)" fontSize={10}>R1</text>
        <rect x={255} y={100} width={48} height={22} rx={2} fill="var(--surface)" stroke="var(--green)" strokeWidth={1}/>
        <text x={279} y={115} textAnchor="middle" fill="var(--green)" fontSize={10}>R2</text>
        <line x1={243} y1={77} x2={263} y2={100} stroke="var(--green)" strokeWidth={1}/>
      </g>
      <g className="diagram-node" onClick={()=>click('Area 1','A non-backbone area. Routers inside share an identical LSDB. Internal routes flood within the area; the ABR summarizes them and advertises to Area 0 via Type 3 LSAs.','oklch(0.72 0.12 245)')} style={{cursor:'pointer'}}>
        <rect x={10} y={35} width={130} height={120} rx={4} fill="var(--input)" stroke="oklch(0.72 0.12 245)" strokeWidth={1} strokeDasharray="4,2"/>
        <text x={75} y={27} textAnchor="middle" fill="oklch(0.72 0.12 245)" fontSize={11}>Area 1</text>
        <rect x={30} y={65} width={44} height={22} rx={2} fill="var(--surface)" stroke="oklch(0.72 0.12 245)" strokeWidth={1}/>
        <text x={52} y={80} textAnchor="middle" fill="oklch(0.72 0.12 245)" fontSize={10}>R3</text>
      </g>
      <g className="diagram-node" onClick={()=>click('ABR — Area Border Router','A router with interfaces in TWO OR MORE OSPF areas, one of which must be Area 0. ABRs maintain a separate LSDB per area and summarize routes between areas via Type 3 LSAs.','var(--amber)')} style={{cursor:'pointer'}}>
        <rect x={110} y={80} width={60} height={24} rx={2} fill="var(--surface)" stroke="var(--amber)" strokeWidth={1.5}/>
        <text x={140} y={95} textAnchor="middle" fill="var(--amber)" fontSize={10}>ABR1</text>
      </g>
      <line x1={74} y1={87} x2={110} y2={92} stroke="oklch(0.72 0.12 245)" strokeWidth={1}/>
      <line x1={170} y1={92} x2={195} y2={72} stroke="var(--green)" strokeWidth={1}/>
      <g className="diagram-node" onClick={()=>click('Area 2','A non-backbone area. Communicates with Area 0 through an ABR. Inter-area traffic between Area 1 and Area 2 must traverse Area 0.','oklch(0.72 0.12 295)')} style={{cursor:'pointer'}}>
        <rect x={360} y={35} width={130} height={120} rx={4} fill="var(--input)" stroke="oklch(0.72 0.12 295)" strokeWidth={1} strokeDasharray="4,2"/>
        <text x={425} y={27} textAnchor="middle" fill="oklch(0.72 0.12 295)" fontSize={11}>Area 2</text>
        <rect x={406} y={65} width={44} height={22} rx={2} fill="var(--surface)" stroke="oklch(0.72 0.12 295)" strokeWidth={1}/>
        <text x={428} y={80} textAnchor="middle" fill="oklch(0.72 0.12 295)" fontSize={10}>R4</text>
      </g>
      <g className="diagram-node" onClick={()=>click('ABR — Area Border Router','A router with interfaces in TWO OR MORE OSPF areas, one of which must be Area 0. ABRs maintain a separate LSDB per area and summarize routes between areas via Type 3 LSAs.','var(--amber)')} style={{cursor:'pointer'}}>
        <rect x={330} y={80} width={60} height={24} rx={2} fill="var(--surface)" stroke="var(--amber)" strokeWidth={1.5}/>
        <text x={360} y={95} textAnchor="middle" fill="var(--amber)" fontSize={10}>ABR2</text>
      </g>
      <line x1={404} y1={87} x2={390} y2={92} stroke="oklch(0.72 0.12 295)" strokeWidth={1}/>
      <line x1={330} y1={92} x2={325} y2={112} stroke="var(--green)" strokeWidth={1}/>
      <text x={250} y={188} textAnchor="middle" fill="var(--fg-mute)" fontSize={10}>Click any area or router</text>
    </svg>
    <DiagramExpansion exp={exp} onClose={()=>setExp(null)}/>
  </div>);
}

const DIAGRAMS={osi:OsiDiagram,vlan:VlanDiagram,stp:StpDiagram,ospf:OspfDiagram};

// ===== TERM DRAWER =====
function TermDrawer({term,def,onClose}){
  const[log,setLog]=React.useState([]);
  const[input,setInput]=React.useState('');
  const[loading,setLoading]=React.useState(false);
  const logRef=React.useRef(null);
  const allNotes=useNotes();
  const termNotes=allNotes.filter(n=>n.topicId==='__term__'&&n.selectedText===term);
  const[showAdd,setShowAdd]=React.useState(false);
  const[noteText,setNoteText]=React.useState('');
  function saveTermNote(){
    const t=noteText.trim();
    if(!t)return;
    addNote('__term__',`Term: ${term}`,term,t);
    setNoteText(''); setShowAdd(false);
  }
  React.useEffect(()=>{if(logRef.current)logRef.current.scrollTop=logRef.current.scrollHeight;},[log,loading]);

  async function ask(){
    if(!input.trim()||loading)return;
    const msg=input.trim();setInput('');
    const newLog=[...log,{r:'user',c:msg}];setLog(newLog);setLoading(true);
    try{
      const sys=`You are a CCNA/CCNP tutor. The student is asking about the networking term "${term}" (definition: "${def}"). Be concise (2-4 sentences). Use Cisco IOS examples where relevant.`;
      const reply=await window.claude.complete({system:sys,messages:newLog.map(m=>({role:m.r==='bot'?'assistant':'user',content:m.c}))});
      setLog(l=>[...l,{r:'bot',c:reply}]);
    }catch(e){setLog(l=>[...l,{r:'bot',c:'Claude unavailable.'}]);}
    setLoading(false);
  }

  return(<div>
    <div className="drawer-overlay" onClick={onClose}/>
    <div className="drawer">
      <div className="dr-head">
        <div><div className="dr-eye">Term</div><div className="dr-title">{term}</div></div>
        <button className="btn btn-ghost btn-sm" onClick={onClose}>✕</button>
      </div>
      <div className="dr-body">
        <p>{def}</p>
        <div style={{marginTop:'22px',paddingTop:'18px',borderTop:'1px solid var(--border)'}}>
          <div style={{fontSize:'10px',color:'var(--fg-mute)',textTransform:'uppercase',letterSpacing:'0.08em',marginBottom:'10px',display:'flex',alignItems:'center',gap:'6px'}}><span style={{color:'var(--purple)'}}>✎</span> Your notes</div>
          {termNotes.length>0&&<div style={{display:'flex',flexDirection:'column',gap:'8px',marginBottom:'12px'}}>
            {termNotes.map(n=>(
              <div key={n.id} style={{padding:'10px 12px',background:'var(--raised)',borderLeft:'3px solid var(--purple)',borderRadius:'0 var(--r) var(--r) 0'}}>
                <div style={{fontSize:'12.5px',color:'var(--fg-hi)',lineHeight:1.55,whiteSpace:'pre-wrap',marginBottom:'6px'}}>{n.note}</div>
                <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',gap:'8px'}}>
                  <span style={{fontSize:'10px',color:'var(--fg-mute)'}}>{new Date(n.createdAt).toLocaleDateString()}</span>
                  <button className="btn btn-ghost btn-sm btn-danger" onClick={()=>{if(confirm('Delete this note?'))deleteNote(n.id);}}>Delete</button>
                </div>
              </div>
            ))}
          </div>}
          {!showAdd
            ?<button className="btn btn-sm" onClick={()=>setShowAdd(true)}>+ Add note</button>
            :<div>
              <textarea className="chat-in" rows={3} value={noteText} onChange={e=>setNoteText(e.target.value)}
                onKeyDown={e=>{if(e.key==='Enter'&&(e.metaKey||e.ctrlKey)){e.preventDefault();saveTermNote();}}}
                placeholder={`Your note about "${term}"… (Cmd/Ctrl+Enter to save)`}
                style={{width:'100%',fontSize:'13px',padding:'9px 11px',marginBottom:'8px'}} autoFocus/>
              <div style={{display:'flex',gap:'6px',justifyContent:'flex-end'}}>
                <button className="btn btn-ghost btn-sm" onClick={()=>{setShowAdd(false);setNoteText('');}}>Cancel</button>
                <button className="btn btn-primary btn-sm" onClick={saveTermNote} disabled={!noteText.trim()}>Save</button>
              </div>
            </div>
          }
        </div>
      </div>
      {AI_TUTOR_ENABLED && <div className="dr-chat">
        <div style={{fontSize:'10px',color:'var(--fg-mute)',textTransform:'uppercase',letterSpacing:'0.08em',marginBottom:'6px'}}>Ask Claude about this</div>
        <div className="chat-log" ref={logRef} style={{maxHeight:'180px',minHeight:'40px'}}>
          {log.length===0&&<div style={{color:'var(--fg-mute)',fontSize:'11px'}}>Ask anything about this term…</div>}
          {log.map((m,i)=><div key={i} className={`chat-msg ${m.r}`}>{m.c}</div>)}
          {loading&&<div className="chat-msg bot thinking">thinking…</div>}
        </div>
        <div className="chat-row">
          <textarea className="chat-in" rows={2} value={input} onChange={e=>setInput(e.target.value)}
            onKeyDown={e=>{if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();if(input.trim())ask();}}}
            placeholder="Ask Claude… (Enter)" disabled={loading}/>
          <button className="btn btn-sm btn-primary" onClick={ask} disabled={!input.trim()||loading}>Ask</button>
        </div>
      </div>}
    </div>
  </div>);
}

// ===== YOUTUBE EMBED (iframe via youtube-nocookie — works on more networks) =====
function YouTubeSection({videoId, playlistId, channelUrl, title}){
  if(!videoId && !playlistId && !channelUrl) return null;
  // Channel-only link → render a clickable "Watch on YouTube" card (no iframe)
  if (!videoId && !playlistId && channelUrl) {
    return(
      <div className="yt-section">
        <div className="yt-bar">
          <span style={{color:'var(--red)'}}>▶</span>
          <span>Video — {title}</span>
          <a href={channelUrl} target="_blank" rel="noopener noreferrer">Pop out ↗</a>
        </div>
        <a href={channelUrl} target="_blank" rel="noopener noreferrer" className="yt-thumb-link"
          style={{padding:'40px 20px',display:'flex',flexDirection:'column',alignItems:'center',gap:'10px',textDecoration:'none'}}>
          <div className="yt-play-btn">▶</div>
          <div className="yt-play-label">Watch on YouTube</div>
          <div style={{color:'var(--fg-mute)',fontSize:'11px'}}>{channelUrl.replace(/^https?:\/\//,'')}</div>
        </a>
      </div>
    );
  }
  const embedUrl = videoId
    ? `https://www.youtube-nocookie.com/embed/${videoId}${playlistId?`?list=${playlistId}`:''}`
    : `https://www.youtube-nocookie.com/embed?listType=playlist&list=${playlistId}`;
  const watchUrl = videoId
    ? `https://www.youtube.com/watch?v=${videoId}${playlistId?`&list=${playlistId}`:''}`
    : `https://www.youtube.com/playlist?list=${playlistId}`;
  return(
    <div className="yt-section">
      <div className="yt-bar">
        <span style={{color:'var(--red)'}}>▶</span>
        <span>NetworkChuck — {title}</span>
        <a href={watchUrl} target="_blank" rel="noopener noreferrer">Pop out ↗</a>
      </div>
      <div className="yt-wrap">
        <iframe
          className="yt-iframe"
          src={embedUrl}
          title={`NetworkChuck — ${title}`}
          frameBorder="0"
          allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
          referrerPolicy="strict-origin-when-cross-origin"
          allowFullScreen/>
      </div>
    </div>
  );
}

// ===== CONCEPT SECTION RENDERER =====
function ConceptSection({sec, termsMap, onTerm, usedTerms, notes, onNoteClick}){
  const ht = (text) => highlightTerms(text, termsMap, onTerm, usedTerms, notes, onNoteClick);

  if(sec.type==='h2') return <h2>{sec.text}</h2>;
  if(sec.type==='p')  return <p>{ht(sec.text)}</p>;
  if(sec.type==='table') return(
    <table className="cp-table">
      <thead><tr>{sec.headers.map(h=><th key={h}>{h}</th>)}</tr></thead>
      <tbody>{sec.rows.map((r,i)=><tr key={i}>{r.map((c,j)=><td key={j}>{ht(c)}</td>)}</tr>)}</tbody>
    </table>
  );
  if(sec.type==='terms') return(
    <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(300px,1fr))',gap:'8px'}}>
      {sec.items.map(t=>(
        <div key={t.term} style={{padding:'10px 12px',background:'var(--raised)',borderRadius:'var(--r)',border:'1px solid var(--border)'}}>
          <div style={{color:'var(--cyan)',fontWeight:600,fontSize:'12px',marginBottom:'5px',letterSpacing:'0.01em'}}>{t.term}</div>
          <div style={{fontSize:'12px',color:'var(--fg-dim)',lineHeight:1.55}}>{t.def}</div>
        </div>
      ))}
    </div>
  );
  if(sec.type==='diagram'){
    const Diagram=DIAGRAMS[sec.id];
    return(
      <div className="cp-diagram">
        <div style={{fontSize:'10px',color:'var(--fg-mute)',textTransform:'uppercase',letterSpacing:'0.08em',marginBottom:'10px'}}>Interactive Diagram — click any element</div>
        {Diagram?<Diagram/>:<div style={{color:'var(--fg-mute)',padding:'20px',textAlign:'center',fontSize:'12px'}}>Diagram: {sec.id}</div>}
      </div>
    );
  }
  return null;
}

// ===== CONCEPT PAGE =====
function ConceptPage({concept, onTerm, onDone, onUnmark, isRead}){
  const domName=(window.DOMAINS||[]).find(d=>d.id===concept.domainId)?.name||`Domain ${concept.domainId}`;
  const termsMap=React.useMemo(()=>buildTermsMap(concept.sections),[concept]);
  const usedTerms=new Set();
  const allNotes=useNotes();
  const notes=allNotes.filter(n=>n.topicId===concept.topicId);
  const[bubble,clearBubble]=useSelectionBubble('.concept-page');
  const[noteInput,setNoteInput]=React.useState(null);
  const[notePop,setNotePop]=React.useState(null);
  function startNote(){if(!bubble)return;setNoteInput({selectedText:bubble.text});clearBubble();}
  function saveNew(text){addNote(concept.topicId,concept.title,noteInput.selectedText,text);setNoteInput(null);}
  function saveEdit(text){updateNote(noteInput.id,text);setNoteInput(null);}
  function onNoteClick(n,e){const r=e.target.getBoundingClientRect();setNotePop({note:n,pos:{x:r.left+r.width/2,y:r.bottom}});}
  return(
    <div className="concept-page">
      <div className="cp-eyebrow">Domain {concept.domainId} — {domName}{isRead?<span style={{color:'var(--green)',marginLeft:'8px'}}>✓ Read</span>:''}</div>
      <div className="cp-title">{concept.title}</div>
      <div className="cp-summary">{concept.summary}</div>
      {(concept.videoId||concept.playlistId||concept.channelUrl)&&(
        <YouTubeSection videoId={concept.videoId} playlistId={concept.playlistId} channelUrl={concept.channelUrl} title={concept.title}/>
      )}
      <div className="cp-body">
        {(concept.sections||[]).map((s,i)=>(
          <ConceptSection key={i} sec={s} termsMap={termsMap} onTerm={onTerm} usedTerms={usedTerms}
            notes={notes} onNoteClick={onNoteClick}/>
        ))}
      </div>
      <div className="cp-done-row">
        {isRead
          ?<button className="btn btn-sm" onClick={onUnmark} title="Click to unmark as read" style={{color:'var(--green)',borderColor:'var(--green)'}}>✓ Marked as read — click to unmark</button>
          :<span style={{flex:1}}/>}
        {onDone&&<button className="btn btn-primary" onClick={onDone}>{isRead?'Next →':'Mark as Read & Next →'}</button>}
      </div>
      {window.DeepDivePanel && <window.DeepDivePanel topicId={concept.topicId} conceptTitle={concept.title}/>}
      <SelectionBubble bubble={bubble} onAdd={startNote}/>
      {noteInput&&<NoteInput selectedText={noteInput.selectedText} initialText={noteInput.initialText}
        title={noteInput.id?'Edit Note':'Add Note'}
        onSave={noteInput.id?saveEdit:saveNew} onCancel={()=>setNoteInput(null)}/>}
      {notePop&&<NotePopover note={notePop.note} pos={notePop.pos}
        onClose={()=>setNotePop(null)}
        onEdit={()=>{setNoteInput({id:notePop.note.id,selectedText:notePop.note.selectedText,initialText:notePop.note.note});setNotePop(null);}}
        onDelete={()=>{if(confirm('Delete this note?')){deleteNote(notePop.note.id);setNotePop(null);}}}/>}
    </div>
  );
}

// ===== CONCEPT DRAWER (used from quiz CLI view) =====
function ConceptDrawer({topicId, hintHeadings, onClose}){
  const concept=(window.CONCEPTS||[]).find(c=>c.topicId===topicId);
  const[termDrawer,setTermDrawer]=React.useState(null);
  const[hintActive,setHintActive]=React.useState(false);
  if(!concept)return null;
  const termsMap=buildTermsMap(concept.sections);
  const usedTerms=new Set();
  return(<div>
    <div className="drawer-overlay" onClick={onClose}/>
    <div className="drawer" style={{width:'min(680px,94vw)'}}>
      <div className="dr-head">
        <div>
          <div className="dr-eye">Learn Mode</div>
          <div className="dr-title">{concept.title}</div>
        </div>
        <div style={{display:'flex',gap:'6px',alignItems:'center'}}>
          {hintHeadings&&hintHeadings.length>0&&(
            <button className={`btn btn-sm ${hintActive?'btn-primary':''}`} onClick={()=>setHintActive(h=>!h)}>
              {hintActive?'✓ Hints On':'💡 Hint'}
            </button>
          )}
          <button className="btn btn-ghost btn-sm" onClick={onClose}>✕</button>
        </div>
      </div>
      <div className="dr-body" style={{flex:1,overflowY:'auto',padding:'18px 22px'}}>
        {(concept.videoId||concept.playlistId||concept.channelUrl)&&(
          <div style={{marginBottom:'16px'}}>
            <YouTubeSection videoId={concept.videoId} playlistId={concept.playlistId} channelUrl={concept.channelUrl} title={concept.title}/>
          </div>
        )}
        <div className="cp-summary" style={{marginBottom:'16px'}}>{concept.summary}</div>
        <div className="cp-body">
          {(()=>{const blocks=[];let cur={h2:null,items:[]};(concept.sections||[]).forEach((s,i)=>{if(s.type==='h2'){if(cur.items.length)blocks.push(cur);cur={h2:s.text,items:[{s,i}]};}else{cur.items.push({s,i});}});if(cur.items.length)blocks.push(cur);return blocks.map((b,bi)=>{const isHint=hintActive&&hintHeadings&&hintHeadings.includes(b.h2);return(<div key={bi} className={`cp-block ${isHint?'cp-block-hint':''}`}>{b.items.map(({s,i})=>(<ConceptSection key={i} sec={s} termsMap={termsMap} onTerm={t=>setTermDrawer(t)} usedTerms={usedTerms}/>))}</div>);});})()}
        </div>
      </div>
    </div>
    {termDrawer&&<TermDrawer term={termDrawer.term} def={termDrawer.def} onClose={()=>setTermDrawer(null)}/>}
  </div>);
}

// ===== LEARN MODE =====
function LearnMode({onExit, certMode}){
  const all=window.CONCEPTS||[];
  const concepts=all.filter(c=>(c.cert||'ccna')===certMode);
  const[activeId,setActiveId]=React.useState(concepts[0]?.topicId||null);
  const[drawer,setDrawer]=React.useState(null);
  const[collapsed,setCollapsed]=React.useState({});
  const contentRef=React.useRef(null);
  const readList=useReadTopics(certMode);

  const active=concepts.find(c=>c.topicId===activeId);
  React.useEffect(()=>{
    // If current active topic isn't in the filtered list, jump to the first available
    if(active===undefined && concepts.length){setActiveId(concepts[0].topicId);}
  },[certMode,concepts.length]);
  const byDomain={};
  concepts.forEach(c=>{if(!byDomain[c.domainId])byDomain[c.domainId]=[];byDomain[c.domainId].push(c);});

  function selectTopic(id){
    setActiveId(id);
    if(contentRef.current) contentRef.current.scrollTop=0;
    window.scrollTo({top:0,behavior:'auto'});
  }

  function handleDone(){
    if(!activeId)return;
    if(!readList.includes(activeId)) markRead(certMode,activeId);
    // Use the same domain-sorted order the sidebar displays so 'next' is intuitive
    const sorted=[...concepts].sort((a,b)=>a.domainId-b.domainId);
    const idx=sorted.findIndex(c=>c.topicId===activeId);
    const next=sorted[idx+1];
    if(next){selectTopic(next.topicId);}
  }
  function handleUnmark(){if(activeId)unmarkRead(certMode,activeId);}

  return(
    <div>
      <div style={{display:'flex',alignItems:'center',gap:'12px',marginBottom:'16px'}}>
        <div style={{flex:1}}>
          <div style={{fontSize:'11px',color:'var(--fg-mute)',textTransform:'uppercase',letterSpacing:'0.1em',marginBottom:'3px'}}>Learn Mode</div>
          <div style={{fontSize:'13px',color:'var(--fg-dim)',marginTop:'2px'}}>
            Click any <span className="term">term</span> to define · highlight a passage to add a note.
          </div>
        </div>
        <button className="btn btn-ghost btn-sm" onClick={onExit}>← Back</button>
      </div>
      <div className="learn-layout">
        <div className="learn-sidebar">
          {Object.entries(byDomain).map(([did,topics])=>{
            const dom=(window.DOMAINS||[]).find(d=>d.id===parseInt(did));
            const isCollapsed=collapsed[did];
            return(<div key={did} className="ls-dom">
              <div className="ls-dom-hd" onClick={()=>setCollapsed(c=>({...c,[did]:!isCollapsed}))}>
                <span style={{color:`var(--d${did})`}}>D{did} — {dom?.short||dom?.name||'Domain'}</span>
                <span>{isCollapsed?'▶':'▼'}</span>
              </div>
              {!isCollapsed&&topics.map(t=>{const isRead=readList.includes(t.topicId);return(
                <button key={t.topicId} className={`ls-topic ${activeId===t.topicId?'active':''} ${isRead?'read':''}`} onClick={()=>selectTopic(t.topicId)}>
                  {isRead?'✓ ':''}{t.title}
                </button>);})}
            </div>);
          })}
        </div>
        <div ref={contentRef} style={{overflow:'auto'}}>
          {active
            ?<ConceptPage concept={active} onTerm={t=>setDrawer(t)} onDone={handleDone} onUnmark={handleUnmark} isRead={readList.includes(active.topicId)}/>
            :<div className="empty-msg">Select a topic from the sidebar.</div>
          }
        </div>
      </div>
      {drawer&&<TermDrawer term={drawer.term} def={drawer.def} onClose={()=>setDrawer(null)}/>}
    </div>
  );
}

Object.assign(window,{LearnMode, ConceptDrawer});
