<!-- front-end/app/pages/oportunidades/[id].vue -->
<script setup lang="ts">
const route = useRoute()
const router = useRouter()
const { apiFetch } = useApi()
const { public: { apiBase } } = useRuntimeConfig()
const token = useCookie<string | null>('auth_token')

interface ApiEdital {
  ID: string; Numero: string; Orgao: string; Modalidade: string
  Objeto: string; Plataforma: string; ValorEstimado: number
  DataPublicacao: string; DataAbertura: string; Status: string
}

interface ApiFile {
  ID: string; Nome: string; Size: number; CreatedAt: string
}

const editalId = route.params.id as string

const { data: edital, refresh } = await useAsyncData(`edital-${editalId}`, () =>
  apiFetch<ApiEdital>(`/editais/${editalId}`)
)

const { data: files, refresh: refreshFiles } = await useAsyncData(`edital-files-${editalId}`, () =>
  apiFetch<ApiFile[]>(`/editais/${editalId}/files`),
  { server: false }
)

// ---------- labels ----------
const MODALIDADE_LABEL: Record<string, string> = {
  pregao_eletronico: 'Pregão Eletrônico',
  pregao_presencial: 'Pregão Presencial',
  concorrencia: 'Concorrência',
  dispensa: 'Dispensa',
  inexigibilidade: 'Inexigibilidade',
}

const STATUS_CFG: Record<string, { label: string; color: string; bg: string; icon: string }> = {
  em_analise:          { label: 'Mapeamento',           color: '#0284c7', bg: '#eff6ff', icon: '🔍' },
  elaborando_proposta: { label: 'Termo de Referência',  color: '#7c3aed', bg: '#faf5ff', icon: '✏️' },
  edital_publicado:    { label: 'Edital Publicado',     color: '#ea580c', bg: '#fff7ed', icon: '📢' },
  fase_lances:         { label: 'Fase de Lances',       color: '#3b82f6', bg: '#eff6ff', icon: '🏁' },
  habilitacao:         { label: 'Habilitação',          color: '#d97706', bg: '#fef3c7', icon: '📋' },
  recurso:             { label: 'Recursos',             color: '#d97706', bg: '#fffbeb', icon: '⚖️' },
  adjudicado:          { label: 'Adjudicado',           color: '#059669', bg: '#ecfdf5', icon: '✔️' },
  contrato:            { label: 'Contrato',             color: '#16a34a', bg: '#f0fdf4', icon: '📝' },
  vencida:             { label: 'Vencida',              color: '#16a34a', bg: '#f0fdf4', icon: '🏆' },
  perdida:             { label: 'Perdida',              color: '#dc2626', bg: '#fef2f2', icon: '✗' },
  deserta:             { label: 'Deserta/Fracassada',   color: '#64748b', bg: '#f8fafc', icon: '—' },
}

const PIPELINE_STAGES = ['Mapeamento', 'Termo de Referência', 'Edital Publicado', 'Fase de Lances', 'Habilitação', 'Recursos', 'Adjudicado', 'Contrato']
const statusToStage: Record<string, number> = {
  em_analise: 1, elaborando_proposta: 2, edital_publicado: 3,
  fase_lances: 4, habilitacao: 5, recurso: 6,
  adjudicado: 7, contrato: 8, vencida: 8, perdida: 8, deserta: 8,
}

const currentStage = computed(() => statusToStage[edital.value?.Status ?? ''] ?? 1)

function formatDate(iso: string): string {
  if (!iso) return '—'
  const [y, m, d] = iso.split('T')[0].split('-')
  return `${d}/${m}/${y}`
}
function formatDateRelative(iso: string): string {
  if (!iso) return ''
  const date = new Date(iso.split('T')[0])
  const now = new Date()
  const diff = Math.floor((date.getTime() - now.getTime()) / (1000 * 60 * 60 * 24))
  if (diff < 0) return `${Math.abs(diff)}d atrás`
  if (diff === 0) return 'Hoje'
  if (diff === 1) return 'Amanhã'
  return `em ${diff}d`
}
function formatBRL(value: number): string {
  return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL', maximumFractionDigits: 0 }).format(value)
}
function formatFileSize(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
}
function fileExtension(name: string): string {
  return name.split('.').pop()?.toUpperCase() ?? ''
}

// ---------- actions ----------
const uploadLoading = ref(false)

// ---------- toast notifications ----------
interface Toast { id: number; message: string; type: 'info' | 'success' | 'error'; icon: string }
const toasts = ref<Toast[]>([])
let toastCounter = 0

function showToast(message: string, type: Toast['type'] = 'info', icon = '', duration = 5000) {
  const id = ++toastCounter
  const icons: Record<string, string> = { info: '🔍', success: '✅', error: '❌' }
  toasts.value.push({ id, message, type, icon: icon || icons[type] || '' })
  setTimeout(() => { toasts.value = toasts.value.filter(t => t.id !== id) }, duration)
}

