<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import { RouterLink, useRouter } from 'vue-router'
import { api, ApiError } from '@/api/client'
import { useAuth } from '@/composables/useAuth'
import type { Discussion, Member, MemberRole, ProjectDetail, ProjectMemberEntry, Resource, RoadmapStep, Wrapped } from '@/types/api'

import StatusBadge from '@/components/StatusBadge.vue'
import CategoryBadge from '@/components/CategoryBadge.vue'
import ProgressBar from '@/components/ProgressBar.vue'
import Avatar from '@/components/Avatar.vue'
import LoadingState from '@/components/LoadingState.vue'
import ErrorState from '@/components/ErrorState.vue'
import JoinProjectModal from '@/components/JoinProjectModal.vue'
import ImageGallery from '@/components/ImageGallery.vue'
import { formatDate, formatMonthYear, formatRelative } from '@/utils/formatDate'

const props = defineProps<{ slug: string }>()
const router = useRouter()
const { isAuthenticated } = useAuth()

// État principal
const project = ref<ProjectDetail | null>(null)
const loading = ref(true)
const error = ref<string | null>(null)

// Tabs
type Tab = 'roadmap' | 'discussions' | 'resources' | 'images'
const activeTab = ref<Tab>('roadmap')

// Discussions et resources : lazy-loaded au 1er affichage du tab.
const discussions = ref<Discussion[] | null>(null)
const discussionsLoading = ref(false)
const resources = ref<Resource[] | null>(null)
const resourcesLoading = ref(false)

// Inputs admin (ajouter étape / ressource / message)
const newStepTitle = ref('')
const newResource = ref({ title: '', url: '', kind: 'link' as Resource['kind'] })
const newMessage = ref({ author_name: '', body: '' })
const adminBusy = ref(false)

// Modal "Rejoindre"
const showJoinModal = ref(false)

async function loadProject() {
  loading.value = true
  error.value = null
  try {
    project.value = await api.get<ProjectDetail>(`/api/projects/${encodeURIComponent(props.slug)}`)
  } catch (e) {
    error.value = e instanceof ApiError ? (e.payload.message ?? e.code) : 'Erreur réseau'
  } finally {
    loading.value = false
  }
}

async function loadDiscussions() {
  if (discussions.value !== null) return // déjà chargé
  discussionsLoading.value = true
  try {
    const r = await api.get<Wrapped<Discussion>>(`/api/projects/${encodeURIComponent(props.slug)}/discussions`)
    discussions.value = r.data
  } catch {
    discussions.value = []
  } finally {
    discussionsLoading.value = false
  }
}

async function loadResources() {
  if (resources.value !== null) return
  resourcesLoading.value = true
  try {
    const r = await api.get<Wrapped<Resource>>(`/api/projects/${encodeURIComponent(props.slug)}/resources`)
    resources.value = r.data
  } catch {
    resources.value = []
  } finally {
    resourcesLoading.value = false
  }
}

// Charge les onglets à la demande
watch(activeTab, (tab) => {
  if (tab === 'discussions') loadDiscussions()
  if (tab === 'resources') loadResources()
})

onMounted(loadProject)

// --- Actions admin ---

async function toggleStepDone(step: RoadmapStep) {
  if (!isAuthenticated.value || !project.value) return
  try {
    const updated = await api.patch<RoadmapStep>(
      `/api/projects/${encodeURIComponent(props.slug)}/roadmap/${step.id}`,
      { is_done: !step.is_done },
    )
    const idx = project.value.roadmap.findIndex(s => s.id === step.id)
    if (idx >= 0) project.value.roadmap[idx] = updated
  } catch (e) {
    console.error('toggleStepDone failed', e)
  }
}

async function addStep() {
  const title = newStepTitle.value.trim()
  if (!title || !isAuthenticated.value || !project.value) return
  adminBusy.value = true
  try {
    const step = await api.post<RoadmapStep>(
      `/api/projects/${encodeURIComponent(props.slug)}/roadmap`,
      { title, is_done: false },
    )
    project.value.roadmap.push(step)
    newStepTitle.value = ''
  } finally {
    adminBusy.value = false
  }
}

