// ─── AI Lab Module ───────────────────────────────────────
function AILabModule(props) {
  var cards = props.cards, db = props.db, showToast = props.showToast, MEDIA_TYPES = props.MEDIA_TYPES, SCORE_FIELDS = props.SCORE_FIELDS;
  var library = props.library, crowdScores = props.crowdScores, generateCode = props.generateCode, generateId = props.generateId;
  var tasteProfile = props.tasteProfile, drops = props.drops, creators = props.creators, culturalPulse = props.culturalPulse;
  var [view, setView] = useState('generate');
  var [generatePrompt, setGeneratePrompt] = useState('');

  function getToken() { return firebase.auth().currentUser.getIdToken(); }

  function handleGenerateFromAnalysis(promptText) {
    setGeneratePrompt(promptText);
    setView('generate');
  }

  return (
    <div className="AI-panel">
      <div style={{display:'flex',gap:8,marginBottom:24,flexWrap:'wrap'}}>
        {['generate','bulk','score','analyze','marketing','audit'].map(function(v) {
          return <button key={v} className={'A-btn' + (view===v?' signal':'')} onClick={function(){setView(v)}}>{v.toUpperCase()}</button>;
        })}
      </div>
      {view === 'generate' && <AIGenerate cards={cards} db={db} showToast={showToast} getToken={getToken} MEDIA_TYPES={MEDIA_TYPES} initialPrompt={generatePrompt} onPromptConsumed={function(){setGeneratePrompt('')}} tasteProfile={tasteProfile} />}
      {view === 'bulk' && <AIBulkPlan cards={cards} db={db} showToast={showToast} getToken={getToken} library={library} crowdScores={crowdScores} generateCode={generateCode} generateId={generateId} tasteProfile={tasteProfile} drops={drops} creators={creators} culturalPulse={culturalPulse} />}
      {view === 'score' && <AIScore cards={cards} db={db} showToast={showToast} getToken={getToken} SCORE_FIELDS={SCORE_FIELDS} />}
      {view === 'analyze' && <AIAnalyze cards={cards} db={db} showToast={showToast} getToken={getToken} MEDIA_TYPES={MEDIA_TYPES} onGenerateFrom={handleGenerateFromAnalysis} />}
      {view === 'marketing' && <AIMarketing showToast={showToast} getToken={getToken} />}
      {view === 'audit' && <AIAudit cards={cards} db={db} showToast={showToast} getToken={getToken} generateCode={generateCode} />}
    </div>
  );
}

