'use client'

import { useState, useEffect, useMemo, useCallback } from 'react'
import dynamic from 'next/dynamic'
import { useRouter } from 'next/navigation'
import { useLanguage } from '@/lib/i18n'
import { motion, AnimatePresence, useReducedMotion } from 'motion/react'
import {
  Sparkles,
  RefreshCw,
  Layers,
  Trophy,
  Zap,
  Lightbulb,
  Sliders,
  CheckCircle2,
  Clock,
  AlertCircle,
  ChevronRight,
  ChevronDown,
  Database,
  Menu,
  Network,
  List,
  Search,
  ArrowUpDown,
  X,
} from 'lucide-react'
import { toast } from 'sonner'
import { useNotePeek, NotePeekPanel } from '@/components/note-peek'
import { createNote } from '@/app/actions/notes'
import { emitNoteChange } from '@/lib/note-change-sync'

const NetworkGraph = dynamic(
  () => import('@/components/network-graph').then(m => ({ default: m.NetworkGraph })),
  {
    loading: () => (
      <div className="w-full h-full flex items-center justify-center">
        <RefreshCw className="animate-spin text-ochre/40" size={32} />
      </div>
    ),
    ssr: false,
  }
)

interface Note {
  id: string
  title: string | null
  content: string
  clusterId?: number
}

interface Cluster {
  id: string
  clusterId: number
  name?: string
  noteIds: string[]
  color?: string
}

interface BridgeNote {
  noteId: string
  bridgeScore: number
  clustersConnected: number[]
  clusterNames?: string[]
  note?: {
    id: string
    title: string | null
    content: string
  }
}

interface BridgeSuggestion {
  clusterAId: number
  clusterBId: number
  clusterAName: string
  clusterBName: string
  suggestedTitle: string
  suggestedContent: string
  justification: string
}

const COLOR_PALETTE = ['#F87171', '#60A5FA', '#34D399', '#FBBF24', '#A78BFA', '#F472B6', '#2DD4BF']