async function deleteStep(step: RoadmapStep) {
  if (!isAuthenticated.value || !project.value) return
  if (!confirm(`Supprimer l'étape « ${step.title} » ?`)) return
  try {
    await api.delete(`/api/projects/${encodeURIComponent(props.slug)}/roadmap/${step.id}`)
    project.value.roadmap = project.value.roadmap.filter(s => s.id !== step.id)
  } catch (e) {
    console.error('deleteStep failed', e)
  }
}

async function postMessage() {
  const author = newMessage.value.author_name.trim()
  const body = newMessage.value.body.trim()
  if (!author || !body || !isAuthenticated.value) return
  adminBusy.value = true
  try {
    const msg = await api.post<Discussion>(
      `/api/projects/${encodeURIComponent(props.slug)}/discussions`,
      { author_name: author, body },
    )
    if (discussions.value) {
      // Ajoute en tête (la liste est triée DESC date par l'API).
      discussions.value.unshift({ ...msg, replies: [] })
    }
    newMessage.value = { author_name: '', body: '' }
    if (project.value) project.value.counts.discussions += 1
  } finally {
    adminBusy.value = false
  }
}

async function addResource() {
  const { title, url, kind } = newResource.value
  if (!title.trim() || !url.trim() || !isAuthenticated.value) return
  adminBusy.value = true
  try {
    const res = await api.post<Resource>(
      `/api/projects/${encodeURIComponent(props.slug)}/resources`,
      { title: title.trim(), url: url.trim(), kind },
    )
    if (resources.value) resources.value.push(res)
    newResource.value = { title: '', url: '', kind: 'link' }
    if (project.value) project.value.counts.resources += 1
  } finally {
    adminBusy.value = false
  }
}

async function deleteProject() {
  if (!isAuthenticated.value || !project.value) return
  if (!confirm(`Supprimer définitivement le projet « ${project.value.title} » ?\n(L'historique reste consultable via les archives.)`)) return
  try {
    await api.delete(`/api/projects/${encodeURIComponent(props.slug)}`)
    router.push({ name: 'projects' })
  } catch (e) {
    console.error('deleteProject failed', e)
  }
}

function onJoined(payload: { member: Member; role: MemberRole }) {
  // Optimiste : push dans la liste des membres locale avec les vraies infos.
  if (!project.value) return
  project.value.members.push({
    id:           payload.member.id,
    slug:         payload.member.slug,
    display_name: payload.member.display_name,
    initials:     payload.member.initials,
    kind:         payload.member.kind,
    avatar_url:   payload.member.avatar_url,
    role:         payload.role,
    joined_at:    new Date().toISOString(),
  })
  project.value.counts.members = project.value.members.length
}

async function changeMemberRole(m: ProjectMemberEntry, newRole: MemberRole) {
  if (!isAuthenticated.value || !project.value || newRole === m.role) return
  const oldRole = m.role
  m.role = newRole  // optimiste
  try {
    await api.patch(
      `/api/projects/${encodeURIComponent(props.slug)}/members/${encodeURIComponent(m.slug)}`,
      { role: newRole },
    )
  } catch (e) {
    m.role = oldRole
    if (e instanceof ApiError) alert(`Erreur : ${e.payload.message ?? e.code}`)
    else alert('Erreur réseau')
  }
}

async function removeMember(m: ProjectMemberEntry) {
  if (!isAuthenticated.value || !project.value) return
  if (!confirm(`Retirer « ${m.display_name} » de l'équipe ?\nLe membre reste dans l'annuaire.`)) return
  try {
    await api.delete(
      `/api/projects/${encodeURIComponent(props.slug)}/members/${encodeURIComponent(m.slug)}`,
    )
    project.value.members = project.value.members.filter(x => x.id !== m.id)
    project.value.counts.members = project.value.members.length
  } catch (e) {
    if (e instanceof ApiError) alert(`Erreur : ${e.payload.message ?? e.code}`)
    else alert('Erreur réseau')
  }
}

// Présentations dérivées
const categoryColor = computed(() => project.value?.category?.color_hex ?? '#111827')
const externalDomain = computed(() => {
  if (!project.value?.external_url) return ''
  try {
    return new URL(project.value.external_url).hostname.replace(/^www\./, '')
  } catch {
    return project.value.external_url
  }
})
const ROLE_LABEL = { admin: 'Admin', contributor: 'Contributor', observer: 'Observer' } as const
</script>