// ─── Bulk Plan (contentlab engine) ───────────────────────
function AIBulkPlan(props) {
  var cards = props.cards, db = props.db, showToast = props.showToast, getToken = props.getToken;
  var library = props.library, crowdScores = props.crowdScores;
  var generateCode = props.generateCode, generateId = props.generateId;
  var tasteProfile = props.tasteProfile, drops = props.drops, creators = props.creators, culturalPulse = props.culturalPulse;

  var [brief, setBrief] = useState('');
  var [count, setCount] = useState(15);
  var [loading, setLoading] = useState(false);
  var [plan, setPlan] = useState(null);
  var [error, setError] = useState(null);
  var [refineText, setRefineText] = useState('');
  var [refining, setRefining] = useState(false);
  var [selectedCreatorId, setSelectedCreatorId] = useState('');

  // Port of contentlab selectReferences — score + rank library cards for prompt context
  function selectReferences(allRefs, briefText) {
    var briefLower = briefText.toLowerCase();
    var scored = allRefs.map(function(r) {
      var score = r.lineconic_score || r.scores && r.scores.lineconic_score || 5;

      // Boost if brief mentions category, source, or themes
      if (r.category && briefLower.includes(r.category.toLowerCase())) score += 2;
      if (r.source && briefLower.includes(r.source.toLowerCase().split(' ')[0].toLowerCase())) score += 3;
      var tags = r.themes || r.tags || [];
      for (var i = 0; i < tags.length; i++) {
        if (briefLower.includes(tags[i].toLowerCase())) score += 1.5;
      }

      // Boost performance potential
      var pp = r.scores && r.scores.performance_potential || 5;
      score += pp * 0.15;

      // Penalize recently played (via crowdScores)
      var cs = crowdScores && crowdScores[r.id];
      if (cs && cs.lastPlayed) {
        var daysSince = (Date.now() - new Date(cs.lastPlayed).getTime()) / 86400000;
        if (daysSince < 30) score -= 4;
      }

      // Random jitter for variety
      score += Math.random() * 1.5;

      return { ref: r, score: score };
    });

    scored.sort(function(a, b) { return b.score - a.score; });
    return scored.slice(0, 200).map(function(s) { return s.ref; });
  }

  // Format selected refs into digest string
  function buildDigest(refs) {
    return refs.map(function(r) {
      var id = r.id || '';
      var answer = r.content && r.content.answer || r.line || '';
      var source = r.source || '';
      var mediaType = r.media_type || '';
      var category = r.category || '';
      var ls = r.lineconic_score || r.scores && r.scores.lineconic_score || '';
      var tags = r.themes || r.tags || [];
      var cs = crowdScores && crowdScores[id];
      var recentTag = cs && cs.lastPlayed && (Date.now() - new Date(cs.lastPlayed).getTime()) / 86400000 < 30 ? ' [RECENTLY USED — avoid]' : '';
      var scoreTag = ls ? ' LS:' + ls : '';
      return '[' + id + '] "' + answer + '" — ' + source + ' (' + mediaType + ') [' + category + ']' + scoreTag + ' tags: ' + tags.join(', ') + recentTag;
    }).join('\n');
  }

  function generate() {
    if (!brief.trim() || !library) return;
    setLoading(true); setError(null); setPlan(null); setRefineText('');

    var refs = selectReferences(library, brief);
    var digest = buildDigest(refs);

    // Gather fire-rated drops for feedback loop
    var topPerformers = null;
    if (drops) {
      var fireDrops = Object.values(drops).filter(function(d) { return d.performance === 'fire'; });
      if (fireDrops.length > 0) {
        var fireSituations = [];
        var fireSignoffs = [];
        fireDrops.forEach(function(d) {
          if (d.situation && fireSituations.indexOf(d.situation) === -1) fireSituations.push(d.situation);
          if (d.signoff && fireSignoffs.indexOf(d.signoff) === -1) fireSignoffs.push(d.signoff);
        });
        topPerformers = { situations: fireSituations.slice(0, 10), signoffs: fireSignoffs.slice(0, 10) };
      }
    }

    var tp = tasteProfile && tasteProfile.content ? tasteProfile.content : null;

    // Build creator profile context if selected
    var creatorProfile = null;
    if (selectedCreatorId && creators && creators[selectedCreatorId]) {
      var cr = creators[selectedCreatorId];
      creatorProfile = 'Creator: ' + cr.name;
      if (cr.handle) creatorProfile += ' (' + cr.handle + ')';
      if (cr.platform) creatorProfile += ' on ' + cr.platform;
      if (cr.audience_size) creatorProfile += '. Audience size: ' + cr.audience_size;
      if (cr.audience_demo) creatorProfile += '. Audience demo: ' + cr.audience_demo;
      if (cr.content_style) creatorProfile += '. Content style: ' + cr.content_style;
      if (cr.notes) creatorProfile += '. Notes: ' + cr.notes;

      // Enrich with performance data
      if (drops) {
        var crDrops = Object.values(drops).filter(function(d) { return d.creator_id === selectedCreatorId; });
        var crRated = crDrops.filter(function(d) { return d.performance; });
        var crFire = crRated.filter(function(d) { return d.performance === 'fire'; });
        var crMid = crRated.filter(function(d) { return d.performance === 'mid'; });
        var crFlop = crRated.filter(function(d) { return d.performance === 'flop'; });

        if (crRated.length > 0) {
          var crFireRate = Math.round(crFire.length / crRated.length * 100);
          creatorProfile += '\nPerformance history: ' + crFire.length + ' fire / ' + crRated.length + ' rated (' + crFireRate + '%).';

          // Best mediums from fire drops
          var mediumCounts = {};
          crFire.forEach(function(d) { if (d.medium) mediumCounts[d.medium] = (mediumCounts[d.medium] || 0) + 1; });
          var bestMediums = Object.keys(mediumCounts).sort(function(a, b) { return mediumCounts[b] - mediumCounts[a]; });
          if (bestMediums.length > 0) creatorProfile += '\nBest mediums: ' + bestMediums.slice(0, 3).join(', ') + '.';

          // Best sources from fire drops
          var sourceCounts = {};
          crFire.forEach(function(d) { if (d.source) sourceCounts[d.source] = (sourceCounts[d.source] || 0) + 1; });
          var bestSources = Object.keys(sourceCounts).sort(function(a, b) { return sourceCounts[b] - sourceCounts[a]; });
          if (bestSources.length > 0) creatorProfile += '\nBest sources: ' + bestSources.slice(0, 3).join(', ') + '.';

          // Fire situation patterns (top 5)
          var fireSits = crFire.map(function(d) { return d.situation; }).filter(Boolean).slice(0, 5);
          if (fireSits.length > 0) creatorProfile += '\nFire situations: ' + fireSits.map(function(s) { return '"' + s + '"'; }).join(', ') + '.';

          // What to avoid — mediums/sources with 0% fire rate
          var avoidMediums = {};
          crFlop.forEach(function(d) { if (d.medium) avoidMediums[d.medium] = (avoidMediums[d.medium] || 0) + 1; });
          var avoid = Object.keys(avoidMediums).filter(function(m) { return !mediumCounts[m]; });
          if (avoid.length > 0) creatorProfile += '\nAvoid: ' + avoid.join(', ') + ' (low engagement).';
        }
      }
    }

    // Build cultural pulse context for Chloe
    var pulseContext = null;
    if (culturalPulse && culturalPulse.topics) {
      var topicEntries = Object.values(culturalPulse.topics);
      if (topicEntries.length > 0) {
        var deadTopics = topicEntries.filter(function(t) { return t.status === 'dead' || t.status === 'dying'; });
        var aliveTopics = topicEntries.filter(function(t) { return t.status === 'alive' || t.status === 'thriving'; });
        var contestedTopics = topicEntries.filter(function(t) { return t.status === 'contested'; });
        var lines = ['AUDIENCE CULTURAL PULSE (from live DOA votes, ' + culturalPulse.total_votes + ' total votes):'];
        if (deadTopics.length > 0) lines.push('DEAD/DYING: ' + deadTopics.map(function(t) { return t.topic + ' (' + t.dead_pct + '% dead)'; }).join(', '));
        if (aliveTopics.length > 0) lines.push('ALIVE/THRIVING: ' + aliveTopics.map(function(t) { return t.topic + ' (' + t.alive_pct + '% alive)'; }).join(', '));
        if (contestedTopics.length > 0) lines.push('CONTESTED: ' + contestedTopics.map(function(t) { return t.topic + ' (' + t.dead_pct + '% dead / ' + t.alive_pct + '% alive)'; }).join(', '));
        pulseContext = lines.join('\n');
      }
    }

    getToken().then(function(token) {
      return aiFetch('/api/ai/bulk-plan', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
        body: JSON.stringify({ brief: brief, count: count, libraryDigest: digest, totalLibrarySize: library.length, tasteProfile: tp, topPerformers: topPerformers, creatorProfile: creatorProfile, culturalPulse: pulseContext })
      }, 60000);
    }).then(function(res) {
      return res.json();
    }).then(function(data) {
      setPlan(data.plan || []);
      setLoading(false);
    }).catch(function(e) { setError(formatAiError(e)); setLoading(false); });
  }

  function refine() {
    if (!refineText.trim() || !plan || !library) return;
    setRefining(true); setError(null);

    var refs = selectReferences(library, refineText);
    var digest = buildDigest(refs);

    getToken().then(function(token) {
      return aiFetch('/api/ai/bulk-plan', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
        body: JSON.stringify({ brief: brief, count: count, libraryDigest: digest, totalLibrarySize: library.length, currentPlan: plan, instruction: refineText, tasteProfile: tasteProfile && tasteProfile.content ? tasteProfile.content : null })
      }, 60000);
    }).then(function(res) {
      return res.json();
    }).then(function(data) {
      setPlan(data.plan || []);
      setRefineText('');
      setRefining(false);
    }).catch(function(e) { setError(formatAiError(e)); setRefining(false); });
  }

  function planItemToCard(item) {
    var answer = item.line || '';
    var id = generateId(answer);
    var code = generateCode(answer);
    return {
      id: id,
      code: code,
      content: { primary: code.split('.').join('.  ').trim(), secondary: '', answer: answer },
      source: item.source || '',
      media_type: item.medium || 'movie',
      difficulty: 3,
      category: 'Evergreen Canon',
      themes: [],
      scores: {},
      staged: true,
      _situation: item.situation || '',
      _signoff: item.signoff || ''
    };
  }

  function acceptItem(item) {
    var card = planItemToCard(item);
    db.ref('cards/cards/' + card.id).set(card).then(function() {
      showToast('ACCEPTED: ' + card.id);
      setPlan(function(prev) { return prev.filter(function(p) { return p.line !== item.line; }); });
    }).catch(function(e) { showToast('SAVE FAILED: ' + e.message); });
  }

  function acceptAll() {
    if (!plan || plan.length === 0) return;
    var promises = plan.map(function(item) {
      var card = planItemToCard(item);
      return db.ref('cards/cards/' + card.id).set(card);
    });
    Promise.all(promises).then(function() {
      showToast(plan.length + ' CARDS STAGED');
      setPlan([]);
    }).catch(function(e) { showToast('BULK SAVE FAILED: ' + e.message); });
  }

  var [autoSchedule, setAutoSchedule] = useState(true);

  function sendToDrops() {
    if (!plan || plan.length === 0) return;
    var updates = {};
    var startDate = new Date();
    startDate.setDate(startDate.getDate() + 1); // start from tomorrow

    plan.forEach(function(item, idx) {
      var id = 'drop-' + Date.now() + '-' + Math.random().toString(36).substr(2, 6);
      var scheduledDate = null;
      var status = 'draft';
      if (autoSchedule) {
        var d = new Date(startDate);
        d.setDate(d.getDate() + idx);
        scheduledDate = d.toISOString().split('T')[0];
        status = 'scheduled';
      }
      updates['drops/' + id] = {
        id: id,
        line: item.line || '',
        source: item.source || '',
        situation: item.situation || '',
        signoff: item.signoff || '',
        acronym: item.acronym || '',
        medium: item.medium || 'movie',
        scheduled_date: scheduledDate,
        status: status,
        created_at: new Date().toISOString(),
        reference_id: generateId(item.line)
      };
    });
    db.ref().update(updates).then(function() {
      showToast(plan.length + ' DROPS ' + (autoSchedule ? 'QUEUED' : 'CREATED'));
      setPlan([]);
    }).catch(function(e) { showToast('SEND TO DROPS FAILED: ' + e.message); });
  }

  return (
    <div>
      <div className="A-section-head">BULK PLAN</div>
      <div style={{fontSize:10,color:'var(--text-dim)',marginBottom:16,letterSpacing:.5}}>Library-aware generation — contentlab engine</div>

      <div style={{marginBottom:16}}>
        <label className="A-label">Creative Brief</label>
        <textarea className="A-textarea" rows="3" value={brief} onChange={function(e){setBrief(e.target.value)}} placeholder="e.g., '90s film nostalgia — iconic movie quotes millennials quote daily'" />
      </div>
      <div style={{display:'flex',gap:12,marginBottom:16,alignItems:'end',flexWrap:'wrap'}}>
        <div><label className="A-label">Count</label><input className="A-input" type="number" min="5" max="30" value={count} onChange={function(e){setCount(parseInt(e.target.value)||15)}} style={{width:60}} /></div>
        {creators && Object.keys(creators).length > 0 && (
          <div><label className="A-label">Generate for Creator</label>
            <select className="A-select" value={selectedCreatorId} onChange={function(e){setSelectedCreatorId(e.target.value)}} style={{width:180}}>
              <option value="">None</option>
              {Object.values(creators).map(function(c){return <option key={c.id} value={c.id}>{c.name}{c.handle ? ' ('+c.handle+')' : ''}</option>})}
            </select>
          </div>
        )}
        <button className="A-btn signal" onClick={generate} disabled={loading || !brief.trim() || !library}>{loading ? 'GENERATING...' : 'GENERATE'}</button>
        {!library && <span style={{fontSize:10,color:'var(--text-dim)'}}>Loading library...</span>}
      </div>

      {error && <div style={{color:'var(--signal)',fontSize:11,marginBottom:16}}>Error: {error}</div>}

      {plan && plan.length === 0 && <div style={{color:'var(--text-dim)',padding:24}}>No items generated. Try a different brief.</div>}

      {plan && plan.length > 0 && (
        <div>
          <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:12}}>
            <span style={{fontSize:11,color:'var(--text-sec)'}}>{plan.length} items</span>
            <div style={{display:'flex',gap:8}}>
              <button className="A-btn gold" onClick={acceptAll}>ACCEPT ALL</button>
              <label style={{display:'flex',alignItems:'center',gap:4,fontSize:10,color:'var(--text-sec)',cursor:'pointer'}}><input type="checkbox" checked={autoSchedule} onChange={function(e){setAutoSchedule(e.target.checked)}} /> AUTO-SCHEDULE</label>
              <button className="A-btn cyan" onClick={sendToDrops}>{autoSchedule ? 'QUEUE DROPS' : 'SEND TO DROPS'}</button>
            </div>
          </div>

          {plan.map(function(item, idx) {
            var exists = cards && cards[generateId(item.line)];
            return (
              <div key={idx} className="AI-card-preview">
                <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:8}}>
                  <div style={{flex:1}}>
                    <div style={{fontSize:13,color:'#fff',marginBottom:4}}>"{item.line}"</div>
                    <div style={{fontSize:11,color:'var(--text-sec)',marginBottom:4}}>{item.source} — {item.medium}</div>
                  </div>
                  <div style={{display:'flex',gap:8,flexShrink:0}}>
                    {exists && <span style={{fontSize:9,color:'var(--gold)',letterSpacing:1}}>EXISTS</span>}
                    <button className="A-btn cyan" onClick={function(){acceptItem(item)}} disabled={exists} style={{fontSize:10,padding:'4px 10px'}}>ACCEPT</button>
                  </div>
                </div>
                <div style={{fontSize:11,color:'var(--cyan)',marginBottom:4}}>{item.situation}</div>
                <div style={{display:'flex',gap:16,fontSize:10,color:'var(--text-dim)'}}>
                  <span>signoff: "{item.signoff}"</span>
                  <span>acronym: {item.acronym}</span>
                </div>
              </div>
            );
          })}

          {/* Refinement */}
          <div style={{marginTop:24,padding:16,border:'1px solid var(--border)',background:'var(--surface-2)'}}>
            <label className="A-label">Refine Plan</label>
            <textarea className="A-textarea" rows="2" value={refineText} onChange={function(e){setRefineText(e.target.value)}} placeholder="e.g., 'swap out rom-com references for action movies'" />
            <button className="A-btn signal" onClick={refine} disabled={refining || !refineText.trim()} style={{marginTop:8}}>{refining ? 'REFINING...' : 'REFINE'}</button>
          </div>
        </div>
      )}
    </div>
  );
}