async function uploadArquivos(event: Event) {
  const input = event.target as HTMLInputElement
  if (!input.files?.length) return
  uploadLoading.value = true
  try {
    const hasPDF = Array.from(input.files).some(f => f.name.toLowerCase().endsWith('.pdf'))
    const fileCount = input.files.length

    const formData = new FormData()
    for (const file of Array.from(input.files)) formData.append('file', file)
    await $fetch(`${apiBase}/editais/${editalId}/files`, {
      method: 'POST',
      headers: { Authorization: `Bearer ${token.value}` },
      body: formData,
    })
    await refreshFiles()

    showToast(`${fileCount} arquivo${fileCount > 1 ? 's' : ''} enviado${fileCount > 1 ? 's' : ''} com sucesso`, 'success', '📎')

    if (hasPDF) {
      showToast('Analisando documento com IA…', 'info', '🔍', 6000)
      setTimeout(async () => {
        await refresh()
        if (edital.value?.Status === 'elaborando_proposta') {
          showToast('Termo de Referência detectado! Pipeline atualizado para "Elaborando Proposta".', 'success', '🚀', 7000)
        }
      }, 4000)
    }
  } catch {
    showToast('Erro ao enviar arquivo. Tente novamente.', 'error')
  } finally {
    uploadLoading.value = false
    input.value = ''
  }
}

async function downloadArquivo(fileId: string, nome: string) {
  const blob = await $fetch<Blob>(`${apiBase}/editais/${editalId}/files/${fileId}/download`, {
    headers: { Authorization: `Bearer ${token.value}` },
    responseType: 'blob',
  })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url; a.download = nome; a.click()
  URL.revokeObjectURL(url)
}

// ---------- file viewer modal ----------
const viewerOpen = ref(false)
const viewerUrl = ref('')
const viewerName = ref('')
const viewerLoading = ref(false)
const viewerType = ref<'pdf' | 'image'>('image')

async function visualizarArquivo(fileId: string, nome: string) {
  viewerName.value = nome
  viewerLoading.value = true
  viewerOpen.value = true
  try {
    const blob = await $fetch<Blob>(`${apiBase}/editais/${editalId}/files/${fileId}/download`, {
      headers: { Authorization: `Bearer ${token.value}` },
      responseType: 'blob',
    })
    const mimeType = nome.toLowerCase().endsWith('.pdf') ? 'application/pdf' : blob.type
    const typedBlob = new Blob([blob], { type: mimeType })
    viewerUrl.value = URL.createObjectURL(typedBlob)
    viewerType.value = nome.toLowerCase().endsWith('.pdf') ? 'pdf' : 'image'
  } catch {
    viewerOpen.value = false
  } finally {
    viewerLoading.value = false
  }
}

function fecharViewer() {
  viewerOpen.value = false
  if (viewerUrl.value) {
    URL.revokeObjectURL(viewerUrl.value)
    viewerUrl.value = ''
  }
}

// close viewer on Esc
if (import.meta.client) {
  const onEsc = (e: KeyboardEvent) => { if (e.key === 'Escape' && viewerOpen.value) fecharViewer() }
  onMounted(() => window.addEventListener('keydown', onEsc))
  onUnmounted(() => window.removeEventListener('keydown', onEsc))
}

function isViewable(nome: string): boolean {
  const ext = nome.toLowerCase().split('.').pop() ?? ''
  return ['pdf', 'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'].includes(ext)
}

async function excluirArquivo(fileId: string) {
  await apiFetch(`/editais/${editalId}/files/${fileId}`, { method: 'DELETE' })
  await refreshFiles()
}

const showDeleteConfirm = ref(false)
const deleting = ref(false)

function confirmarExclusao() {
  showDeleteConfirm.value = true
}

async function executarExclusao() {
  deleting.value = true
  try {
    await apiFetch(`/editais/${editalId}`, { method: 'DELETE' })
    showDeleteConfirm.value = false
    router.push('/oportunidades')
  } finally {
    deleting.value = false
  }
}

// ---------- edit modal ----------
const showEdit = ref(false)
const saving = ref(false)
const erroModal = ref('')
const MODALIDADE_OPTIONS = Object.entries(MODALIDADE_LABEL).map(([value, label]) => ({ value, label }))
const STATUS_OPTIONS = Object.entries(STATUS_CFG).map(([value, cfg]) => ({ value, label: cfg.label }))

const form = ref({
  Numero: '', Orgao: '', Modalidade: 'pregao_eletronico',
  Objeto: '', Plataforma: '', ValorEstimado: 0,
  DataPublicacao: '', DataAbertura: '', Status: 'em_analise',
})

function abrirEditar() {
  if (!edital.value) return
  const e = edital.value
  form.value = {
    Numero: e.Numero, Orgao: e.Orgao, Modalidade: e.Modalidade,
    Objeto: e.Objeto, Plataforma: e.Plataforma, ValorEstimado: e.ValorEstimado,
    DataPublicacao: e.DataPublicacao.split('T')[0], DataAbertura: e.DataAbertura.split('T')[0],
    Status: e.Status,
  }
  erroModal.value = ''
  showEdit.value = true
}

async function salvar() {
  erroModal.value = ''
  saving.value = true
  try {
    await apiFetch(`/editais/${editalId}`, { method: 'PUT', body: form.value })
    showEdit.value = false
    await refresh()
  } catch { erroModal.value = 'Erro ao salvar edital.' }
  finally { saving.value = false }
}
</script>