export default function InsightsPage() {
  const router = useRouter()
  const { t, language: locale } = useLanguage()

  const formatSyncTime = useCallback(
    (date: Date) =>
      date.toLocaleTimeString(locale, { hour: '2-digit', minute: '2-digit' }),
    [locale]
  )
  const [notes, setNotes] = useState<Note[]>([])
  const [clusters, setClusters] = useState<Cluster[]>([])
  const [bridgeNotes, setBridgeNotes] = useState<BridgeNote[]>([])
  const [suggestions, setSuggestions] = useState<BridgeSuggestion[]>([])
  const [loading, setLoading] = useState(true)
  const [isCalculating, setIsCalculating] = useState(false)
  const [isReindexing, setIsReindexing] = useState(false)
  const [embeddingStats, setEmbeddingStats] = useState<{ indexed: number; total: number } | null>(null)
  const [isStale, setIsStale] = useState(false)
  const [selectedClusterId, setSelectedClusterId] = useState<string | null>(null)
  const [viewMode, setViewMode] = useState<'graph' | 'dashboard'>('dashboard')
  const [graphMode, setGraphMode] = useState<'visual' | 'list'>('visual')
  const [listFilter, setListFilter] = useState('')
  const [listSort, setListSort] = useState<'size' | 'alpha' | 'bridges'>('size')
  /** Un seul cluster déplié à la fois (null = tous repliés) */
  const [expandedListClusterId, setExpandedListClusterId] = useState<string | null>(null)
  const [dashboardFilter, setDashboardFilter] = useState('')
  const [bridgesShowAll, setBridgesShowAll] = useState(false)
  const [isolatedShowAll, setIsolatedShowAll] = useState(false)
  const [suggestionsShowAll, setSuggestionsShowAll] = useState(false)
  const [actingSuggestionKey, setActingSuggestionKey] = useState<string | null>(null)
  const [dashboardSectionOpen, setDashboardSectionOpen] = useState({
    recalc: false,
    isolated: true,
    bridges: true,
    suggestions: false,
  })
  const [lastSyncTime, setLastSyncTime] = useState<string>('')
  const peek = useNotePeek()
  const prefersReducedMotion = useReducedMotion()

  const DASHBOARD_PREVIEW = 8
  const SUGGESTIONS_PREVIEW = 3

  useEffect(() => {
    loadInitialData()
  }, [])

  // ─── Données calculées ───────────────────────────────────────────────────────

  const selectedCluster = useMemo(
    () => clusters.find(c => c.id === selectedClusterId) ?? null,
    [clusters, selectedClusterId]
  )

  const selectedClusterNotes = useMemo(
    () => (selectedCluster ? notes.filter(n => selectedCluster.noteIds.includes(n.id)) : []),
    [notes, selectedCluster]
  )

  const isolatedClusters = useMemo(() => {
    const networkedIds = new Set(
      bridgeNotes.flatMap(b => b.clustersConnected.map(cid => String(cid)))
    )
    return clusters.filter(c => !networkedIds.has(c.id))
  }, [clusters, bridgeNotes])

  const listClusters = useMemo(() => {
    const q = listFilter.trim().toLowerCase()
    const rows = clusters.map(cluster => {
      const clusterNotes = notes.filter(n => cluster.noteIds.includes(n.id))
      const clusterBridges = bridgeNotes.filter(b =>
        b.clustersConnected?.some(cid => String(cid) === cluster.id || cid === cluster.clusterId)
      )
      const name =
        cluster.name ||
        t('insightsView.clusterFallback', { index: cluster.clusterId })
      const matchingNotes = q
        ? clusterNotes.filter(n =>
            (n.title || '').toLowerCase().includes(q)
          )
        : clusterNotes
      const nameMatches = !q || name.toLowerCase().includes(q)
      return { cluster, name, clusterNotes, matchingNotes, clusterBridges, nameMatches }
    })

    const filtered = q
      ? rows.filter(r => r.nameMatches || r.matchingNotes.length > 0)
      : rows

    const sorted = [...filtered].sort((a, b) => {
      if (listSort === 'alpha') return a.name.localeCompare(b.name, locale)
      if (listSort === 'bridges') {
        const d = b.clusterBridges.length - a.clusterBridges.length
        return d !== 0 ? d : b.clusterNotes.length - a.clusterNotes.length
      }
      // size (default)
      const d = b.clusterNotes.length - a.clusterNotes.length
      return d !== 0 ? d : a.name.localeCompare(b.name, locale)
    })

    return sorted
  }, [clusters, notes, bridgeNotes, listFilter, listSort, locale, t])

  const bridgeList = useMemo(
    () =>
      bridgeNotes.map(b => ({
        ...b,
        title: b.note?.title || t('insightsView.unknownNote'),
      })),
    [bridgeNotes, t]
  )

  const filteredIsolatedClusters = useMemo(() => {
    const q = dashboardFilter.trim().toLowerCase()
    if (!q) return isolatedClusters
    return isolatedClusters.filter(c =>
      (c.name || t('insightsView.clusterFallback', { index: c.clusterId }))
        .toLowerCase()
        .includes(q)
    )
  }, [isolatedClusters, dashboardFilter, t])

  const filteredBridgeList = useMemo(() => {
    const q = dashboardFilter.trim().toLowerCase()
    const list = [...bridgeList].sort((a, b) => b.bridgeScore - a.bridgeScore)
    if (!q) return list
    return list.filter(b => {
      const titleMatch = b.title.toLowerCase().includes(q)
      const clusterMatch = b.clustersConnected.some(cid => {
        const cluster = clusters.find(c => c.id === String(cid))
        const name =
          cluster?.name || t('insightsView.clusterFallback', { index: cid })
        return name.toLowerCase().includes(q)
      })
      return titleMatch || clusterMatch
    })
  }, [bridgeList, dashboardFilter, clusters, t])

  const visibleIsolatedClusters = isolatedShowAll || dashboardFilter.trim()
    ? filteredIsolatedClusters
    : filteredIsolatedClusters.slice(0, DASHBOARD_PREVIEW)

  const visibleBridgeList = bridgesShowAll || dashboardFilter.trim()
    ? filteredBridgeList
    : filteredBridgeList.slice(0, DASHBOARD_PREVIEW)

  /** Affiche au plus 2 thèmes (pair de brokerage) ; le reste derrière +N. */
  const getBridgeThemeSlice = useCallback((clusterIds: number[]) => {
    const ranked = [...clusterIds].sort((a, b) => {
      const sizeA = clusters.find(c => c.id === String(a))?.noteIds.length ?? 0
      const sizeB = clusters.find(c => c.id === String(b))?.noteIds.length ?? 0
      return sizeB - sizeA
    })
    return {
      primary: ranked.slice(0, 2),
      extraCount: Math.max(0, ranked.length - 2),
    }
  }, [clusters])

  // ─── Chargement initial ──────────────────────────────────────────────────────

  const loadInitialData = async () => {
    setLoading(true)
    try {
      const res = await fetch('/api/clusters')
      if (res.ok) {
        const data = await res.json()

        if (data.clusters?.length > 0) {
          const clustersWithColors = data.clusters.map((c: Cluster, i: number) => ({
            ...c,
            id: c.clusterId.toString(),
            color: COLOR_PALETTE[i % COLOR_PALETTE.length]
          }))
          setNotes(data.notes || [])
          setClusters(clustersWithColors)
          setIsStale(!!data.stale)

          // Bridge notes incluses dans la réponse GET /clusters (enrichies)
          if (data.bridgeNotes?.length > 0) {
            setBridgeNotes(data.bridgeNotes)
          } else {
            const bridgeRes = await fetch('/api/bridge-notes?details=true')
            if (bridgeRes.ok) {
              const bridgeData = await bridgeRes.json()
              setBridgeNotes(bridgeData.bridgeNotes || [])
            }
          }

          const suggestionsRes = await fetch('/api/bridge-notes/suggestions')
          if (suggestionsRes.ok) {
            const suggestionsData = await suggestionsRes.json()
            setSuggestions(suggestionsData.suggestions || [])
          }

          setLastSyncTime(formatSyncTime(new Date()))
          if (typeof data.embeddingCount === 'number' && typeof data.totalNotes === 'number') {
            setEmbeddingStats({ indexed: data.embeddingCount, total: data.totalNotes })
          }
        } else {
          setIsStale(false)
          if (typeof data.embeddingCount === 'number' && typeof data.totalNotes === 'number') {
            setEmbeddingStats({ indexed: data.embeddingCount, total: data.totalNotes })
          }
        }
      }
    } catch (error) {
      console.error('Error loading data:', error)
    } finally {
      setLoading(false)
    }
  }

  const handleReindexEmbeddings = async () => {
    setIsReindexing(true)
    try {
      const res = await fetch('/api/notes/reindex', { method: 'POST' })
      if (!res.ok) throw new Error('reindex failed')
      const data = await res.json()
      setEmbeddingStats(prev => ({
        indexed: data.count ?? prev?.indexed ?? 0,
        total: data.total ?? prev?.total ?? notes.length,
      }))
      setLastSyncTime(formatSyncTime(new Date()))
      setIsStale(true)
    } catch (error) {
      console.error('Error reindexing embeddings:', error)
    } finally {
      setIsReindexing(false)
    }
  }

  // ─── Analyse (POST) ──────────────────────────────────────────────────────────

  const performAnalysis = async () => {
    setIsCalculating(true)
    try {
      const res = await fetch('/api/clusters', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ force: true })
      })

      if (res.ok) {
        const data = await res.json()
        const clusterCount = data.clusters?.length || 0
        if (clusterCount === 0) {
          toast.info(t('insightsView.analysisNoClusters'))
        } else {
          toast.success(t('insightsView.analysisSuccess', { count: clusterCount }))
        }
        const clustersWithColors = (data.clusters || []).map((c: Cluster, i: number) => ({
          ...c,
          id: c.clusterId.toString(),
          color: COLOR_PALETTE[i % COLOR_PALETTE.length]
        }))
        setNotes(data.notes || [])
        setClusters(clustersWithColors)
        setBridgeNotes(data.bridgeNotes || [])
        setIsStale(false)

        const suggestionsRes = await fetch('/api/bridge-notes/suggestions')
        if (suggestionsRes.ok) {
          const suggestionsData = await suggestionsRes.json()
          setSuggestions(suggestionsData.suggestions || [])
        }

        setLastSyncTime(formatSyncTime(new Date()))
        if (data.notes?.length) {
          setEmbeddingStats(prev => ({
            indexed: prev?.indexed ?? data.notes.length,
            total: data.notes.length,
          }))
        }
      }
    } catch (error) {
      console.error('Error running analysis:', error)
      toast.error(t('insightsView.analysisFailed'))
    } finally {
      setIsCalculating(false)
    }
  }

  const handleNoteClick = (noteId: string) => {
    peek.open(noteId)
  }

  const visibleSuggestions = suggestionsShowAll
    ? suggestions.slice(0, 12)
    : suggestions.slice(0, SUGGESTIONS_PREVIEW)

  const handleCreateSuggestion = useCallback(async (s: BridgeSuggestion) => {
    const key = `${s.clusterAId}-${s.clusterBId}`
    setActingSuggestionKey(key)
    try {
      const html = `<p>${s.suggestedContent.replace(/</g, '&lt;')}</p><p><em>${s.justification.replace(/</g, '&lt;')}</em></p>`
      const note = await createNote({ title: s.suggestedTitle, content: html })
      if (!note) throw new Error('create failed')
      emitNoteChange({ type: 'created', note })
      await fetch('/api/bridge-notes', {
        method: 'DELETE',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ clusterAId: s.clusterAId, clusterBId: s.clusterBId }),
      })
      setSuggestions(prev =>
        prev.filter(x => !(x.clusterAId === s.clusterAId && x.clusterBId === s.clusterBId))
      )
      toast.success(t('insightsView.suggestions.created'))
      router.push(`/home?openNote=${note.id}`)
    } catch {
      toast.error(t('insightsView.suggestions.createError'))
    } finally {
      setActingSuggestionKey(null)
    }
  }, [router, t])

  const handleDismissSuggestion = useCallback(async (s: BridgeSuggestion) => {
    const key = `${s.clusterAId}-${s.clusterBId}`
    setActingSuggestionKey(key)
    try {
      const res = await fetch('/api/bridge-notes', {
        method: 'DELETE',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ clusterAId: s.clusterAId, clusterBId: s.clusterBId }),
      })
      if (!res.ok) throw new Error()
      setSuggestions(prev =>
        prev.filter(x => !(x.clusterAId === s.clusterAId && x.clusterBId === s.clusterBId))
      )
    } catch {
      toast.error(t('insightsView.suggestions.dismissError'))
    } finally {
      setActingSuggestionKey(null)
    }
  }, [t])

  const isRtl = locale === 'fa' || locale === 'ar'

  const motionConfig = prefersReducedMotion
    ? { initial: false as const, animate: { opacity: 1, y: 0 }, transition: { duration: 0 } }
    : {}

  // ─── Rendu ───────────────────────────────────────────────────────────────────────────

  return (
    <div className="h-full flex flex-col bg-[#F9F8F6] dark:bg-[#0D0D0D] overflow-hidden">

      {/* ── Header ── */}
      <div className="p-6 sm:p-8 border-b border-border/20 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between sticky top-0 bg-[#F9F8F6]/80 dark:bg-[#0D0D0D]/80 backdrop-blur-md z-30 shrink-0">
        <div className="flex items-center gap-4">
          <button
            className="p-2 -ms-1 text-foreground hover:bg-foreground/5 rounded-lg transition-colors shrink-0 cursor-pointer focus-visible:ring-2 focus-visible:ring-ochre/50 focus-visible:outline-none"
            onClick={() => window.dispatchEvent(new CustomEvent('toggle-insights-sidebar'))}
            aria-label="Toggle sidebar"
          >
            <Menu size={22} />
          </button>
          <div>
          <div className="flex items-center gap-3 mb-1">
            <div className="w-8 h-8 rounded-lg bg-ochre/10 flex items-center justify-center text-ochre">
              <Sparkles size={18} />
            </div>
            <h1 className="text-xl sm:text-2xl font-serif font-medium text-ink dark:text-dark-ink">
              {t('insightsView.title')}
            </h1>
          </div>
          <p className="text-[10px] text-concrete tracking-[0.25em] uppercase font-bold">
            {t('insightsView.subtitle')}
          </p>
          <div className="flex items-center gap-1.5 mt-1.5 text-[10px] text-concrete">
            <span>{t('insightsView.semanticGraphLegend')}</span>
          </div>
          </div>
        </div>

        <div className="flex items-center justify-between sm:justify-end gap-3">
          {/* Tab switcher mobile */}
          <div className="flex lg:hidden p-1 bg-black/5 dark:bg-white/5 rounded-xl shrink-0">
            <button
              onClick={() => setViewMode('graph')}
              className={`px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider rounded-lg transition-all ${
                viewMode === 'graph'
                  ? 'bg-white dark:bg-black text-ink dark:text-dark-ink shadow-sm'
                  : 'text-concrete'
              }`}
            >
              {t('insightsView.viewGraph')}
            </button>
            <button
              onClick={() => setViewMode('dashboard')}
              className={`px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider rounded-lg transition-all ${
                viewMode === 'dashboard'
                  ? 'bg-white dark:bg-black text-ink dark:text-dark-ink shadow-sm'
                  : 'text-concrete'
              }`}
            >
              {t('insightsView.viewDashboard')}
            </button>
          </div>

          <button
            onClick={performAnalysis}
            disabled={isCalculating}
            className="flex items-center gap-2 px-5 py-2.5 bg-ink text-paper dark:bg-white dark:text-black rounded-full text-xs font-bold uppercase tracking-widest hover:scale-105 active:scale-95 transition-all disabled:opacity-50 shadow-sm"
          >
            {isCalculating ? (
              <RefreshCw size={13} className="animate-spin" />
            ) : (
              <RefreshCw size={13} />
            )}
            {isCalculating ? t('insightsView.mapping') : t('insightsView.resync')}
          </button>
        </div>
      </div>

      {/* ── Chargement ── */}
      {loading && (
        <div className="flex-1 flex items-center justify-center">
          <motion.div
            initial={{ opacity: 0, scale: 0.9 }}
            animate={{ opacity: 1, scale: 1 }}
            className="text-center space-y-4"
          >
            <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-ochre mx-auto" />
            <p className="text-sm text-concrete">{t('insightsView.loading')}</p>
          </motion.div>
        </div>
      )}

      {/* ── État vide ── */}
      {!loading && clusters.length === 0 && !isCalculating && (
        <div className="flex-1 flex items-center justify-center">
          <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            className="text-center max-w-sm px-6"
          >
            <div className="w-20 h-20 rounded-2xl bg-ochre/10 flex items-center justify-center mx-auto mb-6">
              <Sparkles size={32} className="text-ochre/60" />
            </div>
            <h3 className="text-xl font-serif font-medium text-ink dark:text-dark-ink mb-3">
              {t('insightsView.emptyTitle')}
            </h3>
            <p className="text-sm text-concrete leading-relaxed mb-6">
              {embeddingStats && embeddingStats.total < 10
                ? t('insightsView.emptyNeedMoreNotes', { count: 10 - embeddingStats.total })
                : t('insightsView.emptyDescription')}
            </p>
            <button
              onClick={performAnalysis}
              disabled={isCalculating}
              className="inline-flex items-center gap-2 px-6 py-3 bg-ink text-paper dark:bg-white dark:text-black rounded-full text-xs font-bold uppercase tracking-widest hover:scale-105 active:scale-95 transition-all disabled:opacity-50"
            >
              <RefreshCw size={14} />
              {t('insightsView.analyzeNow')}
            </button>
          </motion.div>
        </div>
      )}

      {/* ── Calcul en cours ── */}
      {isCalculating && !loading && (
        <div className="flex-1 flex items-center justify-center">
          <motion.div
            initial={{ opacity: 0, scale: 0.9 }}
            animate={{ opacity: 1, scale: 1 }}
            className="text-center space-y-6 max-w-xs"
          >
            <div className="w-16 h-16 rounded-2xl bg-ochre/10 flex items-center justify-center mx-auto">
              <RefreshCw size={28} className="text-ochre animate-spin" />
            </div>
            <div className="space-y-2">
              <p className="text-sm font-semibold text-ink dark:text-dark-ink">
                {t('insightsView.mappingTitle')}
              </p>
              <p className="text-xs text-concrete leading-relaxed">
                {t('insightsView.mappingHint')}
              </p>
            </div>
          </motion.div>
        </div>
      )}

      {/* ── Contenu principal ── */}
      {!loading && clusters.length > 0 && !isCalculating && (
        <div className="flex-1 flex overflow-hidden min-h-0">

          {/* ── Graphe / Liste accessible (gauche) ── */}
          <div
            className={`flex-[1.4] p-6 relative min-h-0 flex flex-col ${
              viewMode === 'graph' ? 'block lg:flex' : 'hidden lg:flex'
            }`}
          >
            {/* Toggle visual/list accessible */}
            <div className="flex items-center gap-1 mb-3 shrink-0">
              <button
                onClick={() => setGraphMode('visual')}
                className={`flex items-center gap-1.5 px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider rounded-lg transition-all cursor-pointer focus-visible:ring-2 focus-visible:ring-ochre/50 focus-visible:outline-none ${
                  graphMode === 'visual'
                    ? 'bg-ink text-paper dark:bg-white dark:text-black shadow-sm'
                    : 'text-concrete hover:bg-black/5 dark:hover:bg-white/5'
                }`}
              >
                <Network size={12} /> {t('insightsView.viewGraph')}
              </button>
              <button
                onClick={() => {
                  setGraphMode('list')
                  if (selectedClusterId) setExpandedListClusterId(selectedClusterId)
                }}
                className={`flex items-center gap-1.5 px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider rounded-lg transition-all cursor-pointer focus-visible:ring-2 focus-visible:ring-ochre/50 focus-visible:outline-none ${
                  graphMode === 'list'
                    ? 'bg-ink text-paper dark:bg-white dark:text-black shadow-sm'
                    : 'text-concrete hover:bg-black/5 dark:hover:bg-white/5'
                }`}
              >
                <List size={12} /> {t('insightsView.listView') || 'List'}
              </button>
            </div>

            {/* Vue visuelle (D3) */}
            {graphMode === 'visual' && (
              <div
                className="flex-1 min-h-0"
                role="img"
                aria-label={t('insightsView.graphAriaLabel', {
                  clusters: clusters.length,
                  notes: notes.length,
                  bridges: bridgeNotes.length,
                }) || `Semantic network: ${clusters.length} clusters, ${notes.length} notes, ${bridgeNotes.length} bridges`}
              >
                <NetworkGraph
                  notes={notes}
                  clusters={clusters}
                  bridgeNotes={bridgeNotes}
                  onNoteSelect={handleNoteClick}
                  selectedClusterId={selectedClusterId}
                  onClusterSelect={setSelectedClusterId}
                  untitledLabel={t('insightsView.unknownNote')}
                  resetFocusLabel={t('insightsView.resetFocus')}
                  fitViewLabel={t('insightsView.fitGraphView')}
                  legendFilterPlaceholder={t('insightsView.legendFilterPlaceholder')}
                  legendShowMoreLabel={t('insightsView.legendShowMore')}
                  legendShowLessLabel={t('insightsView.legendShowLess')}
                />
              </div>
            )}

            {/* Vue liste accessible (accordion + filtre + tri) */}
            {graphMode === 'list' && (
              <div
                className="flex-1 min-h-0 flex flex-col gap-3"
                role="region"
                aria-label={t('insightsView.listAriaLabel') || 'Accessible cluster list'}
              >
                <div className="shrink-0 flex flex-col sm:flex-row gap-2">
                  <div className="relative flex-1 min-w-0">
                    <Search
                      size={14}
                      className="absolute start-3 top-1/2 -translate-y-1/2 text-concrete pointer-events-none"
                      aria-hidden
                    />
                    <input
                      type="search"
                      value={listFilter}
                      onChange={e => setListFilter(e.target.value)}
                      placeholder={t('insightsView.listFilterPlaceholder')}
                      aria-label={t('insightsView.listFilterPlaceholder')}
                      className="w-full bg-white dark:bg-zinc-800/60 border border-border/40 rounded-xl ps-9 pe-3 py-2 text-xs outline-none focus:ring-2 focus:ring-ochre/30 focus:border-ochre/40 transition-all placeholder:text-concrete/70"
                    />
                  </div>
                  <label className="shrink-0 flex items-center gap-1.5 px-3 py-2 rounded-xl bg-white dark:bg-zinc-800/60 border border-border/40 text-[10px] font-bold uppercase tracking-wider text-concrete">
                    <ArrowUpDown size={12} aria-hidden />
                    <span className="sr-only">{t('insightsView.listSortLabel')}</span>
                    <select
                      value={listSort}
                      onChange={e => setListSort(e.target.value as 'size' | 'alpha' | 'bridges')}
                      className="bg-transparent outline-none cursor-pointer text-ink dark:text-dark-ink font-bold uppercase tracking-wider"
                      aria-label={t('insightsView.listSortLabel')}
                    >
                      <option value="size">{t('insightsView.listSortSize')}</option>
                      <option value="bridges">{t('insightsView.listSortBridges')}</option>
                      <option value="alpha">{t('insightsView.listSortAlpha')}</option>
                    </select>
                  </label>
                </div>

                <div className="flex-1 min-h-0 overflow-y-auto custom-scrollbar space-y-2">
                  {listClusters.length === 0 ? (
                    <p className="text-xs text-concrete text-center py-10 px-4">
                      {t('insightsView.listFilterEmpty')}
                    </p>
                  ) : (
                    listClusters.map(({ cluster, name, clusterNotes, matchingNotes, clusterBridges, nameMatches }) => {
                      const isExpanded = expandedListClusterId === cluster.id
                      const displayNotes = !listFilter.trim() || nameMatches
                        ? clusterNotes
                        : matchingNotes

                      return (
                        <div
                          key={cluster.id}
                          className="rounded-xl bg-white dark:bg-zinc-800 border border-border/30 overflow-hidden"
                        >
                          <button
                            type="button"
                            onClick={() => {
                              setExpandedListClusterId(prev =>
                                prev === cluster.id ? null : cluster.id
                              )
                              setSelectedClusterId(cluster.id)
                            }}
                            aria-expanded={isExpanded}
                            className="w-full flex items-center gap-2 p-3.5 text-start hover:bg-black/[0.03] dark:hover:bg-white/[0.04] transition-colors cursor-pointer focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ochre/50 focus-visible:outline-none"
                          >
                            {isExpanded ? (
                              <ChevronDown size={14} className="text-concrete shrink-0" aria-hidden />
                            ) : (
                              <ChevronRight size={14} className="text-concrete shrink-0" aria-hidden />
                            )}
                            <div
                              className="w-2.5 h-2.5 rounded-full shrink-0"
                              style={{ backgroundColor: cluster.color }}
                              aria-hidden
                            />
                            <h3 className="text-xs font-bold uppercase tracking-wider text-ink dark:text-dark-ink truncate flex-1 min-w-0">
                              {name}
                            </h3>
                            <span className="text-[9px] text-concrete shrink-0 tabular-nums">
                              {clusterNotes.length} {t('insightsView.graphNotesLabel')}
                              {clusterBridges.length > 0 &&
                                ` · ${clusterBridges.length} ${t('insightsView.bridgeCount')}`}
                              {listFilter.trim() && matchingNotes.length > 0 && matchingNotes.length < clusterNotes.length && (
                                <span className="text-ochre">
                                  {` · ${matchingNotes.length} ${t('insightsView.listFilterMatch')}`}
                                </span>
                              )}
                            </span>
                          </button>

                          <AnimatePresence initial={false}>
                            {isExpanded && (
                              <motion.div
                                key="notes"
                                initial={prefersReducedMotion ? false : { height: 0, opacity: 0 }}
                                animate={{ height: 'auto', opacity: 1 }}
                                exit={prefersReducedMotion ? undefined : { height: 0, opacity: 0 }}
                                transition={{ duration: 0.2 }}
                                className="overflow-hidden"
                              >
                                <ul className="px-3 pb-3 space-y-0.5 border-t border-border/20 pt-2">
                                  {displayNotes.length === 0 ? (
                                    <li className="px-2.5 py-2 text-[11px] text-concrete">
                                      {t('insightsView.clusters.emptyCluster')}
                                    </li>
                                  ) : (
                                    displayNotes.map(note => (
                                      <li key={note.id}>
                                        <button
                                          type="button"
                                          onClick={() => handleNoteClick(note.id)}
                                          className="w-full text-start px-2.5 py-1.5 rounded-lg hover:bg-black/5 dark:hover:bg-white/5 text-xs text-ink dark:text-dark-ink flex items-center gap-2 cursor-pointer transition-colors focus-visible:ring-2 focus-visible:ring-ochre/50 focus-visible:outline-none"
                                        >
                                          <ChevronRight size={11} className="text-concrete shrink-0" aria-hidden />
                                          <span className="truncate">
                                            {note.title || t('insightsView.unknownNote')}
                                          </span>
                                        </button>
                                      </li>
                                    ))
                                  )}
                                </ul>
                              </motion.div>
                            )}
                          </AnimatePresence>
                        </div>
                      )
                    })
                  )}
                </div>
              </div>
            )}
          </div>

          {/* ── Dashboard (droite) ── */}
          <div
            className={`flex-1 border-l border-border/20 flex flex-col min-h-0 overflow-hidden bg-[#fcfbfa] dark:bg-zinc-900/10 backdrop-blur-sm ${
              viewMode === 'dashboard' ? 'flex' : 'hidden lg:flex'
            }`}
          >
            <div className="p-6 sm:p-8 flex-1 overflow-y-auto custom-scrollbar space-y-8">

              {/* Filtre panneau droit (sticky) */}
              <div className="sticky top-0 z-20 -mx-1 px-1 pb-2 bg-[#fcfbfa]/95 dark:bg-[#0D0D0D]/90 backdrop-blur-md">
                <div className="relative">
                  <Search
                    size={14}
                    className="absolute start-3 top-1/2 -translate-y-1/2 text-concrete pointer-events-none"
                    aria-hidden
                  />
                  <input
                    type="search"
                    value={dashboardFilter}
                    onChange={e => {
                      setDashboardFilter(e.target.value)
                      if (e.target.value.trim()) {
                        setBridgesShowAll(true)
                        setIsolatedShowAll(true)
                        setDashboardSectionOpen(s => ({ ...s, bridges: true, isolated: true }))
                      }
                    }}
                    placeholder={t('insightsView.dashboardFilterPlaceholder')}
                    aria-label={t('insightsView.dashboardFilterPlaceholder')}
                    className="w-full bg-white dark:bg-zinc-800/60 border border-border/40 rounded-xl ps-9 pe-3 py-2.5 text-xs outline-none focus:ring-2 focus:ring-ochre/30 focus:border-ochre/40 transition-all placeholder:text-concrete/70 shadow-sm"
                  />
                </div>
              </div>

              {/* Avertissement d'obsolescence (stale banner) */}
              {isStale && !isCalculating && (
                <motion.div
                  initial={{ opacity: 0, y: -10 }}
                  animate={{ opacity: 1, y: 0 }}
                  className="p-4 rounded-2xl bg-amber-500/10 border border-amber-500/20 text-amber-800 dark:text-amber-300 text-xs flex items-center justify-between gap-4 shadow-sm"
                >
                  <div className="flex items-center gap-2.5">
                    <AlertCircle size={16} className="shrink-0 text-amber-500" />
                    <span>
                      {t('insightsView.staleResults')}
                    </span>
                  </div>
                  <button
                    onClick={performAnalysis}
                    className="px-3.5 py-2 bg-amber-500 text-white dark:text-zinc-950 font-bold uppercase tracking-wider text-[10px] rounded-lg hover:scale-105 active:scale-95 transition-all shrink-0 shadow-sm"
                  >
                    {t('insightsView.resync')}
                  </button>
                </motion.div>
              )}

              {/* ① Panneau d'inspection cluster */}
              <AnimatePresence>
                {selectedCluster && (
                  <motion.div
                    initial={{ opacity: 0, y: -16 }}
                    animate={{ opacity: 1, y: 0 }}
                    exit={{ opacity: 0, y: -16 }}
                    className="p-6 rounded-2xl bg-white dark:bg-zinc-800 border-2 border-ochre/30 shadow-md relative overflow-hidden"
                  >
                    <div
                      className="absolute top-0 left-0 w-1.5 h-full"
                      style={{ backgroundColor: selectedCluster.color }}
                    />
                    <div className="flex items-center justify-between gap-4 mb-4 pl-3">
                      <div className="space-y-0.5">
                        <span className="text-[9px] font-bold uppercase tracking-widest text-ochre">
                          {t('insightsView.focusCluster.title')}
                        </span>
                        <h3 className="text-base font-serif font-semibold text-ink dark:text-dark-ink">
                          {selectedCluster.name ||
                            t('insightsView.clusterFallback', { index: selectedCluster.clusterId })}
                        </h3>
                      </div>
                      <button
                        onClick={() => setSelectedClusterId(null)}
                        className="p-1 px-3 bg-black/5 dark:bg-white/5 hover:bg-black/10 dark:hover:bg-white/10 text-[10px] font-bold rounded-lg uppercase tracking-wider transition-colors shrink-0"
                      >
                        {t('insightsView.focusCluster.close')}
                      </button>
                    </div>
                    <div className="pl-3 space-y-3">
                      <p className="text-xs text-concrete">
                        {t('insightsView.focusCluster.description', {
                          count: selectedClusterNotes.length,
                        })}
                      </p>
                      <div className="space-y-1.5 max-h-[180px] overflow-y-auto custom-scrollbar pr-1">
                        {selectedClusterNotes.map(note => (
                          <button
                            key={note.id}
                            onClick={() => handleNoteClick(note.id)}
                            className="w-full text-left p-2.5 rounded-lg bg-black/[0.03] hover:bg-black/[0.07] dark:bg-white/[0.03] dark:hover:bg-white/[0.07] text-xs font-medium text-ink dark:text-dark-ink flex items-center justify-between gap-3 group transition-all"
                          >
                            <span className="truncate group-hover:translate-x-0.5 transition-transform">
                              {note.title || t('insightsView.unknownNote')}
                            </span>
                            <ChevronRight size={12} className="text-concrete shrink-0" />
                          </button>
                        ))}
                      </div>
                    </div>
                  </motion.div>
                )}
              </AnimatePresence>

              {/* ② Stats */}
              <div className="grid grid-cols-2 gap-4">
                <div className="p-5 rounded-2xl bg-white dark:bg-zinc-800/40 border border-border/40 shadow-sm flex flex-col justify-between">
                  <div className="flex items-center gap-2 text-indigo-500 mb-2">
                    <Layers size={14} />
                    <span className="text-[10px] font-bold uppercase tracking-widest">
                      {t('insightsView.stats.clusters')}
                    </span>
                  </div>
                  <div>
                    <div className="text-2xl font-serif font-semibold text-ink dark:text-dark-ink">
                      {clusters.length}
                    </div>
                    <p className="text-[9px] text-concrete font-medium uppercase mt-1">
                      {t('insightsView.stats.themesSubtitle')}
                    </p>
                  </div>
                </div>
                <div className="p-5 rounded-2xl bg-white dark:bg-zinc-800/40 border border-border/40 shadow-sm flex flex-col justify-between">
                  <div className="flex items-center gap-2 text-ochre mb-2">
                    <Trophy size={14} />
                    <span className="text-[10px] font-bold uppercase tracking-widest">
                      {t('insightsView.stats.bridgeNotes')}
                    </span>
                  </div>
                  <div>
                    <div className="text-2xl font-serif font-semibold text-ink dark:text-dark-ink">
                      {bridgeNotes.length}
                    </div>
                    <p className="text-[9px] text-concrete font-medium uppercase mt-1">
                      {t('insightsView.stats.bridgesSubtitle')}
                    </p>
                  </div>
                </div>
              </div>

              {/* ③ Système de Recalcul (replié par défaut) */}
              <section className="rounded-2xl bg-white dark:bg-zinc-800 border border-border/40 shadow-sm overflow-hidden">
                <button
                  type="button"
                  onClick={() => setDashboardSectionOpen(s => ({ ...s, recalc: !s.recalc }))}
                  aria-expanded={dashboardSectionOpen.recalc}
                  className="w-full flex items-center justify-between gap-4 p-5 text-start hover:bg-black/[0.02] dark:hover:bg-white/[0.03] transition-colors cursor-pointer"
                >
                  <div className="flex items-center gap-2">
                    <Sliders size={15} className="text-ochre" />
                    <h4 className="text-[11px] font-black uppercase tracking-[0.2em] text-ink dark:text-dark-ink">
                      {t('insightsView.recalcSystem.title')}
                    </h4>
                  </div>
                  <div className="flex items-center gap-2 shrink-0">
                    <span className="flex items-center gap-1 text-[9.5px] font-bold text-emerald-500 uppercase">
                      <CheckCircle2 size={11} /> {t('insightsView.recalcSystem.statusSynced')}
                    </span>
                    {dashboardSectionOpen.recalc ? (
                      <ChevronDown size={14} className="text-concrete" aria-hidden />
                    ) : (
                      <ChevronRight size={14} className="text-concrete" aria-hidden />
                    )}
                  </div>
                </button>
                <AnimatePresence initial={false}>
                  {dashboardSectionOpen.recalc && (
                    <motion.div
                      initial={prefersReducedMotion ? false : { height: 0, opacity: 0 }}
                      animate={{ height: 'auto', opacity: 1 }}
                      exit={prefersReducedMotion ? undefined : { height: 0, opacity: 0 }}
                      transition={{ duration: 0.2 }}
                      className="overflow-hidden"
                    >
                      <div className="px-5 pb-5 space-y-4 border-t border-border/10 pt-4">
                        <div className="grid grid-cols-2 gap-4">
                          <div className="space-y-1">
                            <span className="text-[9px] text-concrete block">{t('insightsView.recalcSystem.scheduledCron')}</span>
                            <p className="text-xs text-ink dark:text-dark-ink font-semibold flex items-center gap-1.5">
                              <Clock size={12} className="opacity-50" /> 04:00
                            </p>
                          </div>
                          <div className="space-y-1">
                            <span className="text-[9px] text-concrete block">{t('insightsView.recalcSystem.lastSync')}</span>
                            <p className="text-xs text-ink dark:text-dark-ink font-bold font-mono">
                              {lastSyncTime || '—'}
                            </p>
                          </div>
                        </div>
                        <div className="pt-2 border-t border-border/10 space-y-3">
                          <div className="flex justify-between items-center text-[10px]">
                            <span className="text-concrete">
                              {embeddingStats
                                ? t('insightsView.embeddingsHint', {
                                    indexed: embeddingStats.indexed,
                                    total: embeddingStats.total,
                                  })
                                : '—'}
                            </span>
                            <span className="font-bold font-mono text-ink dark:text-dark-ink shrink-0 ms-3">
                              {embeddingStats
                                ? `${embeddingStats.indexed} / ${embeddingStats.total}`
                                : '—'}
                            </span>
                          </div>
                          <button
                            type="button"
                            onClick={() => void handleReindexEmbeddings()}
                            disabled={isReindexing || isCalculating}
                            className="w-full flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl border border-ochre/30 bg-ochre/5 hover:bg-ochre/10 text-[10px] font-bold uppercase tracking-widest text-ochre disabled:opacity-50 transition-colors"
                          >
                            {isReindexing ? (
                              <RefreshCw size={13} className="animate-spin" />
                            ) : (
                              <Database size={13} />
                            )}
                            {isReindexing ? t('insightsView.mapping') : t('insightsView.resync')}
                          </button>
                        </div>
                      </div>
                    </motion.div>
                  )}
                </AnimatePresence>
              </section>

              {/* ④ Clusters Isolés */}
              <section className="space-y-3">
                <button
                  type="button"
                  onClick={() => setDashboardSectionOpen(s => ({ ...s, isolated: !s.isolated }))}
                  aria-expanded={dashboardSectionOpen.isolated}
                  className="w-full flex items-center justify-between gap-4 px-1 text-start cursor-pointer group"
                >
                  <div className="flex items-center gap-2 min-w-0">
                    <AlertCircle size={15} className="text-rose-400 shrink-0" />
                    <h3 className="text-xs font-bold uppercase tracking-[0.2em] text-ink dark:text-dark-ink truncate">
                      {t('insightsView.isolatedClusters.title', { count: filteredIsolatedClusters.length })}
                    </h3>
                  </div>
                  {dashboardSectionOpen.isolated ? (
                    <ChevronDown size={14} className="text-concrete shrink-0" aria-hidden />
                  ) : (
                    <ChevronRight size={14} className="text-concrete shrink-0" aria-hidden />
                  )}
                </button>
                {dashboardSectionOpen.isolated && (
                  <div className="space-y-2">
                    <p className="text-[10px] text-concrete italic px-1 leading-relaxed">
                      {t('insightsView.tipIsolatedAction')}
                    </p>
                    {visibleIsolatedClusters.map(c => (
                      <motion.div
                        key={c.id}
                        whileHover={prefersReducedMotion ? undefined : { y: -1 }}
                        onClick={() => setSelectedClusterId(c.id)}
                        className="p-3.5 rounded-xl bg-white dark:bg-zinc-800 border border-border/30 hover:border-black/10 dark:hover:border-white/10 flex items-center justify-between cursor-pointer transition-all focus-visible:ring-2 focus-visible:ring-ochre/50 focus-visible:outline-none"
                      >
                        <div className="flex items-center gap-2.5 min-w-0">
                          <div className="w-2 h-2 rounded-full shrink-0" style={{ backgroundColor: c.color }} />
                          <span className="text-xs font-medium text-ink dark:text-dark-ink truncate">
                            {c.name || t('insightsView.clusterFallback', { index: c.clusterId })}
                          </span>
                        </div>
                        <span className="text-[10px] text-rose-500 font-semibold uppercase tracking-wider bg-rose-500/5 px-2.5 py-0.5 rounded-full border border-rose-500/10 shrink-0">
                          {t('insightsView.isolatedClusters.badge')}
                        </span>
                      </motion.div>
                    ))}
                    {!dashboardFilter.trim() && filteredIsolatedClusters.length > DASHBOARD_PREVIEW && (
                      <button
                        type="button"
                        onClick={() => setIsolatedShowAll(v => !v)}
                        className="w-full py-2 text-[10px] font-bold uppercase tracking-widest text-concrete hover:text-ink border border-dashed border-border/50 rounded-xl cursor-pointer"
                      >
                        {isolatedShowAll
                          ? t('insightsView.legendShowLess')
                          : t('insightsView.legendShowMore', {
                              count: filteredIsolatedClusters.length - DASHBOARD_PREVIEW,
                            })}
                      </button>
                    )}
                    {filteredIsolatedClusters.length === 0 && (
                      <div className="p-4 bg-white dark:bg-zinc-800 rounded-xl text-xs text-concrete text-center italic border border-border/20">
                        {dashboardFilter.trim()
                          ? t('insightsView.listFilterEmpty')
                          : t('insightsView.isolatedClusters.empty')}
                      </div>
                    )}
                  </div>
                )}
              </section>

              {/* ⑤ Notes-Ponts Influentes */}
              <section className="space-y-3">
                <button
                  type="button"
                  onClick={() => setDashboardSectionOpen(s => ({ ...s, bridges: !s.bridges }))}
                  aria-expanded={dashboardSectionOpen.bridges}
                  className="w-full flex items-center justify-between gap-4 px-1 text-start cursor-pointer"
                >
                  <div className="flex items-center gap-2 min-w-0">
                    <Zap size={16} className="text-ochre shrink-0" />
                    <h3 className="text-xs font-bold uppercase tracking-[0.2em] text-ink dark:text-dark-ink truncate">
                      {t('insightsView.bridgeNotes.title')}
                      <span className="ms-2 text-concrete font-mono normal-case tracking-normal">
                        ({filteredBridgeList.length})
                      </span>
                    </h3>
                  </div>
                  {dashboardSectionOpen.bridges ? (
                    <ChevronDown size={14} className="text-concrete shrink-0" aria-hidden />
                  ) : (
                    <ChevronRight size={14} className="text-concrete shrink-0" aria-hidden />
                  )}
                </button>
                {dashboardSectionOpen.bridges && (
                  <div className="space-y-3">
                    <p className="text-[10px] text-concrete italic px-1 leading-relaxed">
                      {t('insightsView.tipBridgeNotes')}
                    </p>
                    {visibleBridgeList.map(bridge => {
                      const { primary, extraCount } = getBridgeThemeSlice(bridge.clustersConnected)
                      const themeA = clusters.find(c => c.id === String(primary[0]))
                      const themeB = clusters.find(c => c.id === String(primary[1]))
                      const nameA =
                        themeA?.name ||
                        t('insightsView.clusterFallback', { index: primary[0] ?? 0 })
                      const nameB =
                        themeB?.name ||
                        (primary[1] !== undefined
                          ? t('insightsView.clusterFallback', { index: primary[1] })
                          : null)

                      return (
                      <motion.div
                        key={bridge.noteId}
                        whileHover={prefersReducedMotion ? undefined : { x: 4 }}
                        onClick={() => handleNoteClick(bridge.noteId)}
                        className="p-4 rounded-xl bg-white dark:bg-zinc-800 border border-border/30 hover:border-ochre/40 hover:shadow-sm transition-all cursor-pointer group focus-visible:ring-2 focus-visible:ring-ochre/50 focus-visible:outline-none"
                        tabIndex={0}
                        role="button"
                        onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleNoteClick(bridge.noteId) } }}
                      >
                        <div className="flex items-start justify-between gap-3 mb-3">
                          <h4 className="text-xs font-semibold text-ink dark:text-dark-ink line-clamp-2 flex-1 group-hover:text-ochre transition-colors">
                            {bridge.title}
                          </h4>
                          <span
                            className="text-[9.5px] font-bold text-ochre bg-ochre/5 border border-ochre/10 px-2.5 py-0.5 rounded-full shrink-0"
                            title={t('insightsView.bridgeNotes.scoreHint')}
                          >
                            {t('insightsView.bridgeNotes.affinity', {
                              score: (bridge.bridgeScore * 100).toFixed(0),
                            })}
                          </span>
                        </div>

                        {nameB ? (
                          <div className="flex items-center gap-2 flex-wrap">
                            <button
                              type="button"
                              onClick={e => {
                                e.stopPropagation()
                                setSelectedClusterId(String(primary[0]))
                              }}
                              className="inline-flex items-center gap-1.5 max-w-[42%] px-2 py-1 rounded-lg bg-black/[0.03] dark:bg-white/[0.04] border border-border/30 hover:border-concrete/40 cursor-pointer"
                            >
                              <span
                                className="w-2 h-2 rounded-full shrink-0"
                                style={{ backgroundColor: themeA?.color || '#cbd5e1' }}
                              />
                              <span className="text-[10px] font-medium text-ink dark:text-dark-ink truncate">
                                {nameA}
                              </span>
                            </button>
                            <span className="text-[10px] font-bold text-ochre shrink-0" aria-hidden>
                              ↔
                            </span>
                            <button
                              type="button"
                              onClick={e => {
                                e.stopPropagation()
                                setSelectedClusterId(String(primary[1]))
                              }}
                              className="inline-flex items-center gap-1.5 max-w-[42%] px-2 py-1 rounded-lg bg-black/[0.03] dark:bg-white/[0.04] border border-border/30 hover:border-concrete/40 cursor-pointer"
                            >
                              <span
                                className="w-2 h-2 rounded-full shrink-0"
                                style={{ backgroundColor: themeB?.color || '#cbd5e1' }}
                              />
                              <span className="text-[10px] font-medium text-ink dark:text-dark-ink truncate">
                                {nameB}
                              </span>
                            </button>
                            {extraCount > 0 && (
                              <span className="text-[9px] text-concrete font-bold uppercase tracking-wider">
                                {t('insightsView.bridgeNotes.moreThemes', { count: extraCount })}
                              </span>
                            )}
                          </div>
                        ) : (
                          <p className="text-[10px] text-concrete italic">
                            {t('insightsView.bridgeNotes.needsResync')}
                          </p>
                        )}
                      </motion.div>
                      )
                    })}
                    {!dashboardFilter.trim() && filteredBridgeList.length > DASHBOARD_PREVIEW && (
                      <button
                        type="button"
                        onClick={() => setBridgesShowAll(v => !v)}
                        className="w-full py-2.5 text-[10px] font-bold uppercase tracking-widest text-concrete hover:text-ink border border-dashed border-border/50 rounded-xl cursor-pointer"
                      >
                        {bridgesShowAll
                          ? t('insightsView.legendShowLess')
                          : t('insightsView.legendShowMore', {
                              count: filteredBridgeList.length - DASHBOARD_PREVIEW,
                            })}
                      </button>
                    )}
                    {filteredBridgeList.length === 0 && !isCalculating && (
                      <div className="text-xs text-concrete italic text-center p-6 bg-white dark:bg-zinc-800 rounded-xl border border-border/20">
                        {dashboardFilter.trim()
                          ? t('insightsView.listFilterEmpty')
                          : t('insightsView.bridgeNotes.empty')}
                      </div>
                    )}
                  </div>
                )}
              </section>

              {/* ⑥ Opportunités de Connexion (link prediction — top near-miss pairs) */}
              <section className="space-y-3">
                <button
                  type="button"
                  onClick={() => setDashboardSectionOpen(s => ({ ...s, suggestions: !s.suggestions }))}
                  aria-expanded={dashboardSectionOpen.suggestions}
                  className="w-full flex items-center justify-between gap-4 px-1 text-start cursor-pointer"
                >
                  <div className="flex items-center gap-2 min-w-0">
                    <Lightbulb size={16} className="text-indigo-500 shrink-0" />
                    <h3 className="text-xs font-bold uppercase tracking-[0.2em] text-ink dark:text-dark-ink truncate">
                      {t('insightsView.suggestions.title')}
                      {suggestions.length > 0 && (
                        <span className="ms-2 text-concrete font-mono normal-case tracking-normal">
                          ({Math.min(suggestions.length, 12)})
                        </span>
                      )}
                    </h3>
                  </div>
                  {dashboardSectionOpen.suggestions ? (
                    <ChevronDown size={14} className="text-concrete shrink-0" aria-hidden />
                  ) : (
                    <ChevronRight size={14} className="text-concrete shrink-0" aria-hidden />
                  )}
                </button>
                {dashboardSectionOpen.suggestions && (
                  <div className="space-y-3">
                    <p className="text-[10px] text-concrete italic px-1 leading-relaxed">
                      {t('insightsView.tipSuggestions')}
                    </p>
                    {visibleSuggestions.map(s => {
                      const key = `${s.clusterAId}-${s.clusterBId}`
                      const busy = actingSuggestionKey === key
                      return (
                        <div
                          key={key}
                          className="p-4 rounded-xl bg-white dark:bg-zinc-800 border border-indigo-500/15 hover:border-indigo-500/30 transition-all shadow-sm"
                        >
                          <div className="flex items-start justify-between gap-2 mb-2">
                            <div className="flex items-center gap-2 min-w-0 flex-1">
                              <span
                                className="w-2 h-2 rounded-full shrink-0 bg-indigo-500"
                                aria-hidden
                              />
                              <span className="text-[10px] font-medium text-ink dark:text-dark-ink truncate">
                                {s.clusterAName}
                              </span>
                              <span className="text-[10px] font-bold text-ochre shrink-0" aria-hidden>
                                ↔
                              </span>
                              <span
                                className="w-2 h-2 rounded-full shrink-0 bg-ochre"
                                aria-hidden
                              />
                              <span className="text-[10px] font-medium text-ink dark:text-dark-ink truncate">
                                {s.clusterBName}
                              </span>
                            </div>
                            <button
                              type="button"
                              disabled={busy}
                              onClick={() => void handleDismissSuggestion(s)}
                              className="p-1 rounded-md text-concrete hover:text-ink hover:bg-black/5 dark:hover:bg-white/5 disabled:opacity-50 cursor-pointer"
                              aria-label={t('insightsView.suggestions.dismiss')}
                              title={t('insightsView.suggestions.dismiss')}
                            >
                              <X size={14} />
                            </button>
                          </div>
                          <h4 className="text-sm font-semibold text-ink dark:text-dark-ink mb-1.5 line-clamp-2">
                            {s.suggestedTitle}
                          </h4>
                          <p className="text-[11px] text-muted-ink leading-relaxed line-clamp-2 mb-3">
                            {s.suggestedContent}
                          </p>
                          <button
                            type="button"
                            disabled={busy}
                            onClick={() => void handleCreateSuggestion(s)}
                            className="w-full flex items-center justify-center gap-2 px-3 py-2 rounded-lg bg-ink text-paper dark:bg-white dark:text-black text-[10px] font-bold uppercase tracking-widest hover:opacity-90 disabled:opacity-50 transition-opacity cursor-pointer"
                          >
                            {busy ? (
                              <RefreshCw size={12} className="animate-spin" />
                            ) : (
                              <Lightbulb size={12} />
                            )}
                            {t('insightsView.suggestions.createNote')}
                          </button>
                        </div>
                      )
                    })}
                    {!suggestionsShowAll && suggestions.length > SUGGESTIONS_PREVIEW && (
                      <button
                        type="button"
                        onClick={() => setSuggestionsShowAll(true)}
                        className="w-full py-2.5 text-[10px] font-bold uppercase tracking-widest text-concrete hover:text-ink border border-dashed border-border/50 rounded-xl cursor-pointer"
                      >
                        {t('insightsView.legendShowMore', {
                          count: Math.min(suggestions.length, 12) - SUGGESTIONS_PREVIEW,
                        })}
                      </button>
                    )}
                    {suggestionsShowAll && suggestions.length > SUGGESTIONS_PREVIEW && (
                      <button
                        type="button"
                        onClick={() => setSuggestionsShowAll(false)}
                        className="w-full py-2.5 text-[10px] font-bold uppercase tracking-widest text-concrete hover:text-ink border border-dashed border-border/50 rounded-xl cursor-pointer"
                      >
                        {t('insightsView.legendShowLess')}
                      </button>
                    )}
                    {isCalculating && (
                      <div className="animate-pulse space-y-3">
                        {[1, 2].map(i => (
                          <div
                            key={i}
                            className="h-24 bg-indigo-500/5 rounded-xl border border-indigo-500/10"
                          />
                        ))}
                      </div>
                    )}
                    {!isCalculating && suggestions.length === 0 && (
                      <div className="text-xs text-concrete text-center italic p-6 border border-border/20 bg-white/40 dark:bg-zinc-800 rounded-xl">
                        {t('insightsView.suggestions.emptyDescription')}
                      </div>
                    )}
                  </div>
                )}
              </section>

            </div>
          </div>
        </div>
      )}

      {/* ── Peek panel (factorisé) ── */}
      <NotePeekPanel
        note={peek.note}
        blockId={peek.blockId}
        loading={peek.loading}
        mode="overlay"
        onClose={peek.close}
        onOpenFully={(n) => { router.push(`/home?openNote=${n.id}`); peek.close() }}
      />

    </div>
  )
}