<template>
  <section class="px-10 py-8 max-w-7xl">
    <!-- Retour -->
    <RouterLink
      :to="{ name: 'projects' }"
      class="inline-flex items-center gap-1.5 text-sm text-donut-muted hover:text-donut-text"
    >
      <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <path d="m12 19-7-7 7-7M5 12h14"/>
      </svg>
      Retour
    </RouterLink>

    <LoadingState v-if="loading" class="mt-8" />
    <ErrorState v-else-if="error" :message="error" :retry="loadProject" class="mt-8" />

    <div v-else-if="project" class="mt-4 grid grid-cols-1 lg:grid-cols-3 gap-8">
      <!-- ============== COLONNE GAUCHE ============== -->
      <div class="lg:col-span-2 space-y-8">
        <!-- Badges + bouton supprimer -->
        <div class="flex items-start justify-between gap-4 flex-wrap">
          <div class="flex items-center gap-2 flex-wrap">
            <StatusBadge :status="project.status" />
            <CategoryBadge
              v-if="project.category"
              :name="project.category.name"
              :color-hex="project.category.color_hex"
            />
          </div>
          <button
            v-if="isAuthenticated"
            type="button"
            class="flex items-center gap-1.5 bg-red-500 text-white rounded-lg px-3 py-1.5 text-sm font-medium hover:bg-red-600"
            @click="deleteProject"
          >
            <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/>
            </svg>
            Supprimer
          </button>
        </div>

        <!-- Titre + métadonnées -->
        <div>
          <h1 class="text-4xl font-bold text-donut-text">{{ project.title }}</h1>
          <div class="mt-3 flex items-center gap-4 text-sm text-donut-muted flex-wrap">
            <span class="inline-flex items-center gap-1.5">
              <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/>
              </svg>
              {{ project.territory }}
            </span>
            <span class="inline-flex items-center gap-1.5">
              <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/>
              </svg>
              Créé en {{ formatMonthYear(project.created_at) }}
            </span>
            <a
              v-if="project.external_url"
              :href="project.external_url"
              target="_blank"
              rel="noopener"
              class="inline-flex items-center gap-1.5 text-status-active hover:underline"
            >
              <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <path d="M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1M14 11a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1"/>
              </svg>
              Voir sur {{ externalDomain }}
            </a>
          </div>
        </div>

        <!-- Contexte -->
        <div v-if="project.context_description">
          <h2 class="text-xl font-semibold text-donut-text">Contexte &amp; Description</h2>
          <p class="mt-2 text-donut-muted leading-relaxed whitespace-pre-line">
            {{ project.context_description }}
          </p>
        </div>

        <!-- Objectifs -->
        <div v-if="project.objectives">
          <h2 class="text-xl font-semibold text-donut-text">Objectifs</h2>
          <p class="mt-2 text-donut-muted leading-relaxed whitespace-pre-line">
            {{ project.objectives }}
          </p>
        </div>

        <!-- TABS -->
        <div class="border-b border-donut-border">
          <nav class="flex items-center gap-6 -mb-px" role="tablist">
            <button
              v-for="tab in (['roadmap', 'images', 'discussions', 'resources'] as const)"
              :key="tab"
              type="button"
              role="tab"
              :aria-selected="activeTab === tab"
              :class="[
                'flex items-center gap-2 pb-3 text-sm font-medium border-b-2 transition-colors',
                activeTab === tab
                  ? 'border-donut-text text-donut-text'
                  : 'border-transparent text-donut-muted hover:text-donut-text',
              ]"
              @click="activeTab = tab"
            >
              <span v-if="tab === 'roadmap'" class="inline-flex items-center gap-2">
                <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                  <path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
                </svg>
                Feuille de route
              </span>
              <span v-else-if="tab === 'images'" class="inline-flex items-center gap-2">
                <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                  <rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/>
                </svg>
                Images
              </span>
              <span v-else-if="tab === 'discussions'" class="inline-flex items-center gap-2">
                <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                  <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
                </svg>
                Discussions ({{ project.counts.discussions }})
              </span>
              <span v-else class="inline-flex items-center gap-2">
                <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                  <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16"/>
                </svg>
                Ressources ({{ project.counts.resources }})
              </span>
            </button>
          </nav>
        </div>

        <!-- Tab : Feuille de route -->
        <div v-if="activeTab === 'roadmap'" class="space-y-3">
          <p v-if="project.roadmap.length === 0" class="text-donut-muted text-sm">
            Pas encore d'étape. {{ isAuthenticated ? 'Ajoute-en une ci-dessous.' : '' }}
          </p>
          <ul v-else class="space-y-3">
            <li
              v-for="step in project.roadmap"
              :key="step.id"
              class="flex items-start gap-3 group"
            >
              <button
                type="button"
                :class="[
                  'mt-0.5 w-5 h-5 rounded-full border-2 flex items-center justify-center shrink-0',
                  step.is_done
                    ? 'bg-green-500 border-green-500 text-white'
                    : 'border-gray-300 hover:border-green-500',
                ]"
                :disabled="!isAuthenticated"
                :title="isAuthenticated ? (step.is_done ? 'Marquer comme à faire' : 'Marquer comme fait') : 'Admin requis'"
                @click="toggleStepDone(step)"
              >
                <svg v-if="step.is_done" viewBox="0 0 24 24" class="w-3 h-3" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
                  <path d="M5 12l5 5L20 7"/>
                </svg>
              </button>
              <div class="flex-1">
                <div
                  :class="[
                    'text-sm font-medium',
                    step.is_done ? 'line-through text-donut-muted' : 'text-donut-text',
                  ]"
                >
                  {{ step.title }}
                </div>
                <div v-if="step.step_date" class="text-xs text-donut-muted mt-0.5">
                  {{ formatDate(step.step_date) }}
                </div>
                <div v-if="step.description" class="text-xs text-donut-muted mt-0.5 whitespace-pre-line">
                  {{ step.description }}
                </div>
              </div>
              <button
                v-if="isAuthenticated"
                type="button"
                class="opacity-0 group-hover:opacity-100 text-donut-muted hover:text-red-500"
                title="Supprimer"
                @click="deleteStep(step)"
              >
                <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                  <path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/>
                </svg>
              </button>
            </li>
          </ul>

          <form
            v-if="isAuthenticated"
            class="flex items-center gap-2 pt-2"
            @submit.prevent="addStep"
          >
            <input
              v-model="newStepTitle"
              type="text"
              placeholder="Nouvelle étape…"
              class="flex-1 bg-donut-surface border border-donut-border rounded-lg px-3 py-2 text-sm outline-none focus:border-donut-text"
            />
            <button
              type="submit"
              :disabled="adminBusy || !newStepTitle.trim()"
              class="px-4 py-2 rounded-lg bg-gray-100 text-donut-text text-sm font-medium hover:bg-gray-200 disabled:opacity-50"
            >
              Ajouter
            </button>
          </form>
        </div>

        <!-- Tab : Images -->
        <div v-else-if="activeTab === 'images'">
          <ImageGallery :project-slug="project.slug" />
        </div>

        <!-- Tab : Discussions -->
        <div v-else-if="activeTab === 'discussions'" class="space-y-4">
          <LoadingState v-if="discussionsLoading" />
          <template v-else>
            <p v-if="(discussions ?? []).length === 0" class="text-donut-muted text-sm">
              Aucun message pour le moment.
            </p>
            <ul v-else class="space-y-4">
              <li
                v-for="d in discussions ?? []"
                :key="d.id"
                class="rounded-lg border border-donut-border bg-donut-surface p-4"
              >
                <div class="flex items-center gap-2 mb-1.5">
                  <Avatar :name="d.author_name" :size="28" />
                  <div class="text-sm font-medium text-donut-text">{{ d.author_name }}</div>
                  <div class="text-xs text-donut-muted">· {{ formatRelative(d.created_at) }}</div>
                </div>
                <p class="text-sm text-donut-text whitespace-pre-line leading-relaxed">{{ d.body }}</p>
                <ul v-if="d.replies && d.replies.length" class="mt-3 ml-6 pl-4 border-l border-donut-border space-y-3">
                  <li v-for="r in d.replies" :key="r.id">
                    <div class="flex items-center gap-2 mb-0.5">
                      <Avatar :name="r.author_name" :size="22" />
                      <div class="text-xs font-medium text-donut-text">{{ r.author_name }}</div>
                      <div class="text-xs text-donut-muted">· {{ formatRelative(r.created_at) }}</div>
                    </div>
                    <p class="text-sm text-donut-muted whitespace-pre-line">{{ r.body }}</p>
                  </li>
                </ul>
              </li>
            </ul>

            <form
              v-if="isAuthenticated"
              class="space-y-2 pt-4 border-t border-donut-border"
              @submit.prevent="postMessage"
            >
              <div class="text-sm font-medium text-donut-text">Nouveau message</div>
              <input
                v-model="newMessage.author_name"
                type="text"
                placeholder="Ton nom"
                class="w-full bg-donut-surface border border-donut-border rounded-lg px-3 py-2 text-sm outline-none focus:border-donut-text"
              />
              <textarea
                v-model="newMessage.body"
                rows="3"
                placeholder="Ton message…"
                class="w-full bg-donut-surface border border-donut-border rounded-lg px-3 py-2 text-sm outline-none focus:border-donut-text resize-y"
              />
              <div class="flex justify-end">
                <button
                  type="submit"
                  :disabled="adminBusy || !newMessage.author_name.trim() || !newMessage.body.trim()"
                  class="px-4 py-2 rounded-lg bg-donut-text text-white text-sm font-medium hover:bg-black disabled:opacity-50"
                >
                  Publier
                </button>
              </div>
            </form>
            <p v-else class="text-xs text-donut-muted">Connecte-toi en admin pour publier un message.</p>
          </template>
        </div>

        <!-- Tab : Ressources -->
        <div v-else class="space-y-3">
          <LoadingState v-if="resourcesLoading" />
          <template v-else>
            <p v-if="(resources ?? []).length === 0" class="text-donut-muted text-sm">
              Aucune ressource pour le moment.
            </p>
            <ul v-else class="space-y-2">
              <li
                v-for="r in resources ?? []"
                :key="r.id"
                class="flex items-start gap-3 rounded-lg border border-donut-border bg-donut-surface p-3"
              >
                <div class="w-8 h-8 rounded-md bg-gray-100 flex items-center justify-center shrink-0 text-donut-muted">
                  <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                    <path d="M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1M14 11a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1"/>
                  </svg>
                </div>
                <div class="flex-1 min-w-0">
                  <a
                    :href="r.url"
                    target="_blank"
                    rel="noopener"
                    class="text-sm font-medium text-donut-text hover:underline"
                  >
                    {{ r.title }}
                  </a>
                  <div class="text-xs text-donut-muted truncate">{{ r.url }}</div>
                  <div v-if="r.description" class="text-xs text-donut-muted mt-1">{{ r.description }}</div>
                </div>
                <span class="text-[10px] uppercase tracking-wide text-donut-muted">{{ r.kind }}</span>
              </li>
            </ul>

            <form
              v-if="isAuthenticated"
              class="space-y-2 pt-4 border-t border-donut-border"
              @submit.prevent="addResource"
            >
              <div class="text-sm font-medium text-donut-text">Nouvelle ressource</div>
              <input
                v-model="newResource.title"
                type="text"
                placeholder="Titre"
                class="w-full bg-donut-surface border border-donut-border rounded-lg px-3 py-2 text-sm outline-none focus:border-donut-text"
              />
              <div class="flex gap-2">
                <input
                  v-model="newResource.url"
                  type="url"
                  placeholder="https://…"
                  class="flex-1 bg-donut-surface border border-donut-border rounded-lg px-3 py-2 text-sm outline-none focus:border-donut-text"
                />
                <select
                  v-model="newResource.kind"
                  class="bg-donut-surface border border-donut-border rounded-lg px-3 py-2 text-sm outline-none focus:border-donut-text"
                >
                  <option value="link">Lien</option>
                  <option value="document">Document</option>
                  <option value="dataset">Dataset</option>
                  <option value="video">Vidéo</option>
                  <option value="other">Autre</option>
                </select>
              </div>
              <div class="flex justify-end">
                <button
                  type="submit"
                  :disabled="adminBusy || !newResource.title.trim() || !newResource.url.trim()"
                  class="px-4 py-2 rounded-lg bg-donut-text text-white text-sm font-medium hover:bg-black disabled:opacity-50"
                >
                  Ajouter
                </button>
              </div>
            </form>
            <p v-else class="text-xs text-donut-muted">Connecte-toi en admin pour ajouter une ressource.</p>
          </template>
        </div>
      </div>

      <!-- ============== COLONNE DROITE ============== -->
      <aside class="space-y-4">
        <!-- Card Progression + Rejoindre -->
        <div class="rounded-xl border border-donut-border bg-donut-surface p-5">
          <div class="text-sm font-medium text-donut-text mb-1">Progression</div>
          <div class="flex items-center gap-3 mb-4">
            <ProgressBar :value="project.progress_percent" :color="categoryColor" class="flex-1" />
            <span class="text-sm font-semibold tabular-nums">{{ project.progress_percent }}%</span>
          </div>
          <button
            type="button"
            class="w-full bg-donut-text text-white rounded-lg px-4 py-2.5 text-sm font-medium hover:bg-black transition-colors"
            @click="showJoinModal = true"
          >
            Rejoindre le projet
          </button>
        </div>

        <!-- Card Équipe -->
        <div class="rounded-xl border border-donut-border bg-donut-surface p-5">
          <div class="flex items-center gap-2 mb-4">
            <svg viewBox="0 0 24 24" class="w-4 h-4 text-donut-muted" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/>
            </svg>
            <span class="text-sm font-medium text-donut-text">Équipe ({{ project.members.length }})</span>
          </div>
          <p v-if="project.members.length === 0" class="text-sm text-donut-muted">
            Pas encore de membre. Sois le premier !
          </p>
          <ul v-else class="space-y-3">
            <li
              v-for="m in project.members"
              :key="m.id"
              class="flex items-center gap-3 group"
            >
              <Avatar :name="m.display_name" :initials="m.initials" :avatar-url="m.avatar_url" :size="34" />
              <div class="flex-1 min-w-0">
                <div class="text-sm font-medium text-donut-text truncate">{{ m.display_name }}</div>
                <!-- Lecture seule pour les visiteurs / édition inline pour les admins -->
                <div v-if="!isAuthenticated" class="text-xs text-donut-muted">{{ ROLE_LABEL[m.role] }}</div>
                <select
                  v-else
                  :value="m.role"
                  class="text-xs text-donut-muted bg-transparent border-0 -ml-1 pl-1 pr-4 py-0.5 rounded hover:bg-gray-100 focus:bg-gray-100 focus:text-donut-text outline-none"
                  @change="changeMemberRole(m, ($event.target as HTMLSelectElement).value as MemberRole)"
                >
                  <option value="admin">Admin</option>
                  <option value="contributor">Contributor</option>
                  <option value="observer">Observer</option>
                </select>
              </div>
              <!-- Sur desktop (group:hover supporté) : icône cachée par défaut, apparaît au survol.
                   Sur touch : le bouton reste visible (opacity-60) pour rester atteignable.
                   sm:opacity-0 ne s'applique qu'au-delà du breakpoint sm où le hover est fiable. -->
              <button
                v-if="isAuthenticated"
                type="button"
                class="opacity-60 sm:opacity-0 sm:group-hover:opacity-100 focus:opacity-100 transition-opacity text-donut-muted hover:text-red-500"
                title="Retirer du projet"
                aria-label="Retirer ce membre du projet"
                @click="removeMember(m)"
              >
                <svg viewBox="0 0 24 24" class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
                  <path d="M18 6 6 18M6 6l12 12"/>
                </svg>
              </button>
            </li>
          </ul>
        </div>
      </aside>
    </div>

    <!-- Modal Rejoindre -->
    <JoinProjectModal
      v-if="showJoinModal && project"
      :project-slug="project.slug"
      :exclude-slugs="project.members.map(m => m.slug)"
      @close="showJoinModal = false"
      @joined="onJoined"
    />
  </section>
</template>