<template>
  <div class="detail-page">
    <!-- Top bar -->
    <header class="detail-topbar">
      <div class="topbar-left">
        <button class="back-btn" @click="router.push('/oportunidades')" title="Voltar">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><polyline points="12 19 5 12 12 5"/></svg>
        </button>
        <div>
          <p class="topbar-crumb">Oportunidades / Detalhe</p>
          <h1 class="topbar-title">{{ edital?.Numero }}</h1>
        </div>
      </div>
      <div class="topbar-actions">
        <button class="btn-outline" @click="abrirEditar">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
          Editar
        </button>
        <button class="btn-danger-outline" @click="confirmarExclusao">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
          Excluir
        </button>
      </div>
    </header>

    <div class="detail-content" v-if="edital">
      <!-- Hero section -->
      <section class="hero-card">
        <div class="hero-gradient" />
        <div class="hero-inner">
          <div class="hero-top">
            <div class="hero-badge-row">
              <span
                class="status-badge-lg"
                :style="{
                  background: STATUS_CFG[edital.Status]?.bg ?? '#f8fafc',
                  color: STATUS_CFG[edital.Status]?.color ?? '#64748b',
                  borderColor: STATUS_CFG[edital.Status]?.color ?? '#64748b',
                }"
              >
                <span class="status-icon">{{ STATUS_CFG[edital.Status]?.icon ?? '•' }}</span>
                {{ STATUS_CFG[edital.Status]?.label ?? edital.Status }}
              </span>
              <span class="modalidade-tag">{{ MODALIDADE_LABEL[edital.Modalidade] ?? edital.Modalidade }}</span>
            </div>
          </div>

          <h2 class="hero-orgao">{{ edital.Orgao }}</h2>
          <p class="hero-objeto" v-if="edital.Objeto">{{ edital.Objeto }}</p>

          <div class="hero-metrics">
            <div class="metric">
              <span class="metric-label">Valor Estimado</span>
              <span class="metric-value money">{{ formatBRL(edital.ValorEstimado) }}</span>
            </div>
            <div class="metric-divider" />
            <div class="metric">
              <span class="metric-label">Publicação</span>
              <span class="metric-value">{{ formatDate(edital.DataPublicacao) }}</span>
            </div>
            <div class="metric-divider" />
            <div class="metric">
              <span class="metric-label">Abertura</span>
              <span class="metric-value">
                {{ formatDate(edital.DataAbertura) }}
                <span class="metric-relative">{{ formatDateRelative(edital.DataAbertura) }}</span>
              </span>
            </div>
            <div class="metric-divider" />
            <div class="metric">
              <span class="metric-label">Plataforma</span>
              <span class="metric-value">{{ edital.Plataforma || '—' }}</span>
            </div>
          </div>
        </div>
      </section>

      <!-- Pipeline progress -->
      <section class="pipeline-section">
        <h3 class="section-title">Progresso no Pipeline</h3>
        <div class="pipeline-track">
          <div
            v-for="(stage, i) in PIPELINE_STAGES"
            :key="stage"
            class="pipeline-step"
            :class="{
              completed: i + 1 < currentStage,
              current: i + 1 === currentStage,
              future: i + 1 > currentStage,
            }"
          >
            <div class="step-dot">
              <svg v-if="i + 1 < currentStage" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
              <span v-else-if="i + 1 === currentStage" class="dot-pulse" />
              <span v-else class="dot-empty" />
            </div>
            <span class="step-label">{{ stage }}</span>
            <div v-if="i < PIPELINE_STAGES.length - 1" class="step-connector" :class="{ filled: i + 1 < currentStage }" />
          </div>
        </div>
      </section>

      <!-- Two-column bottom -->
      <div class="bottom-grid">
        <!-- Info card -->
        <section class="info-card">
          <h3 class="section-title">Informações do Edital</h3>
          <div class="info-rows">
            <div class="info-row">
              <span class="info-key">Número</span>
              <span class="info-val mono">{{ edital.Numero }}</span>
            </div>
            <div class="info-row">
              <span class="info-key">Órgão</span>
              <span class="info-val">{{ edital.Orgao }}</span>
            </div>
            <div class="info-row">
              <span class="info-key">Modalidade</span>
              <span class="info-val">{{ MODALIDADE_LABEL[edital.Modalidade] ?? edital.Modalidade }}</span>
            </div>
            <div class="info-row">
              <span class="info-key">Plataforma</span>
              <span class="info-val">{{ edital.Plataforma || '—' }}</span>
            </div>
            <div class="info-row">
              <span class="info-key">Valor Estimado</span>
              <span class="info-val mono">{{ formatBRL(edital.ValorEstimado) }}</span>
            </div>
            <div class="info-row">
              <span class="info-key">Data Publicação</span>
              <span class="info-val">{{ formatDate(edital.DataPublicacao) }}</span>
            </div>
            <div class="info-row">
              <span class="info-key">Data Abertura</span>
              <span class="info-val">{{ formatDate(edital.DataAbertura) }}</span>
            </div>
            <div class="info-row">
              <span class="info-key">Status</span>
              <span
                class="badge-sm"
                :style="{ background: STATUS_CFG[edital.Status]?.bg, color: STATUS_CFG[edital.Status]?.color }"
              >{{ STATUS_CFG[edital.Status]?.label ?? edital.Status }}</span>
            </div>
            <div class="info-row full" v-if="edital.Objeto">
              <span class="info-key">Objeto</span>
              <span class="info-val obj-text">{{ edital.Objeto }}</span>
            </div>
          </div>
        </section>

        <!-- Files card -->
        <section class="files-card">
          <div class="files-card-header">
            <h3 class="section-title">Arquivos</h3>
            <label class="upload-btn" :class="{ loading: uploadLoading }">
              <input type="file" multiple style="display:none" @change="uploadArquivos" />
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
              {{ uploadLoading ? 'Enviando…' : 'Upload' }}
            </label>
          </div>

          <div v-if="!files || files.length === 0" class="files-empty-state">
            <div class="empty-icon">
              <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
            </div>
            <p>Nenhum arquivo anexado</p>
            <label class="empty-upload-btn">
              <input type="file" multiple style="display:none" @change="uploadArquivos" />
              Fazer upload
            </label>
          </div>

          <div v-else class="files-list">
            <div v-for="f in files" :key="f.ID" class="file-card">
              <div class="file-ext-badge" :class="fileExtension(f.Nome).toLowerCase()">
                {{ fileExtension(f.Nome) }}
              </div>
              <div class="file-info">
                <span class="file-name">{{ f.Nome }}</span>
                <span class="file-meta">{{ formatFileSize(f.Size) }} · {{ formatDate(f.CreatedAt) }}</span>
              </div>
              <div class="file-actions">
                <button v-if="isViewable(f.Nome)" class="file-action-btn view" @click="visualizarArquivo(f.ID, f.Nome)" title="Visualizar">
                  <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
                </button>
                <button class="file-action-btn dl" @click="downloadArquivo(f.ID, f.Nome)" title="Baixar">
                  <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
                </button>
                <button class="file-action-btn rm" @click="excluirArquivo(f.ID)" title="Remover">
                  <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
                </button>
              </div>
            </div>
          </div>
        </section>
      </div>
    </div>

    <!-- File Viewer Lightbox -->
    <Teleport to="body">
      <div v-if="viewerOpen" class="viewer-overlay" @click.self="fecharViewer">
        <!-- Loading -->
        <div v-if="viewerLoading" class="viewer-loading">
          <div class="viewer-spinner" />
          <span>Carregando…</span>
        </div>

        <!-- Content -->
        <div v-else class="viewer-container" @click.stop>
          <!-- Header -->
          <div class="viewer-header">
            <span class="viewer-filename">{{ viewerName }}</span>
            <button class="viewer-close" @click="fecharViewer" title="Fechar (Esc)">
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
            </button>
          </div>

          <!-- PDF -->
          <iframe
            v-if="viewerType === 'pdf'"
            :src="viewerUrl"
            class="viewer-pdf"
          />

          <!-- Image -->
          <div v-else class="viewer-image-wrapper">
            <img :src="viewerUrl" :alt="viewerName" class="viewer-image" />
          </div>
        </div>
      </div>
    </Teleport>

    <!-- Toast notifications -->
    <Teleport to="body">
      <div class="toast-container">
        <TransitionGroup name="toast">
          <div v-for="t in toasts" :key="t.id" class="toast" :class="t.type">
            <span class="toast-icon">{{ t.icon }}</span>
            <span class="toast-msg">{{ t.message }}</span>
          </div>
        </TransitionGroup>
      </div>
    </Teleport>

    <!-- Delete Confirmation Modal -->
    <Teleport to="body">
      <div v-if="showDeleteConfirm" class="modal-overlay" @click.self="showDeleteConfirm = false">
        <div class="modal-delete" @click.stop>
          <div class="modal-delete-icon">
            <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
              <polyline points="3 6 5 6 21 6"/>
              <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
              <line x1="10" y1="11" x2="10" y2="17"/>
              <line x1="14" y1="11" x2="14" y2="17"/>
            </svg>
          </div>
          <h3 class="modal-delete-title">Excluir Oportunidade</h3>
          <p class="modal-delete-text">
            Tem certeza que deseja excluir o edital
            <strong>{{ edital?.Numero }}</strong>?
            <br/>Esta ação não pode ser desfeita.
          </p>
          <div class="modal-delete-actions">
            <button class="btn-cancel-del" @click="showDeleteConfirm = false">Cancelar</button>
            <button class="btn-delete" :disabled="deleting" @click="executarExclusao">
              {{ deleting ? 'Excluindo…' : 'Sim, excluir' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- Edit Modal -->
    <Teleport to="body">
      <div v-if="showEdit" class="modal-overlay" @click.self="showEdit = false">
        <div class="modal-edit" @click.stop>
          <div class="modal-edit-header">
            <h3>Editar Edital</h3>
            <button class="close-btn" @click="showEdit = false">
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
            </button>
          </div>
          <div class="modal-edit-body">
            <div class="form-row">
              <div class="field"><label>Nº do Edital *</label><input v-model="form.Numero" class="inp" /></div>
              <div class="field"><label>Órgão *</label><input v-model="form.Orgao" class="inp" /></div>
            </div>
            <div class="field"><label>Objeto</label><textarea v-model="form.Objeto" class="inp textarea" rows="3" /></div>
            <div class="form-row">
              <div class="field">
                <label>Modalidade *</label>
                <select v-model="form.Modalidade" class="inp">
                  <option v-for="opt in MODALIDADE_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</option>
                </select>
              </div>
              <div class="field"><label>Plataforma</label><input v-model="form.Plataforma" class="inp" /></div>
            </div>
            <div class="form-row">
              <div class="field"><label>Valor Estimado (R$)</label><input v-model.number="form.ValorEstimado" type="number" min="0" step="0.01" class="inp" /></div>
              <div class="field">
                <label>Status *</label>
                <select v-model="form.Status" class="inp">
                  <option v-for="opt in STATUS_OPTIONS" :key="opt.value" :value="opt.value">{{ opt.label }}</option>
                </select>
              </div>
            </div>
            <div class="form-row">
              <div class="field"><label>Data Publicação *</label><input v-model="form.DataPublicacao" type="date" class="inp" /></div>
              <div class="field"><label>Data Abertura *</label><input v-model="form.DataAbertura" type="date" class="inp" /></div>
            </div>
            <p v-if="erroModal" class="erro-msg">{{ erroModal }}</p>
          </div>
          <div class="modal-edit-footer">
            <button class="btn-cancel" @click="showEdit = false">Cancelar</button>
            <button class="btn-save" :disabled="saving" @click="salvar">{{ saving ? 'Salvando…' : 'Salvar Alterações' }}</button>
          </div>
        </div>
      </div>
    </Teleport>
  </div>
</template>

<style scoped>
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300..700;1,9..40,300..700&family=JetBrains+Mono:wght@400;600&display=swap');

.detail-page {
  display: flex; flex-direction: column; height: 100vh;
  font-family: 'DM Sans', system-ui, sans-serif;
  background: #f1f3f8;
}

/* ── Topbar ── */
.detail-topbar {
  background: white;
  border-bottom: 1px solid #e2e8f0;
  padding: 12px 28px;
  display: flex; align-items: center; justify-content: space-between;
  flex-shrink: 0;
}
.topbar-left { display: flex; align-items: center; gap: 14px; }
.back-btn {
  width: 36px; height: 36px; border-radius: 10px;
  border: 1px solid #e2e8f0; background: white;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; color: #475569; transition: all .15s;
}
.back-btn:hover { background: #f8fafc; border-color: #cbd5e1; color: #1e293b; }
.topbar-crumb { font-size: 11px; color: #94a3b8; letter-spacing: .2px; }
.topbar-title { font-size: 17px; font-weight: 700; color: #0f172a; margin-top: 1px; }
.topbar-actions { display: flex; gap: 8px; }
.btn-outline {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 16px; border-radius: 8px;
  border: 1px solid #e2e8f0; background: white;
  font-size: 13px; font-weight: 600; color: #334155; cursor: pointer;
  transition: all .15s;
}
.btn-outline:hover { background: #f8fafc; border-color: #cbd5e1; }
.btn-danger-outline {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 16px; border-radius: 8px;
  border: 1px solid #fecaca; background: white;
  font-size: 13px; font-weight: 600; color: #dc2626; cursor: pointer;
  transition: all .15s;
}
.btn-danger-outline:hover { background: #fef2f2; }

/* ── Content ── */
.detail-content {
  flex: 1; overflow-y: auto;
  padding: 24px 28px 40px;
  display: flex; flex-direction: column; gap: 20px;
}

/* ── Hero Card ── */
.hero-card {
  background: white;
  border-radius: 16px;
  border: 1px solid #e2e8f0;
  overflow: hidden;
  position: relative;
  box-shadow: 0 1px 3px rgba(0,0,0,.04);
  flex-shrink: 0;
}
.hero-gradient {
  height: 4px;
  background: linear-gradient(90deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
}
.hero-inner { padding: 24px 28px 28px; }
.hero-top { margin-bottom: 16px; }
.hero-badge-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.status-badge-lg {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 14px; border-radius: 20px;
  font-size: 13px; font-weight: 700;
  border: 1.5px solid;
}
.status-icon { font-size: 14px; }
.modalidade-tag {
  padding: 4px 12px; border-radius: 6px;
  font-size: 12px; font-weight: 600; color: #475569;
  background: #f1f5f9; border: 1px solid #e2e8f0;
}
.hero-orgao {
  font-size: 24px; font-weight: 700; color: #0f172a;
  margin: 0 0 6px; line-height: 1.25;
  letter-spacing: -0.3px;
}
.hero-objeto {
  font-size: 14px; color: #64748b; line-height: 1.55;
  max-width: 700px; margin: 0 0 24px;
}
.hero-metrics {
  display: flex; align-items: stretch; gap: 0;
  background: #f8fafc; border-radius: 12px;
  border: 1px solid #e9edf3;
  overflow: hidden;
}
.metric {
  flex: 1; padding: 16px 20px;
  display: flex; flex-direction: column; gap: 4px;
}
.metric-divider { width: 1px; background: #e2e8f0; flex-shrink: 0; }
.metric-label {
  font-size: 10.5px; font-weight: 700; color: #94a3b8;
  text-transform: uppercase; letter-spacing: .8px;
}
.metric-value {
  font-size: 15px; font-weight: 700; color: #1e293b;
  display: flex; align-items: baseline; gap: 8px;
}
.metric-value.money {
  font-family: 'JetBrains Mono', monospace;
  color: #059669; font-size: 16px;
}
.metric-relative {
  font-size: 11px; font-weight: 600; color: #94a3b8;
  background: #f1f5f9; padding: 1px 7px; border-radius: 4px;
}

/* ── Pipeline ── */
.pipeline-section {
  background: white; border-radius: 16px;
  border: 1px solid #e2e8f0; padding: 24px 28px;
  box-shadow: 0 1px 3px rgba(0,0,0,.04);
  flex-shrink: 0;
}
.section-title {
  font-size: 14px; font-weight: 700; color: #0f172a;
  margin: 0 0 18px; letter-spacing: -0.2px;
}
.pipeline-track {
  display: flex; align-items: flex-start;
  position: relative;
}
.pipeline-step {
  display: flex; flex-direction: column; align-items: center;
  flex: 1; position: relative; min-width: 0;
}
.step-dot {
  width: 28px; height: 28px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  position: relative; z-index: 2;
  transition: all .3s;
}
.completed .step-dot {
  background: linear-gradient(135deg, #667eea, #764ba2);
  color: white;
}
.current .step-dot {
  background: white;
  border: 3px solid #667eea;
  box-shadow: 0 0 0 4px rgba(102,126,234,.2);
}
.future .step-dot {
  background: #f1f5f9; border: 2px solid #e2e8f0;
}
.dot-pulse {
  width: 8px; height: 8px; border-radius: 50%;
  background: #667eea;
  animation: pulse 1.5s ease infinite;
}
@keyframes pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.3); opacity: .7; }
}
.dot-empty { width: 6px; height: 6px; border-radius: 50%; background: #cbd5e1; }
.step-label {
  margin-top: 8px; font-size: 11px; font-weight: 600;
  color: #94a3b8; text-align: center;
  line-height: 1.3;
}
.completed .step-label { color: #667eea; }
.current .step-label { color: #1e293b; font-weight: 700; }
.step-connector {
  position: absolute; top: 13px;
  left: calc(50% + 16px); right: calc(-50% + 16px);
  height: 3px; background: #e9edf3; border-radius: 2px; z-index: 1;
}
.step-connector.filled {
  background: linear-gradient(90deg, #667eea, #764ba2);
}

/* ── Bottom grid ── */
.bottom-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; flex-shrink: 0; }

/* ── Info card ── */
.info-card {
  background: white; border-radius: 16px;
  border: 1px solid #e2e8f0; padding: 24px 28px;
  box-shadow: 0 1px 3px rgba(0,0,0,.04);
}
.info-rows { display: flex; flex-direction: column; }
.info-row {
  display: flex; align-items: baseline; justify-content: space-between;
  padding: 11px 0; border-bottom: 1px solid #f5f7fa;
}
.info-row:last-child { border-bottom: none; }
.info-row.full { flex-direction: column; gap: 6px; }
.info-key {
  font-size: 12.5px; font-weight: 500; color: #94a3b8;
  min-width: 120px; flex-shrink: 0;
}
.info-val { font-size: 13.5px; font-weight: 600; color: #1e293b; text-align: right; }
.info-val.mono { font-family: 'JetBrains Mono', monospace; font-size: 13px; }
.obj-text { text-align: left; line-height: 1.5; font-weight: 500; color: #475569; }
.badge-sm {
  display: inline-block; padding: 3px 10px; border-radius: 20px;
  font-size: 11.5px; font-weight: 700;
}

/* ── Toast notifications ── */
.toast-container {
  position: fixed; bottom: 20px; right: 20px;
  z-index: 3000;
  display: flex; flex-direction: column-reverse; gap: 8px;
  pointer-events: none;
  max-width: 380px;
}
.toast {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 14px 18px; border-radius: 12px;
  background: white;
  box-shadow: 0 8px 30px rgba(0,0,0,.15), 0 2px 8px rgba(0,0,0,.08);
  border-left: 4px solid #3b82f6;
  pointer-events: auto;
  animation: toastIn .35s cubic-bezier(.21,1.02,.73,1);
}
.toast.success { border-left-color: #16a34a; }
.toast.error { border-left-color: #dc2626; }
.toast.info { border-left-color: #3b82f6; }
.toast-icon { font-size: 18px; flex-shrink: 0; line-height: 1.2; }
.toast-msg { font-size: 13px; font-weight: 500; color: #1e293b; line-height: 1.45; }

@keyframes toastIn {
  from { opacity: 0; transform: translateX(40px) scale(.95); }
  to { opacity: 1; transform: translateX(0) scale(1); }
}
.toast-enter-active { animation: toastIn .35s cubic-bezier(.21,1.02,.73,1); }
.toast-leave-active { transition: all .25s ease; }
.toast-leave-to { opacity: 0; transform: translateX(40px) scale(.95); }

/* ── Files card ── */
.files-card {
  background: white; border-radius: 16px;
  border: 1px solid #e2e8f0;
  box-shadow: 0 1px 3px rgba(0,0,0,.04);
  display: flex; flex-direction: column;
  overflow: hidden; min-width: 0;
}
.files-card-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 20px 24px 0;
}
.upload-btn {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 14px; border-radius: 8px;
  background: linear-gradient(135deg, #667eea, #764ba2);
  color: white; font-size: 12.5px; font-weight: 600;
  cursor: pointer; border: none; transition: all .15s;
}
.upload-btn:hover { opacity: .9; transform: translateY(-1px); }
.upload-btn.loading { opacity: .6; pointer-events: none; }

.files-empty-state {
  flex: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 40px 20px; gap: 12px;
}
.empty-icon { color: #cbd5e1; }
.files-empty-state p { font-size: 13px; color: #94a3b8; margin: 0; }
.empty-upload-btn {
  margin-top: 4px; padding: 8px 18px; border-radius: 8px;
  background: #f1f5f9; color: #475569; font-size: 13px;
  font-weight: 600; cursor: pointer; border: 1px solid #e2e8f0;
  transition: all .15s;
}
.empty-upload-btn:hover { background: #e2e8f0; }

.files-list {
  padding: 16px 20px 20px;
  display: flex; flex-direction: column; gap: 8px;
}
.file-card {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; border-radius: 10px;
  background: #f8fafc; border: 1px solid #f1f5f9;
  transition: all .15s;
  overflow: hidden;
}
.file-card:hover { background: #f1f5f9; border-color: #e2e8f0; }
.file-ext-badge {
  width: 40px; height: 40px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 800; color: white;
  background: #94a3b8; flex-shrink: 0;
  font-family: 'JetBrains Mono', monospace;
  letter-spacing: .5px;
}
.file-ext-badge.pdf { background: linear-gradient(135deg, #ef4444, #dc2626); }
.file-ext-badge.doc, .file-ext-badge.docx { background: linear-gradient(135deg, #3b82f6, #2563eb); }
.file-ext-badge.xls, .file-ext-badge.xlsx { background: linear-gradient(135deg, #22c55e, #16a34a); }
.file-ext-badge.jpg, .file-ext-badge.png, .file-ext-badge.jpeg { background: linear-gradient(135deg, #f59e0b, #d97706); }
.file-ext-badge.zip, .file-ext-badge.rar { background: linear-gradient(135deg, #8b5cf6, #7c3aed); }
.file-info { flex: 1; min-width: 0; }
.file-name {
  font-size: 13px; font-weight: 600; color: #1e293b;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  display: block;
}
.file-meta { font-size: 11.5px; color: #94a3b8; margin-top: 2px; display: block; }
.file-actions { display: flex; gap: 4px; flex-shrink: 0; }
.file-action-btn {
  width: 32px; height: 32px; border-radius: 8px;
  border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: all .15s;
}
.file-action-btn.view { background: #f0fdf4; color: #16a34a; }
.file-action-btn.view:hover { background: #dcfce7; }
.file-action-btn.dl { background: #eff6ff; color: #2563eb; }
.file-action-btn.dl:hover { background: #dbeafe; }
.file-action-btn.rm { background: transparent; color: #cbd5e1; }
.file-action-btn.rm:hover { background: #fef2f2; color: #dc2626; }

/* ── File Viewer Lightbox ── */
.viewer-overlay {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, .75);
  backdrop-filter: blur(8px);
  z-index: 2000;
  display: flex; align-items: center; justify-content: center;
  animation: fadeIn .2s ease;
}
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
.viewer-loading {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  color: white; font-size: 14px; font-weight: 600;
}
.viewer-spinner {
  width: 36px; height: 36px; border-radius: 50%;
  border: 3px solid rgba(255,255,255,.2);
  border-top-color: white;
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.viewer-container {
  display: flex; flex-direction: column;
  width: 90vw; max-width: 1100px;
  height: 90vh;
  border-radius: 14px;
  overflow: hidden;
  background: #1e1e1e;
  box-shadow: 0 30px 80px rgba(0,0,0,.5);
  animation: viewerIn .25s ease;
}
@keyframes viewerIn {
  from { opacity: 0; transform: scale(.95); }
  to { opacity: 1; transform: scale(1); }
}

.viewer-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 18px;
  background: #111;
  border-bottom: 1px solid rgba(255,255,255,.08);
  flex-shrink: 0;
}
.viewer-filename {
  font-size: 13px; font-weight: 600; color: #e2e8f0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.viewer-close {
  width: 34px; height: 34px; border-radius: 8px;
  border: none; background: rgba(255,255,255,.08);
  color: #94a3b8; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: all .15s;
}
.viewer-close:hover { background: rgba(255,255,255,.15); color: white; }

.viewer-pdf {
  flex: 1; width: 100%; border: none;
  background: #2a2a2a;
}

.viewer-image-wrapper {
  flex: 1; display: flex; align-items: center; justify-content: center;
  padding: 20px; overflow: auto;
  background: #111;
}
.viewer-image {
  max-width: 100%; max-height: 100%;
  object-fit: contain;
  border-radius: 4px;
  box-shadow: 0 4px 20px rgba(0,0,0,.4);
}

/* ── Modal ── */
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(15,23,42,.5);
  backdrop-filter: blur(4px);
  z-index: 1000;
  display: flex; align-items: center; justify-content: center;
}
.modal-edit {
  background: white; border-radius: 16px;
  width: 640px; max-width: 95vw; max-height: 90vh; overflow-y: auto;
  box-shadow: 0 20px 60px rgba(0,0,0,.25);
  animation: modalIn .2s ease;
}
@keyframes modalIn {
  from { opacity: 0; transform: translateY(10px) scale(.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.modal-edit-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 20px 24px; border-bottom: 1px solid #f1f5f9;
}
.modal-edit-header h3 { font-size: 16px; font-weight: 700; color: #0f172a; margin: 0; }
.close-btn {
  width: 32px; height: 32px; border-radius: 8px;
  border: none; background: #f8fafc; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  color: #94a3b8; transition: all .15s;
}
.close-btn:hover { background: #f1f5f9; color: #475569; }
.modal-edit-body { padding: 24px 24px; display: flex; flex-direction: column; gap: 16px; }
.modal-edit-footer {
  display: flex; justify-content: flex-end; gap: 10px;
  padding: 16px 24px; border-top: 1px solid #f1f5f9;
}
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.field { display: flex; flex-direction: column; gap: 5px; }
.field label { font-size: 12px; font-weight: 700; color: #475569; }
.inp {
  border: 1.5px solid #e2e8f0; border-radius: 9px;
  padding: 9px 12px; font-size: 13.5px; color: #1e293b;
  width: 100%; box-sizing: border-box;
  font-family: 'DM Sans', system-ui, sans-serif;
  transition: all .15s;
}
.inp:focus { outline: none; border-color: #667eea; box-shadow: 0 0 0 3px rgba(102,126,234,.12); }
.textarea { resize: vertical; min-height: 60px; }
.btn-cancel {
  background: #f8fafc; color: #475569; border: 1px solid #e2e8f0;
  border-radius: 9px; padding: 9px 20px; font-size: 13px;
  font-weight: 600; cursor: pointer; transition: all .15s;
}
.btn-cancel:hover { background: #f1f5f9; }
.btn-save {
  background: linear-gradient(135deg, #667eea, #764ba2);
  color: white; border: none; border-radius: 9px;
  padding: 9px 22px; font-size: 13px; font-weight: 700;
  cursor: pointer; transition: all .15s;
}
.btn-save:hover { opacity: .9; transform: translateY(-1px); }
.btn-save:disabled { opacity: .5; cursor: not-allowed; transform: none; }
.erro-msg { color: #dc2626; font-size: 12px; margin: 0; }

/* ── Delete confirm modal ── */
.modal-delete {
  background: white; border-radius: 16px;
  width: 400px; max-width: 90vw;
  padding: 32px 28px 24px;
  box-shadow: 0 20px 60px rgba(0,0,0,.25);
  text-align: center;
  animation: modalPopIn .2s ease;
}
@keyframes modalPopIn {
  from { opacity: 0; transform: scale(.92); }
  to { opacity: 1; transform: scale(1); }
}
.modal-delete-icon {
  width: 56px; height: 56px; border-radius: 50%;
  background: #fef2f2; color: #dc2626;
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto 16px;
}
.modal-delete-title {
  font-size: 16px; font-weight: 700; color: #0f172a;
  margin: 0 0 8px;
}
.modal-delete-text {
  font-size: 13.5px; color: #64748b; line-height: 1.6;
  margin: 0 0 24px;
}
.modal-delete-text strong { color: #1e293b; }
.modal-delete-actions {
  display: flex; gap: 10px; justify-content: center;
}
.btn-cancel-del {
  background: #f8fafc; color: #475569; border: 1px solid #e2e8f0;
  border-radius: 9px; padding: 9px 20px; font-size: 13px;
  font-weight: 600; cursor: pointer; transition: all .15s;
}
.btn-cancel-del:hover { background: #f1f5f9; }
.btn-delete {
  background: #dc2626; color: white;
  border: none; border-radius: 9px;
  padding: 9px 22px; font-size: 13px; font-weight: 700;
  cursor: pointer; transition: all .15s;
}
.btn-delete:hover { background: #b91c1c; }
.btn-delete:disabled { opacity: .5; cursor: not-allowed; }

/* ── Responsive ── */
@media (max-width: 900px) {
  .bottom-grid { grid-template-columns: 1fr; }
  .hero-metrics { flex-direction: column; }
  .metric-divider { width: 100%; height: 1px; }
  .pipeline-track { overflow-x: auto; }
}
</style>