// ─── Generate Cards ──────────────────────────────────────
function AIGenerate(props) {
  var cards = props.cards, db = props.db, showToast = props.showToast, getToken = props.getToken, MEDIA_TYPES = props.MEDIA_TYPES;
  var initialPrompt = props.initialPrompt, onPromptConsumed = props.onPromptConsumed;
  var tasteProfile = props.tasteProfile;
  var [prompt, setPrompt] = useState(initialPrompt || '');
  var [count, setCount] = useState(5);

  useEffect(function() {
    if (initialPrompt) {
      setPrompt(initialPrompt);
      if (onPromptConsumed) onPromptConsumed();
    }
  }, [initialPrompt]);
  var [mediaType, setMediaType] = useState('');
  var [difficulty, setDifficulty] = useState('');
  var [loading, setLoading] = useState(false);
  var [results, setResults] = useState(null);
  var [error, setError] = useState(null);

  function generate() {
    if (!prompt.trim()) return;
    setLoading(true); setError(null); setResults(null);
    getToken().then(function(token) {
      return aiFetch('/api/ai/generate-cards', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
        body: JSON.stringify({ prompt: prompt, count: count, media_type: mediaType || undefined, difficulty: difficulty ? parseInt(difficulty) : undefined, tasteProfile: tasteProfile && tasteProfile.content ? tasteProfile.content : undefined })
      });
    }).then(function(res) {
      return res.json();
    }).then(function(data) {
      setResults(data.cards || []);
      setLoading(false);
    }).catch(function(e) { setError(formatAiError(e)); setLoading(false); });
  }

  function acceptCard(card) {
    card.staged = true;
    db.ref('cards/cards/' + card.id).set(card).then(function() {
      showToast('CARD ACCEPTED: ' + card.id);
      setResults(function(prev) { return prev.filter(function(c){return c.id !== card.id}) });
    }).catch(function(e) { showToast('SAVE FAILED: ' + e.message); });
  }

  return (
    <div>
      <div className="A-section-head">GENERATE CARDS</div>
      <div style={{marginBottom:16}}>
        <label className="A-label">Prompt</label>
        <textarea className="A-textarea" rows="3" value={prompt} onChange={function(e){setPrompt(e.target.value)}} placeholder="e.g., 'Iconic 90s movie quotes that everyone knows' or 'Deep cut internet memes from 2015-2020'" />
      </div>
      <div style={{display:'flex',gap:12,marginBottom:16,flexWrap:'wrap',alignItems:'end'}}>
        <div><label className="A-label">Count</label><input className="A-input" type="number" min="1" max="20" value={count} onChange={function(e){setCount(parseInt(e.target.value)||5)}} style={{width:60}} /></div>
        <div><label className="A-label">Media Type (optional)</label>
          <select className="A-select" value={mediaType} onChange={function(e){setMediaType(e.target.value)}} style={{width:150}}>
            <option value="">Any</option>
            {MEDIA_TYPES.map(function(mt){return <option key={mt} value={mt}>{mt}</option>})}
          </select>
        </div>
        <div><label className="A-label">Difficulty (optional)</label>
          <select className="A-select" value={difficulty} onChange={function(e){setDifficulty(e.target.value)}} style={{width:100}}>
            <option value="">Any</option>
            {[1,2,3,4,5].map(function(d){return <option key={d} value={d}>{d}</option>})}
          </select>
        </div>
        <button className="A-btn signal" onClick={generate} disabled={loading || !prompt.trim()}>{loading ? 'GENERATING...' : 'GENERATE'}</button>
      </div>

      {error && <div style={{color:'var(--signal)',fontSize:11,marginBottom:16}}>Error: {error}</div>}

      {results && results.length === 0 && <div style={{color:'var(--text-dim)',padding:24}}>No cards generated. Try a different prompt.</div>}

      {results && results.map(function(card) {
        var exists = cards && cards[card.id];
        return (
          <div key={card.id} className="AI-card-preview">
            <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:8}}>
              <div>
                <span style={{fontSize:13,color:'#fff'}}>{card.content&&card.content.answer}</span>
                <span style={{fontSize:10,color:'var(--text-sec)',marginLeft:8}}>— {card.source}</span>
              </div>
              <div style={{display:'flex',gap:8}}>
                {exists && <span style={{fontSize:9,color:'var(--gold)',letterSpacing:1}}>EXISTS</span>}
                <button className="A-btn cyan" onClick={function(){acceptCard(card)}} disabled={exists} style={{fontSize:10,padding:'4px 10px'}}>ACCEPT</button>
              </div>
            </div>
            <div style={{display:'flex',gap:16,fontSize:10,color:'var(--text-sec)',flexWrap:'wrap'}}>
              <span>ID: {card.id}</span>
              <span>Code: {card.code}</span>
              <span>Type: {card.media_type}</span>
              <span>Diff: {card.difficulty}</span>
              <span style={{color:'var(--gold)'}}>LS: {card.scores&&card.scores.lineconic_score}</span>
              <span>Cat: {card.category}</span>
              <span>Themes: {(card.themes||[]).join(', ')}</span>
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ─── Score Cards ─────────────────────────────────────────
function AIScore(props) {
  var cards = props.cards, db = props.db, showToast = props.showToast, getToken = props.getToken, SCORE_FIELDS = props.SCORE_FIELDS;
  var [search, setSearch] = useState('');
  var [selected, setSelected] = useState({});
  var [loading, setLoading] = useState(false);
  var [results, setResults] = useState(null);

  var cardArray = useMemo(function() {
    if (!cards) return [];
    var arr = Object.values(cards);
    if (search) {
      var q = search.toLowerCase();
      arr = arr.filter(function(c){return (c.content&&c.content.answer||'').toLowerCase().includes(q) || (c.source||'').toLowerCase().includes(q)});
    }
    return arr;
  }, [cards, search]);

  function toggleSelect(id) { setSelected(function(p){var n=Object.assign({},p);if(n[id])delete n[id];else n[id]=true;return n}) }
  function selectAll() { var n={}; cardArray.forEach(function(c){n[c.id]=true}); setSelected(n); }

  var [scoreProgress, setScoreProgress] = useState(null);

  function scoreSelected() {
    var ids = Object.keys(selected);
    if (ids.length === 0) return;
    var allCards = ids.map(function(id){return cards[id]}).filter(Boolean);
    var BATCH_SIZE = 20;
    var batches = [];
    for (var i = 0; i < allCards.length; i += BATCH_SIZE) {
      batches.push(allCards.slice(i, i + BATCH_SIZE));
    }
    setLoading(true); setResults(null); setScoreProgress('0 / ' + allCards.length);
    var allScores = [];
    var done = 0;

    var failedBatches = 0;
    getToken().then(function(token) {
      function processBatch(idx) {
        if (idx >= batches.length) {
          setResults(allScores);
          setLoading(false);
          if (failedBatches > 0) {
            setScoreProgress((batches.length - failedBatches) + '/' + batches.length + ' batches completed, ' + failedBatches + ' failed');
          } else {
            setScoreProgress(null);
          }
          return;
        }
        return aiFetch('/api/ai/score-cards', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
          body: JSON.stringify({ cards: batches[idx] })
        }, 60000).then(function(res) {
          return res.json();
        }).then(function(data) {
          var scores = data.scores || [];
          allScores = allScores.concat(scores);
          done += batches[idx].length;
          setScoreProgress(done + ' / ' + allCards.length);
          return processBatch(idx + 1);
        }).catch(function(e) {
          failedBatches++;
          done += batches[idx].length;
          setScoreProgress(done + ' / ' + allCards.length + ' (batch ' + (idx + 1) + ' failed)');
          return processBatch(idx + 1);
        });
      }
      return processBatch(0);
    }).catch(function(e) { showToast('SCORE FAILED: ' + formatAiError(e)); setLoading(false); setScoreProgress(null); });
  }

  function applyScore(scoreObj) {
    var id = scoreObj.id;
    if (!cards[id]) return;
    var updated = JSON.parse(JSON.stringify(cards[id]));
    updated.scores = scoreObj.scores;
    db.ref('cards/cards/' + id).set(updated).then(function() { showToast('SCORES APPLIED: ' + id); })
    .catch(function(e) { showToast('APPLY FAILED: ' + e.message); });
  }

  function applyAll() {
    if (!results) return;
    var promises = results.map(function(s) {
      if (!cards[s.id]) return Promise.resolve();
      var updated = JSON.parse(JSON.stringify(cards[s.id]));
      updated.scores = s.scores;
      return db.ref('cards/cards/' + s.id).set(updated);
    });
    Promise.all(promises).then(function() { showToast('ALL SCORES APPLIED'); }).catch(function(e) { showToast('BULK APPLY FAILED: ' + e.message); });
  }

  var selCount = Object.keys(selected).length;

  return (
    <div>
      <div className="A-section-head">AI SCORING</div>
      <div style={{display:'flex',gap:12,marginBottom:16,alignItems:'end'}}>
        <div style={{flex:1}}><label className="A-label">Search cards to score</label><input className="A-input" value={search} onChange={function(e){setSearch(e.target.value)}} placeholder="Search..." /></div>
        <button className="A-btn" onClick={selectAll}>SELECT ALL</button>
        <button className="A-btn signal" onClick={scoreSelected} disabled={loading || selCount===0}>{loading ? 'SCORING ' + (scoreProgress || '...') : 'AI SCORE (' + selCount + ')'}</button>
      </div>

      {/* Card selection list */}
      {!results && (
        <div style={{maxHeight:400,overflow:'auto',border:'1px solid var(--border)'}}>
          {cardArray.map(function(c) {
            var sel = selected[c.id];
            return <div key={c.id} onClick={function(){toggleSelect(c.id)}} style={{
              display:'flex',alignItems:'center',gap:12,padding:'6px 12px',borderBottom:'1px solid rgba(42,42,42,.4)',cursor:'pointer',
              background:sel?'rgba(0,255,255,.05)':'transparent'
            }}>
              <div style={{width:16,height:16,border:'1px solid '+(sel?'var(--cyan)':'var(--border)'),background:sel?'var(--cyan)':'transparent',flexShrink:0}}></div>
              <div style={{flex:1,fontSize:11,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{c.content&&c.content.answer||'—'}</div>
              <div style={{fontSize:10,color:'var(--text-sec)'}}>{c.source||''}</div>
              <div style={{fontSize:10,color:'var(--gold)',minWidth:30,textAlign:'right'}}>{c.scores&&c.scores.lineconic_score||'—'}</div>
            </div>;
          })}
        </div>
      )}

      {/* Results */}
      {results && (
        <div>
          <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:12}}>
            <span style={{fontSize:11,color:'var(--text-sec)'}}>{results.length} cards scored</span>
            <button className="A-btn gold" onClick={applyAll}>APPLY ALL</button>
          </div>
          {results.map(function(s) {
            var current = cards[s.id];
            return <div key={s.id} className="AI-result">
              <div className="AI-result-head">
                <span style={{fontSize:12,color:'#fff'}}>{current&&current.content&&current.content.answer||s.id}</span>
                <button className="A-btn cyan" onClick={function(){applyScore(s)}} style={{fontSize:10,padding:'4px 10px'}}>APPLY</button>
              </div>
              <div className="AI-score-compare">
                {SCORE_FIELDS.map(function(sf) {
                  var oldVal = current&&current.scores&&current.scores[sf]||0;
                  var newVal = s.scores&&s.scores[sf]||0;
                  return <div key={sf} style={{display:'flex',justifyContent:'space-between',padding:'2px 0'}}>
                    <span style={{fontSize:9,textTransform:'uppercase',color:'var(--text-dim)'}}>{sf.replace(/_/g,' ')}</span>
                    <span><span className="old">{oldVal}</span> → <span className="new">{newVal}</span></span>
                  </div>;
                })}
                <div style={{display:'flex',justifyContent:'space-between',padding:'4px 0',borderTop:'1px solid var(--border)'}}>
                  <span style={{fontSize:10,fontWeight:700}}>LINECONIC SCORE</span>
                  <span><span className="old">{current&&current.scores&&current.scores.lineconic_score||0}</span> → <span className="new" style={{fontWeight:700}}>{s.scores&&s.scores.lineconic_score||0}</span></span>
                </div>
              </div>
            </div>;
          })}
        </div>
      )}
    </div>
  );
}

// ─── Analyze Content ─────────────────────────────────────
function AIAnalyze(props) {
  var cards = props.cards, showToast = props.showToast, getToken = props.getToken, MEDIA_TYPES = props.MEDIA_TYPES, onGenerateFrom = props.onGenerateFrom;
  var db = props.db;
  var [loading, setLoading] = useState(false);
  var [report, setReport] = useState(null);
  var [healthHistory, setHealthHistory] = useState(null);
  var [cronRunning, setCronRunning] = useState(false);

  // Load health check history
  useEffect(function() {
    var ref = firebase.database().ref('health_checks');
    ref.limitToLast(10).on('value', function(snap) {
      setHealthHistory(snap.val() || {});
    });
    return function() { ref.off(); };
  }, []);

  function runHealthCheck() {
    setCronRunning(true);
    getToken().then(function(token) {
      return aiFetch('/api/cron/library-health', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }
      }, 60000);
    }).then(function(res) {
      return res.json();
    }).then(function(data) {
      setCronRunning(false);
      if (data.skipped) {
        showToast('SKIPPED: ' + data.reason);
      } else {
        showToast('HEALTH CHECK: ' + data.health_score + '/100, ' + data.cards_generated + ' cards generated');
      }
    }).catch(function(e) { setCronRunning(false); showToast('HEALTH CHECK FAILED: ' + formatAiError(e)); });
  }

  function runAnalysis() {
    if (!cards) return;
    var cardArray = Object.values(cards);
    var byMedia = {}; var byDiff = {}; var byCategory = {}; var byTheme = {}; var sources = {};
    var totalScore = 0; var scored = 0;
    cardArray.forEach(function(c) {
      byMedia[c.media_type] = (byMedia[c.media_type]||0) + 1;
      byDiff[c.difficulty] = (byDiff[c.difficulty]||0) + 1;
      byCategory[c.category||'Uncategorized'] = (byCategory[c.category||'Uncategorized']||0) + 1;
      (c.themes||[]).forEach(function(t){byTheme[t]=(byTheme[t]||0)+1});
      sources[c.source] = (sources[c.source]||0) + 1;
      if (c.scores&&c.scores.lineconic_score>0) { totalScore+=c.scores.lineconic_score; scored++; }
    });
    var stats = {
      total: cardArray.length, byMedia: byMedia, byDifficulty: byDiff, byCategory: byCategory,
      byTheme: byTheme, topSources: Object.entries(sources).sort(function(a,b){return b[1]-a[1]}).slice(0,20),
      avgScore: scored>0?(totalScore/scored).toFixed(1):'0', scoredCount: scored
    };

    setLoading(true); setReport(null);
    getToken().then(function(token) {
      return aiFetch('/api/ai/analyze-content', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
        body: JSON.stringify({ stats: stats })
      });
    }).then(function(res) {
      return res.json();
    }).then(function(data) { setReport(data.report); setLoading(false); })
    .catch(function(e) { showToast('ANALYSIS FAILED: ' + formatAiError(e)); setLoading(false); });
  }

  return (
    <div>
      <div className="A-section-head">CONTENT GAP ANALYSIS</div>
      <div style={{display:'flex',gap:12,marginBottom:24}}>
        <button className="A-btn signal" onClick={runAnalysis} disabled={loading}>{loading ? 'ANALYZING...' : 'RUN ANALYSIS'}</button>
        <button className="A-btn cyan" onClick={runHealthCheck} disabled={cronRunning}>{cronRunning ? 'RUNNING...' : 'AUTO-HEAL (CRON)'}</button>
      </div>

      {/* Health check history */}
      {healthHistory && Object.keys(healthHistory).length > 0 && (
        <div style={{marginBottom:24,padding:16,border:'1px solid var(--border)',background:'var(--surface-01)'}}>
          <div className="A-section-head">HEALTH CHECK HISTORY</div>
          {Object.entries(healthHistory).sort(function(a,b){return b[0].localeCompare(a[0])}).slice(0,5).map(function(entry) {
            var ts = entry[0], h = entry[1];
            var scoreColor = h.health_score >= 70 ? '#4ade80' : h.health_score >= 40 ? 'var(--gold)' : 'var(--signal)';
            return (
              <div key={ts} style={{display:'flex',alignItems:'center',gap:16,padding:'6px 0',borderBottom:'1px solid rgba(42,42,42,.4)',fontSize:11}}>
                <span style={{color:'var(--text-dim)',minWidth:140}}>{h.timestamp ? new Date(h.timestamp).toLocaleDateString() + ' ' + new Date(h.timestamp).toLocaleTimeString() : ts}</span>
                <span style={{color:scoreColor,fontWeight:700,minWidth:50}}>{h.health_score}/100</span>
                <span style={{color:'var(--text-sec)',flex:1}}>{h.cards_generated > 0 ? h.cards_generated + ' cards auto-generated' : 'no generation needed'}</span>
                <span style={{fontSize:9,color:'var(--text-dim)'}}>{h.triggered_by || 'unknown'}</span>
              </div>
            );
          })}
        </div>
      )}

      {report && (
        <div>
          {/* Health score */}
          <div className="AN-card" style={{marginBottom:16}}>
            <div className="AN-card-val" style={{color:report.health_score>=70?'#4ade80':report.health_score>=40?'var(--gold)':'var(--signal)'}}>{report.health_score}/100</div>
            <div className="AN-card-label">CONTENT HEALTH SCORE</div>
            <div style={{fontSize:12,color:'var(--text-sec)',marginTop:8}}>{report.summary}</div>
          </div>

          {/* Media gaps */}
          {report.media_gaps && report.media_gaps.length > 0 && (
            <div className="A-section"><div className="A-section-head">MEDIA GAPS</div>
              {report.media_gaps.map(function(g,i) {
                return <div key={i} style={{padding:'8px 0',borderBottom:'1px solid rgba(42,42,42,.4)'}}>
                  <div style={{fontSize:11,color:'var(--signal)',textTransform:'uppercase'}}>{g.type}</div>
                  <div style={{fontSize:11,color:'var(--text-sec)',marginTop:2}}>{g.issue}</div>
                  <div style={{fontSize:11,color:'var(--cyan)',marginTop:2}}>{g.recommendation}</div>
                </div>;
              })}
            </div>
          )}

          {/* Expansion recommendations */}
          {report.expansion_recommendations && report.expansion_recommendations.length > 0 && (
            <div className="A-section"><div className="A-section-head">EXPANSION RECOMMENDATIONS</div>
              {report.expansion_recommendations.map(function(r,i) {
                var prioColors = {high:'var(--signal)',medium:'var(--gold)',low:'var(--text-sec)'};
                return <div key={i} style={{padding:'8px 0',borderBottom:'1px solid rgba(42,42,42,.4)'}}>
                  <div style={{display:'flex',gap:8,alignItems:'center'}}>
                    <span style={{fontSize:9,padding:'2px 6px',border:'1px solid '+(prioColors[r.priority]||'#555'),color:prioColors[r.priority],textTransform:'uppercase',letterSpacing:1}}>{r.priority}</span>
                    <span style={{fontSize:12,color:'#fff'}}>{r.area}</span>
                    {onGenerateFrom && <button className="A-btn cyan" style={{fontSize:9,padding:'2px 8px',marginLeft:'auto'}} onClick={function(){onGenerateFrom('Generate cards for: ' + r.area + '. ' + r.description)}}>GENERATE FROM THIS</button>}
                  </div>
                  <div style={{fontSize:11,color:'var(--text-sec)',marginTop:4}}>{r.description}</div>
                </div>;
              })}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ─── Marketing Copy ──────────────────────────────────────
function AIMarketing(props) {
  var showToast = props.showToast, getToken = props.getToken;
  var [type, setType] = useState('instagram');
  var [context, setContext] = useState('');
  var [loading, setLoading] = useState(false);
  var [result, setResult] = useState(null);

  function generate() {
    if (!context.trim()) return;
    setLoading(true); setResult(null);
    getToken().then(function(token) {
      return aiFetch('/api/ai/marketing', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
        body: JSON.stringify({ type: type, context: context })
      });
    }).then(function(res) {
      return res.json();
    }).then(function(data) { setResult(data.result); setLoading(false); })
    .catch(function(e) { showToast('GENERATION FAILED: ' + formatAiError(e)); setLoading(false); });
  }

  function copyToClipboard(text) {
    navigator.clipboard.writeText(text).then(function(){showToast('COPIED')}).catch(function(){showToast('COPY FAILED')});
  }

  return (
    <div>
      <div className="A-section-head">MARKETING COPY</div>
      <div style={{display:'flex',gap:12,marginBottom:16,alignItems:'end'}}>
        <div><label className="A-label">Type</label>
          <select className="A-select" value={type} onChange={function(e){setType(e.target.value)}} style={{width:200}}>
            <option value="instagram">Instagram Caption</option>
            <option value="show_description">Show Description</option>
            <option value="newsletter">Newsletter</option>
            <option value="creator_brief">Creator Brief</option>
          </select>
        </div>
        <button className="A-btn signal" onClick={generate} disabled={loading || !context.trim()}>{loading ? 'GENERATING...' : 'GENERATE'}</button>
      </div>
      <div style={{marginBottom:16}}>
        <label className="A-label">Context</label>
        <textarea className="A-textarea" rows="4" value={context} onChange={function(e){setContext(e.target.value)}} placeholder="e.g., 'Next show is March 20 at The Lexington, 90-min format, 80 capacity, tickets on Dice'" />
      </div>

      {result && (
        <div className="AI-result">
          {result.subject_line && <div style={{marginBottom:8}}><span className="A-label">Subject Line</span><div style={{fontSize:13,color:'var(--gold)',cursor:'pointer'}} onClick={function(){copyToClipboard(result.subject_line)}}>{result.subject_line}</div></div>}
          {result.preview_text && <div style={{marginBottom:8}}><span className="A-label">Preview Text</span><div style={{fontSize:11,color:'var(--text-sec)',cursor:'pointer'}} onClick={function(){copyToClipboard(result.preview_text)}}>{result.preview_text}</div></div>}
          <div style={{marginBottom:8}}><span className="A-label">Content</span>
            <div style={{fontSize:12,color:'#fff',whiteSpace:'pre-wrap',lineHeight:1.6,cursor:'pointer',padding:12,background:'var(--surface-2)',border:'1px solid var(--border)'}} onClick={function(){copyToClipboard(result.content)}}>{result.content}</div>
          </div>
          {result.hashtags && <div><span className="A-label">Hashtags</span><div style={{fontSize:11,color:'var(--cyan)',cursor:'pointer'}} onClick={function(){copyToClipboard(result.hashtags.map(function(h){return '#'+h}).join(' '))}}>{result.hashtags.map(function(h){return '#'+h}).join(' ')}</div></div>}
          {result.brief_sections && (function(){
            var sections = result.brief_sections;
            var labels = {brand_context:'BRAND CONTEXT',campaign_objective:'CAMPAIGN OBJECTIVE',deliverables:'DELIVERABLES',creative_direction:'CREATIVE DIRECTION',talking_points:'TALKING POINTS',exclusions:'EXCLUSIONS',usage_rights:'USAGE RIGHTS',timeline:'TIMELINE'};
            return React.createElement('div',{style:{marginTop:8}},
              Object.keys(labels).map(function(key){
                if(!sections[key])return null;
                var val = Array.isArray(sections[key]) ? sections[key].join('\n') : sections[key];
                return React.createElement('div',{key:key,style:{marginBottom:12}},
                  React.createElement('span',{className:'A-label'},labels[key]),
                  React.createElement('div',{style:{fontSize:12,color:'#fff',whiteSpace:'pre-wrap',lineHeight:1.5,cursor:'pointer',padding:8,background:'var(--surface-2)',border:'1px solid var(--border)'},onClick:function(){copyToClipboard(val)}},val)
                );
              })
            );
          })()}
          <div style={{marginTop:12,fontSize:9,color:'var(--text-dim)'}}>Click any section to copy</div>
        </div>
      )}
    </div>
  );
}

// ─── Pre-filter Candidates for Clarity Audit ────────────
var STOP_WORDS = ['the','a','an','in','on','at','to','for','of','is','it','and','or','but','my','your','i','me','we','he','she','they','you','that','this','with','from','by','as','not','no','so','do','did','was','be','am','are'];

function normalize(str) {
  return (str || '').toLowerCase().replace(/[^a-z0-9\s]/g, '').trim();
}

function extractTitle(source, mediaType) {
  if (!source) return '';
  if (mediaType === 'music') {
    var parts = source.split(' - ');
    return parts.length >= 2 ? parts.slice(1).join(' - ').trim() : source.trim();
  }
  // Movies/TV: strip year suffix like "(2019)"
  return source.replace(/\s*\(\d{4}\)\s*$/, '').trim();
}

function jaccardSimilarity(a, b) {
  var wordsA = normalize(a).split(/\s+/).filter(function(w) { return STOP_WORDS.indexOf(w) === -1 && w.length > 1; });
  var wordsB = normalize(b).split(/\s+/).filter(function(w) { return STOP_WORDS.indexOf(w) === -1 && w.length > 1; });
  if (wordsA.length === 0 || wordsB.length === 0) return 0;
  var setA = {}; wordsA.forEach(function(w) { setA[w] = true; });
  var setB = {}; wordsB.forEach(function(w) { setB[w] = true; });
  var intersection = 0;
  Object.keys(setA).forEach(function(w) { if (setB[w]) intersection++; });
  var union = Object.keys(Object.assign({}, setA, setB)).length;
  return union > 0 ? intersection / union : 0;
}

function preFilterCandidates(cards) {
  var cardArray = Object.values(cards || {});
  var candidates = [];

  cardArray.forEach(function(card) {
    var answer = card.content && card.content.answer || '';
    var source = card.source || '';
    var mediaType = card.media_type || '';
    var title = extractTitle(source, mediaType);
    var normAnswer = normalize(answer);
    var normTitle = normalize(title);
    var reasons = [];

    if (!normAnswer || !normTitle) return;

    // 1. Answer IS title
    if (normAnswer === normTitle) reasons.push('answer_is_title');
    // 2. Answer contains title
    else if (normAnswer.indexOf(normTitle) !== -1) reasons.push('answer_contains_title');
    // 3. Title contains answer
    else if (normTitle.indexOf(normAnswer) !== -1) reasons.push('title_contains_answer');

    // 4. Single word answer
    if (answer.trim().split(/\s+/).length === 1) reasons.push('single_word_answer');

    // 5. High Jaccard overlap
    if (jaccardSimilarity(answer, title) >= 0.5) reasons.push('high_overlap');

    if (reasons.length > 0) {
      candidates.push({
        id: card.id,
        answer: answer,
        source: source,
        media_type: mediaType,
        code: card.code || '',
        preFilterReasons: reasons
      });
    }
  });

  return candidates;
}

// ─── AI Audit Component ─────────────────────────────────
function AIAudit(props) {
  var cards = props.cards, db = props.db, showToast = props.showToast, getToken = props.getToken, generateCode = props.generateCode;
  var [candidates, setCandidates] = useState(null);
  var [reasonBreakdown, setReasonBreakdown] = useState(null);
  var [results, setResults] = useState(null);
  var [loading, setLoading] = useState(false);
  var [progress, setProgress] = useState(null);
  var [error, setError] = useState(null);
  var [verdictFilter, setVerdictFilter] = useState('all');
  var [dismissed, setDismissed] = useState({});

  function scanLibrary() {
    if (!cards) return;
    var found = preFilterCandidates(cards);
    setCandidates(found);
    setResults(null);
    setError(null);
    setDismissed({});

    // Build reason breakdown
    var counts = {};
    found.forEach(function(c) {
      c.preFilterReasons.forEach(function(r) { counts[r] = (counts[r] || 0) + 1; });
    });
    setReasonBreakdown(counts);
  }

  function runAudit() {
    if (!candidates || candidates.length === 0) return;
    var BATCH_SIZE = 20;
    var batches = [];
    for (var i = 0; i < candidates.length; i += BATCH_SIZE) {
      batches.push(candidates.slice(i, i + BATCH_SIZE));
    }
    setLoading(true); setResults(null); setError(null);
    setProgress('0 / ' + candidates.length);
    var allResults = [];
    var done = 0;
    var failedBatches = 0;

    getToken().then(function(token) {
      function processBatch(idx) {
        if (idx >= batches.length) {
          setResults(allResults);
          setLoading(false);
          if (failedBatches > 0) {
            setProgress((batches.length - failedBatches) + '/' + batches.length + ' batches completed, ' + failedBatches + ' failed');
          } else {
            setProgress(null);
          }
          return;
        }
        return aiFetch('/api/ai/audit-clarity', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
          body: JSON.stringify({ cards: batches[idx] })
        }, 60000).then(function(res) {
          return res.json();
        }).then(function(data) {
          var batch = data.results || [];
          allResults = allResults.concat(batch);
          done += batches[idx].length;
          setProgress(done + ' / ' + candidates.length);
          return processBatch(idx + 1);
        }).catch(function(e) {
          failedBatches++;
          done += batches[idx].length;
          setProgress(done + ' / ' + candidates.length + ' (batch ' + (idx + 1) + ' failed)');
          return processBatch(idx + 1);
        });
      }
      return processBatch(0);
    }).catch(function(e) { setError(formatAiError(e)); setLoading(false); setProgress(null); });
  }

  function flagForReview(id) {
    if (!cards[id]) return;
    db.ref('cards/cards/' + id + '/status').set('review').then(function() {
      showToast('FLAGGED: ' + id);
    }).catch(function(e) { showToast('FLAG FAILED: ' + e.message); });
  }

  function dismissCard(id) {
    setDismissed(function(p) { var n = Object.assign({}, p); n[id] = true; return n; });
  }

  function applyFix(result) {
    var id = result.id;
    if (!cards[id] || !result.suggested_answer) return;
    var updated = JSON.parse(JSON.stringify(cards[id]));
    updated.content.answer = result.suggested_answer;
    updated.code = generateCode(result.suggested_answer);
    updated.content.primary = updated.code.split('.').join('.  ').trim();
    updated.status = 'review';
    db.ref('cards/cards/' + id).set(updated).then(function() {
      showToast('FIXED: ' + id + ' → "' + result.suggested_answer + '"');
    }).catch(function(e) { showToast('FIX FAILED: ' + e.message); });
  }

  function flagAllNonClear() {
    if (!results) return;
    var nonClear = results.filter(function(r) { return r.verdict !== 'clear' && !dismissed[r.id]; });
    if (nonClear.length === 0) { showToast('Nothing to flag'); return; }
    var updates = {};
    nonClear.forEach(function(r) { updates['cards/cards/' + r.id + '/status'] = 'review'; });
    db.ref().update(updates).then(function() {
      showToast(nonClear.length + ' CARDS FLAGGED FOR REVIEW');
    }).catch(function(e) { showToast('BULK FLAG FAILED: ' + e.message); });
  }

  var verdictColors = { title_repeat: 'var(--signal)', ambiguous: 'var(--gold)', fixable: 'var(--cyan)', clear: '#4ade80' };
  var verdictLabels = { title_repeat: 'TITLE REPEAT', ambiguous: 'AMBIGUOUS', fixable: 'FIXABLE', clear: 'CLEAR' };

  var filteredResults = results ? results.filter(function(r) {
    if (dismissed[r.id]) return false;
    if (verdictFilter === 'all') return true;
    return r.verdict === verdictFilter;
  }) : null;

  var verdictCounts = {};
  if (results) {
    results.forEach(function(r) {
      if (!dismissed[r.id]) verdictCounts[r.verdict] = (verdictCounts[r.verdict] || 0) + 1;
    });
  }

  return (
    <div>
      <div className="A-section-head">ANSWER CLARITY AUDIT</div>
      <div style={{fontSize:10,color:'var(--text-dim)',marginBottom:16,letterSpacing:.5}}>Flags cards where the answer overlaps with the source title</div>

      {/* Scan button */}
      {!results && (
        <div>
          <button className="A-btn signal" onClick={scanLibrary} disabled={!cards} style={{marginBottom:16}}>SCAN LIBRARY</button>

          {candidates && (
            <div style={{marginBottom:16,padding:16,border:'1px solid var(--border)',background:'var(--surface-01)'}}>
              <div style={{fontSize:13,color:'#fff',marginBottom:8}}>{candidates.length} candidates found</div>
              {reasonBreakdown && Object.entries(reasonBreakdown).map(function(entry) {
                return <div key={entry[0]} style={{display:'flex',justifyContent:'space-between',padding:'4px 0',fontSize:11}}>
                  <span style={{color:'var(--text-sec)'}}>{entry[0].replace(/_/g, ' ')}</span>
                  <span style={{color:'var(--gold)'}}>{entry[1]}</span>
                </div>;
              })}
              <button className="A-btn signal" onClick={runAudit} disabled={loading || candidates.length === 0} style={{marginTop:12}}>
                {loading ? 'AUDITING ' + (progress || '...') : 'RUN AI AUDIT (' + candidates.length + ')'}
              </button>
            </div>
          )}
        </div>
      )}

      {error && <div style={{color:'var(--signal)',fontSize:11,marginBottom:16}}>Error: {error}</div>}

      {/* Results */}
      {results && (
        <div>
          {/* Verdict filter tabs */}
          <div style={{display:'flex',gap:8,marginBottom:16,flexWrap:'wrap',alignItems:'center'}}>
            <button className={'A-btn' + (verdictFilter==='all'?' signal':'')} onClick={function(){setVerdictFilter('all')}} style={{fontSize:10}}>ALL ({results.filter(function(r){return !dismissed[r.id]}).length})</button>
            {['title_repeat','ambiguous','fixable','clear'].map(function(v) {
              var count = verdictCounts[v] || 0;
              return <button key={v} className={'A-btn' + (verdictFilter===v?' signal':'')} onClick={function(){setVerdictFilter(v)}} style={{fontSize:10,borderColor:verdictColors[v],color:verdictFilter===v?'#fff':verdictColors[v]}}>
                {verdictLabels[v]} ({count})
              </button>;
            })}
            <div style={{marginLeft:'auto'}}>
              <button className="A-btn gold" onClick={flagAllNonClear} style={{fontSize:10}}>FLAG ALL NON-CLEAR</button>
            </div>
          </div>

          {filteredResults.length === 0 && <div style={{color:'var(--text-dim)',padding:24}}>No cards match this filter.</div>}

          {filteredResults.map(function(r) {
            var card = cards[r.id];
            var color = verdictColors[r.verdict] || 'var(--text-sec)';
            return (
              <div key={r.id} className="AI-card-preview" style={{borderLeft:'3px solid ' + color}}>
                <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:8}}>
                  <div style={{flex:1}}>
                    <div style={{display:'flex',gap:8,alignItems:'center',marginBottom:4}}>
                      <span style={{fontSize:9,padding:'2px 6px',border:'1px solid ' + color,color:color,textTransform:'uppercase',letterSpacing:1}}>{verdictLabels[r.verdict] || r.verdict}</span>
                      <span style={{fontSize:9,color:'var(--text-dim)'}}>{Math.round((r.confidence || 0) * 100)}% confident</span>
                    </div>
                    <div style={{fontSize:13,color:'#fff',marginBottom:2}}>"{card && card.content && card.content.answer || r.id}"</div>
                    <div style={{fontSize:11,color:'var(--text-sec)',marginBottom:4}}>{card && card.source || ''} ({card && card.media_type || ''})</div>
                    <div style={{fontSize:10,color:'var(--text-dim)'}}>{r.reason}</div>
                    {r.suggested_answer && (
                      <div style={{fontSize:11,color:'var(--cyan)',marginTop:4}}>Suggested: "{r.suggested_answer}"</div>
                    )}
                  </div>
                  <div style={{display:'flex',gap:6,flexShrink:0}}>
                    <button className="A-btn" onClick={function(){dismissCard(r.id)}} style={{fontSize:9,padding:'4px 8px'}}>DISMISS</button>
                    <button className="A-btn gold" onClick={function(){flagForReview(r.id)}} style={{fontSize:9,padding:'4px 8px'}}>FLAG</button>
                    {r.verdict === 'fixable' && r.suggested_answer && (
                      <button className="A-btn cyan" onClick={function(){applyFix(r)}} style={{fontSize:9,padding:'4px 8px'}}>APPLY FIX</button>
                    )}
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}
