const { useState, useEffect, useCallback, useRef, useMemo } = React;

// ─── CONSTANTS & CONFIG ───
const PRIORITY_CONFIG = {
  urgent: { label: "Urgente", color: "#ef4444", bg: "#fef2f2", icon: "🔴" },
  high: { label: "Alta", color: "#f97316", bg: "#fff7ed", icon: "🟠" },
  medium: { label: "Media", color: "#eab308", bg: "#fefce8", icon: "🟡" },
  low: { label: "Baja", color: "#22c55e", bg: "#f0fdf4", icon: "🟢" },
};

const DEFAULT_COLUMNS = {
  tech: [
    { id: "backlog", title: "Backlog", color: "#64748b" },
    { id: "todo", title: "Por hacer", color: "#3b82f6" },
    { id: "in_progress", title: "En progreso", color: "#f59e0b" },
    { id: "review", title: "En revisión", color: "#8b5cf6" },
    { id: "done", title: "Completado", color: "#22c55e" },
  ],
  institutional: [
    { id: "proposal", title: "Propuesta", color: "#64748b" },
    { id: "approved", title: "Aprobado", color: "#3b82f6" },
    { id: "execution", title: "En ejecución", color: "#f59e0b" },
    { id: "monitoring", title: "Seguimiento", color: "#8b5cf6" },
    { id: "closed", title: "Cerrado", color: "#22c55e" },
  ],
  tickets: [
    { id: "new", title: "Nuevo", color: "#ef4444" },
    { id: "assigned", title: "Asignado", color: "#3b82f6" },
    { id: "working", title: "En atención", color: "#f59e0b" },
    { id: "resolved", title: "Resuelto", color: "#22c55e" },
  ],
  agreements: [
    { id: "registered", title: "Registrado", color: "#64748b" },
    { id: "in_progress", title: "En proceso", color: "#f59e0b" },
    { id: "blocked", title: "Bloqueado", color: "#ef4444" },
    { id: "fulfilled", title: "Cumplido", color: "#22c55e" },
  ],
};

const FLOW_TYPES = {
  tech: { label: "Proyectos Tech", icon: "💻", color: "#6366f1" },
  institutional: { label: "Institucional", icon: "🏛️", color: "#0891b2" },
  tickets: { label: "Tickets", icon: "🎫", color: "#e11d48" },
  agreements: { label: "Acuerdos Directivo", icon: "📋", color: "#7c3aed" },
};

const DEFAULT_WORKSPACES = [
  { id: "dev_tech", name: "Desarrollo Tecnológico", icon: "⚡", color: "#6366f1", memberIds: ["gaby"], ticketEnabled: true, ticketSlug: "soporte-tech", ticketCategories: ["Soporte técnico", "Acceso a plataforma", "Error en sistema", "Solicitud de desarrollo"], ticketAssignMode: "rotation", ticketBoardId: null, ticketColumnId: null, ticketAssigneeId: null, ticketSedes: ["Monterrey", "CDMX", "Guadalajara", "En línea"] },
  { id: "directivo", name: "Directivo", icon: "🏛️", color: "#0891b2", memberIds: ["gaby", "enrique", "rocky", "gabriela_s", "estrella", "juancarlos", "hernan"], ticketEnabled: false, ticketSlug: null, ticketCategories: [], ticketAssignMode: "rotation", ticketBoardId: null, ticketColumnId: null, ticketAssigneeId: null, ticketSedes: [] },
  { id: "control_escolar", name: "Control Escolar", icon: "📚", color: "#059669", memberIds: ["gaby", "gabriela_s"], ticketEnabled: true, ticketSlug: "control-escolar", ticketCategories: ["Inscripción", "Calificaciones", "Certificados", "Baja/Alta"], ticketAssignMode: "manual", ticketBoardId: null, ticketColumnId: null, ticketAssigneeId: "gabriela_s", ticketSedes: ["Monterrey", "CDMX"] },
  { id: "vinculacion", name: "Vinculación Docente", icon: "🤝", color: "#d97706", memberIds: ["gaby", "estrella"], ticketEnabled: false, ticketSlug: null, ticketCategories: [], ticketAssignMode: "rotation", ticketBoardId: null, ticketColumnId: null, ticketAssigneeId: null, ticketSedes: [] },
  { id: "internacional", name: "Internacionalización", icon: "🌎", color: "#7c3aed", memberIds: ["gaby", "juancarlos", "hernan"], ticketEnabled: false, ticketSlug: null, ticketCategories: [], ticketAssignMode: "rotation", ticketBoardId: null, ticketColumnId: null, ticketAssigneeId: null, ticketSedes: [] },
  { id: "finanzas", name: "Finanzas", icon: "💰", color: "#dc2626", memberIds: ["gaby", "rocky"], ticketEnabled: false, ticketSlug: null, ticketCategories: [], ticketAssignMode: "rotation", ticketBoardId: null, ticketColumnId: null, ticketAssigneeId: null, ticketSedes: [] },
];

const DEFAULT_MEMBERS = [
  { id: "gaby", name: "Gaby", role: "admin", avatar: "G", color: "#6366f1" },
  { id: "enrique", name: "Dr. Enrique Navarro", role: "member", avatar: "EN", color: "#0891b2" },
  { id: "rocky", name: "Dr. Rocky Cruz", role: "member", avatar: "RC", color: "#dc2626" },
  { id: "gabriela_s", name: "Dra. Gabriela Sámano", role: "member", avatar: "GS", color: "#059669" },
  { id: "estrella", name: "Mtra. Estrella Pérez", role: "member", avatar: "EP", color: "#d97706" },
  { id: "juancarlos", name: "Dr. Juan Carlos Viñas", role: "member", avatar: "JV", color: "#7c3aed" },
  { id: "hernan", name: "Dr. Hernán Ocampo", role: "member", avatar: "HO", color: "#475569" },
];

const TAG_PRESETS = [
  { id: "platform", label: "Plataforma", color: "#6366f1" },
  { id: "diplomado", label: "Diplomado", color: "#0891b2" },
  { id: "website", label: "Sitio Web", color: "#059669" },
  { id: "enrollment", label: "Matrícula", color: "#d97706" },
  { id: "bug", label: "Bug", color: "#dc2626" },
  { id: "improvement", label: "Mejora", color: "#8b5cf6" },
  { id: "urgent_tag", label: "Urgente", color: "#ef4444" },
  { id: "peru", label: "Perú", color: "#f97316" },
  { id: "marketing", label: "Marketing", color: "#ec4899" },
  { id: "ssl", label: "SSL/Hosting", color: "#475569" },
];

const uid = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 8);

// Time tracking helpers — business days only (no weekends, no Mexican holidays)
const getMexicanHolidays = (year) => {
  const holidays = [];
  // Fixed holidays
  holidays.push(`${year}-01-01`); // Año Nuevo
  holidays.push(`${year}-02-05`); // Constitución (se mueve al lunes más cercano, pero simplificamos con fecha fija)
  holidays.push(`${year}-03-21`); // Natalicio de Benito Juárez
  holidays.push(`${year}-05-01`); // Día del Trabajo
  holidays.push(`${year}-09-16`); // Independencia
  holidays.push(`${year}-11-20`); // Revolución (se mueve al lunes más cercano)
  holidays.push(`${year}-12-25`); // Navidad
  // Primer lunes de febrero (Constitución movible)
  const feb1 = new Date(year, 1, 1);
  const firstMonFeb = new Date(year, 1, 1 + (8 - feb1.getDay()) % 7);
  holidays.push(firstMonFeb.toISOString().slice(0, 10));
  // Tercer lunes de marzo (Benito Juárez movible)
  const mar1 = new Date(year, 2, 1);
  const firstMonMar = new Date(year, 2, 1 + (8 - mar1.getDay()) % 7);
  const thirdMonMar = new Date(firstMonMar); thirdMonMar.setDate(firstMonMar.getDate() + 14);
  holidays.push(thirdMonMar.toISOString().slice(0, 10));
  // Tercer lunes de noviembre (Revolución movible)
  const nov1 = new Date(year, 10, 1);
  const firstMonNov = new Date(year, 10, 1 + (8 - nov1.getDay()) % 7);
  const thirdMonNov = new Date(firstMonNov); thirdMonNov.setDate(firstMonNov.getDate() + 14);
  holidays.push(thirdMonNov.toISOString().slice(0, 10));
  // Dec 1 every 6 years (transmisión de poder) - 2024, 2030...
  if (year % 6 === 0) holidays.push(`${year}-12-01`);
  return new Set(holidays);
};

const isBusinessDay = (date) => {
  const day = date.getDay();
  if (day === 0 || day === 6) return false; // Weekend
  const dateStr = date.toISOString().slice(0, 10);
  const holidays = getMexicanHolidays(date.getFullYear());
  return !holidays.has(dateStr);
};

const countBusinessHours = (startISO, endISO) => {
  if (!startISO) return null;
  const start = new Date(startISO);
  const end = endISO ? new Date(endISO) : new Date();
  if (end <= start) return null;

  // Count full business days between start and end
  let businessDays = 0;
  const cursor = new Date(start);
  cursor.setHours(0, 0, 0, 0);
  const endDay = new Date(end);
  endDay.setHours(0, 0, 0, 0);

  // If same day
  if (cursor.getTime() === endDay.getTime()) {
    if (!isBusinessDay(cursor)) return { days: 0, hours: 0 };
    const diffHrs = (end - start) / 3600000;
    return { days: 0, hours: Math.min(Math.round(diffHrs), 9) }; // cap at 9h workday
  }

  // First partial day
  cursor.setDate(cursor.getDate() + 1);

  // Full days in between
  while (cursor < endDay) {
    if (isBusinessDay(cursor)) businessDays++;
    cursor.setDate(cursor.getDate() + 1);
  }

  // Add partial start day (if business day)
  let startHours = 0;
  const startDayEnd = new Date(start);
  startDayEnd.setHours(23, 59, 59, 999);
  if (isBusinessDay(new Date(start.getFullYear(), start.getMonth(), start.getDate()))) {
    startHours = Math.min(Math.round((startDayEnd - start) / 3600000), 9);
  }

  // Add partial end day (if business day)
  let endHours = 0;
  if (isBusinessDay(endDay)) {
    const endDayStart = new Date(endDay);
    endHours = Math.min(Math.round((end - endDayStart) / 3600000), 9);
  }

  const totalHours = startHours + (businessDays * 9) + endHours; // 9h workday
  return { days: Math.floor(totalHours / 9), hours: totalHours % 9 };
};

const formatDuration = (startISO, endISO) => {
  const result = countBusinessHours(startISO, endISO);
  if (!result) return null;
  const { days, hours } = result;
  if (days > 0) return `${days}d ${hours}h`;
  if (hours > 0) return `${hours}h`;
  return "<1h";
};

const getTimeInfo = (task, board) => {
  const lastColId = board?.columns?.[board.columns.length - 1]?.id;
  const isDone = task.status === lastColId;
  const elapsed = formatDuration(task.createdAt, isDone ? task.completedAt : null);
  return { isDone, elapsed };
};

const getFileIcon = (type) => {
  if (!type) return "📄";
  if (type.startsWith("image/")) return "🖼️";
  if (type.includes("pdf")) return "📕";
  if (type.includes("word") || type.includes("document")) return "📘";
  if (type.includes("sheet") || type.includes("excel") || type.includes("csv")) return "📗";
  if (type.includes("presentation") || type.includes("powerpoint")) return "📙";
  if (type.includes("zip") || type.includes("rar") || type.includes("7z")) return "📦";
  if (type.includes("video")) return "🎬";
  if (type.includes("audio")) return "🎵";
  if (type.includes("text")) return "📝";
  return "📄";
};

const formatFileSize = (bytes) => {
  if (!bytes) return "0 B";
  if (bytes < 1024) return bytes + " B";
  if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
  return (bytes / (1024 * 1024)).toFixed(1) + " MB";
};

// ─── STORAGE HELPERS (backend-backed, replaces the old window.storage artifact API) ───
const ACCESS_CODE_KEY = "unisant-kanban-access-code";
const getAccessCode = () => { try { return localStorage.getItem(ACCESS_CODE_KEY) || ""; } catch (e) { return ""; } };
const setAccessCode = (code) => { try { localStorage.setItem(ACCESS_CODE_KEY, code); } catch (e) {} };
const clearAccessCode = () => { try { localStorage.removeItem(ACCESS_CODE_KEY); } catch (e) {} };

// Checks a code against the server. Returns true/false; never throws.
const verifyAccessCode = async (code) => {
  try {
    const res = await fetch("/api/data", { headers: { "x-kanban-access": code } });
    return res.ok || res.status === 404; // 404 = no data saved yet, but code was accepted
  } catch (e) { return false; }
};

// Returns the stored data, or null if there is none yet (first run).
// Throws { unauthorized: true } if the stored access code was rejected — callers should
// send the user back to the login screen rather than silently falling back to demo data.
const loadData = async () => {
  const code = getAccessCode();
  const res = await fetch("/api/data", { headers: { "x-kanban-access": code } });
  if (res.status === 401) { clearAccessCode(); throw { unauthorized: true }; }
  if (res.status === 404) return null;
  if (!res.ok) throw new Error("No se pudo cargar el tablero (HTTP " + res.status + ")");
  const body = await res.json();
  return body.data || null;
};
const saveData = async (data) => {
  const code = getAccessCode();
  try {
    const res = await fetch("/api/data", {
      method: "PUT",
      headers: { "Content-Type": "application/json", "x-kanban-access": code },
      body: JSON.stringify({ data }),
    });
    if (res.status === 401) clearAccessCode();
    else if (!res.ok) console.error("Save failed: HTTP " + res.status);
  } catch (e) { console.error("Storage save error:", e); }
};

const getDefaultData = () => ({
  workspaces: DEFAULT_WORKSPACES,
  members: DEFAULT_MEMBERS,
  boards: [
    {
      id: "board_1", workspaceId: "dev_tech", name: "Plataforma Educativa", flowType: "tech",
      columns: DEFAULT_COLUMNS.tech,
      tasks: [
        { id: uid(), title: "Definir reglas de negocio faltantes", description: "Documentar y validar las reglas de negocio pendientes de la plataforma educativa lanzada en septiembre.", status: "in_progress", priority: "high", assignees: ["gaby"], tags: ["platform"], dueDate: "2026-06-15", comments: [{ id: uid(), author: "gaby", text: "Necesitamos sesión con Control Escolar para mapear flujos de inscripción.", date: "2026-05-20T10:00:00" }], createdAt: "2026-05-01T09:00:00", subtasks: [{ id: uid(), title: "Mapear flujo de inscripción", done: true }, { id: uid(), title: "Validar reglas de calificaciones", done: false }, { id: uid(), title: "Documentar proceso de certificación", done: false }] },
        { id: uid(), title: "Resolver SSL sunibes.unisant.es", description: "El subdominio www. necesita resolución DCV en cPanel. Deadline julio 2026.", status: "todo", priority: "urgent", assignees: ["gaby"], tags: ["ssl"], dueDate: "2026-07-01", comments: [], createdAt: "2026-05-18T14:00:00", subtasks: [] },
        { id: uid(), title: "Evaluar Hasso para contenido interactivo", description: "Análisis de la herramienta Hasso para transformar contenido en formatos interactivos.", status: "review", priority: "medium", assignees: ["gaby"], tags: ["platform", "improvement"], dueDate: "2026-06-30", comments: [], createdAt: "2026-04-15T09:00:00", subtasks: [] },
      ],
    },
    {
      id: "board_2", workspaceId: "directivo", name: "Acuerdos Junta Directiva", flowType: "agreements",
      columns: DEFAULT_COLUMNS.agreements,
      tasks: [
        { id: uid(), title: "Protocolo validación diplomados pre-lanzamiento", description: "Ningún diplomado debe lanzarse sin validación previa del Depto. de Desarrollo Tecnológico. Derivado de incidentes con diplomados El Salvador y Ecuador.", status: "in_progress", priority: "urgent", assignees: ["gaby", "enrique"], tags: ["diplomado"], dueDate: "2026-06-01", comments: [{ id: uid(), author: "gaby", text: "Email enviado a Rectoría documentando los casos. Esperando respuesta.", date: "2026-05-19T16:00:00" }], createdAt: "2026-05-10T08:00:00", subtasks: [] },
        { id: uid(), title: "Proceso de onboarding becarios Perú", description: "Definir proceso documentado para onboarding de estudiantes becarios de Perú. Incluir registros de asistencia Zoom.", status: "registered", priority: "high", assignees: ["hernan", "gaby", "gabriela_s"], tags: ["peru", "enrollment"], dueDate: "2026-06-15", comments: [], createdAt: "2026-05-15T10:00:00", subtasks: [{ id: uid(), title: "Definir checklist de onboarding", done: false }, { id: uid(), title: "Configurar registro automático Zoom", done: false }, { id: uid(), title: "Asignar responsables por área", done: false }] },
      ],
    },
  ],
  tags: TAG_PRESETS,
  currentUser: "gaby",
  taskCreators: ["gaby"], // user IDs allowed to create tasks (besides admins)
  dashboardViewers: [], // user IDs (besides admins) who can see the general dashboard
});

// ─── MINI COMPONENTS ───
const Avatar = ({ member, size = 28 }) => (
  <div style={{ width: size, height: size, borderRadius: "50%", background: member?.color || "#94a3b8", display: "flex", alignItems: "center", justifyContent: "center", color: "#fff", fontSize: size * 0.38, fontWeight: 700, flexShrink: 0, letterSpacing: "-0.02em", border: "2px solid rgba(255,255,255,0.9)", boxShadow: "0 1px 3px rgba(0,0,0,0.15)" }}>
    {member?.avatar || "?"}
  </div>
);

const Badge = ({ children, color, bg, small }) => (
  <span style={{ display: "inline-flex", alignItems: "center", gap: 3, padding: small ? "1px 6px" : "2px 8px", borderRadius: 4, fontSize: small ? 10 : 11, fontWeight: 600, color: color || "#475569", background: bg || "#f1f5f9", whiteSpace: "nowrap" }}>
    {children}
  </span>
);

const IconBtn = ({ children, onClick, active, title, size = 32, danger }) => (
  <button onClick={onClick} title={title} style={{ width: size, height: size, borderRadius: 6, border: "none", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", background: active ? "rgba(99,102,241,0.12)" : "transparent", color: danger ? "#ef4444" : active ? "#6366f1" : "#64748b", fontSize: 15, transition: "all 0.15s" }}
    onMouseEnter={e => { if (!active) e.currentTarget.style.background = danger ? "#fef2f2" : "#f1f5f9"; }}
    onMouseLeave={e => { if (!active) e.currentTarget.style.background = "transparent"; }}>
    {children}
  </button>
);

// ─── MAIN APP ───
function KanbanApp({ onUnauthorized, onLogout }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [loadError, setLoadError] = useState(null);
  const [activeWorkspace, setActiveWorkspace] = useState(null);
  const [activeBoard, setActiveBoard] = useState(null);
  const [view, setView] = useState("board");
  const [showTaskModal, setShowTaskModal] = useState(null);
  const [showNewTask, setShowNewTask] = useState(null);
  const [showNewBoard, setShowNewBoard] = useState(false);
  const [showAdminPanel, setShowAdminPanel] = useState(false);
  const [searchQuery, setSearchQuery] = useState("");
  const [filterPriority, setFilterPriority] = useState("all");
  const [filterAssignee, setFilterAssignee] = useState("all");
  const [dragState, setDragState] = useState(null);
  const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
  const [notification, setNotification] = useState(null);
  const [showUserSwitcher, setShowUserSwitcher] = useState(false);
  const [collapsedCols, setCollapsedCols] = useState({});
  const [showTicketForm, setShowTicketForm] = useState(null); // workspace id to preview // { "backlog": true }

  useEffect(() => {
    (async () => {
      let stored;
      try {
        stored = await loadData();
      } catch (e) {
        if (e && e.unauthorized) { onUnauthorized(); return; }
        console.error(e);
        setLoadError("No se pudo conectar con el servidor. Intenta recargar la página.");
        setLoading(false);
        return;
      }
      const d = stored || getDefaultData();
      // Migrate old workspaces without memberIds
      d.workspaces = d.workspaces.map(ws => {
        if (!ws.memberIds) {
          const def = DEFAULT_WORKSPACES.find(dw => dw.id === ws.id);
          return { ...ws, memberIds: def?.memberIds || ["gaby"] };
        }
        return ws;
      });
      setData(d);
      // Set initial workspace to first accessible
      const user = d.members.find(m => m.id === d.currentUser);
      const accessible = d.workspaces.filter(ws => user?.role === "admin" || ws.memberIds?.includes(d.currentUser));
      if (accessible.length > 0) {
        setActiveWorkspace(accessible[0].id);
        const boards = d.boards.filter(b => b.workspaceId === accessible[0].id);
        if (boards.length > 0) setActiveBoard(boards[0].id);
      }
      setLoading(false);
    })();
  }, []);

  const saveTimeout = useRef(null);
  const persistData = useCallback((newData) => {
    setData(newData);
    clearTimeout(saveTimeout.current);
    saveTimeout.current = setTimeout(() => saveData(newData), 500);
  }, []);

  const notify = (msg) => { setNotification(msg); setTimeout(() => setNotification(null), 2500); };

  const currentUser = data?.members?.find(m => m.id === data?.currentUser);
  const isAdmin = currentUser?.role === "admin";
  const canCreateTasks = isAdmin || (data?.taskCreators || []).includes(data?.currentUser);
  const canViewGeneralDash = isAdmin || (data?.dashboardViewers || []).includes(data?.currentUser);

  // Accessible workspaces based on role
  const accessibleWorkspaces = useMemo(() => {
    if (!data) return [];
    if (isAdmin) return data.workspaces;
    return data.workspaces.filter(ws => ws.memberIds?.includes(data.currentUser));
  }, [data, isAdmin]);

  const currentWorkspace = data?.workspaces?.find(w => w.id === activeWorkspace);
  const workspaceBoards = data?.boards?.filter(b => b.workspaceId === activeWorkspace) || [];
  const currentBoard = data?.boards?.find(b => b.id === activeBoard);

  const filteredTasks = useMemo(() => {
    if (!currentBoard) return [];
    return currentBoard.tasks.filter(t => {
      if (searchQuery && !t.title.toLowerCase().includes(searchQuery.toLowerCase()) && !t.description?.toLowerCase().includes(searchQuery.toLowerCase())) return false;
      if (filterPriority !== "all" && t.priority !== filterPriority) return false;
      if (filterAssignee !== "all" && !t.assignees?.includes(filterAssignee)) return false;
      return true;
    });
  }, [currentBoard, searchQuery, filterPriority, filterAssignee]);

  // Collapse backlog by default when switching boards
  useEffect(() => {
    if (currentBoard) {
      const backlogCol = currentBoard.columns.find(c => c.id === "backlog");
      if (backlogCol) {
        setCollapsedCols(prev => ({ ...prev, [backlogCol.id]: prev[backlogCol.id] !== undefined ? prev[backlogCol.id] : true }));
      }
    }
  }, [activeBoard]);

  const toggleColCollapse = (colId) => setCollapsedCols(prev => ({ ...prev, [colId]: !prev[colId] }));

  // ─── DATA OPS ───
  const updateBoard = (boardId, updater) => {
    persistData({ ...data, boards: data.boards.map(b => b.id === boardId ? updater(b) : b) });
  };
  const addTask = (boardId, columnId, taskData) => {
    const newTask = { id: uid(), status: columnId, priority: "medium", assignees: [], tags: [], comments: [], subtasks: [], createdAt: new Date().toISOString(), ...taskData };
    updateBoard(boardId, b => ({ ...b, tasks: [...b.tasks, newTask] }));
    notify("✅ Tarea creada");
  };
  const updateTask = (boardId, taskId, updates) => {
    updateBoard(boardId, b => {
      const lastColId = b.columns[b.columns.length - 1]?.id;
      return { ...b, tasks: b.tasks.map(t => {
        if (t.id !== taskId) return t;
        let merged = { ...t, ...updates };
        // Auto-track completion time
        if (updates.status) {
          if (updates.status === lastColId && t.status !== lastColId) {
            merged.completedAt = new Date().toISOString();
          } else if (updates.status !== lastColId && t.status === lastColId) {
            merged.completedAt = null; // moved back out of done
          }
        }
        return merged;
      })};
    });
  };
  const deleteTask = (boardId, taskId) => {
    updateBoard(boardId, b => ({ ...b, tasks: b.tasks.filter(t => t.id !== taskId) }));
    setShowTaskModal(null);
    notify("🗑️ Tarea eliminada");
  };
  const moveTask = (boardId, taskId, newStatus) => updateTask(boardId, taskId, { status: newStatus });
  const addBoard = (boardData) => {
    const newBoard = { id: uid(), workspaceId: activeWorkspace, columns: DEFAULT_COLUMNS[boardData.flowType] || DEFAULT_COLUMNS.tech, tasks: [], ...boardData };
    persistData({ ...data, boards: [...data.boards, newBoard] });
    setActiveBoard(newBoard.id);
    setShowNewBoard(false);
    notify("📋 Tablero creado");
  };

  // ─── WORKSPACE ACCESS MANAGEMENT ───
  const updateWorkspaceMembers = (workspaceId, newMemberIds) => {
    const newData = { ...data, workspaces: data.workspaces.map(ws => ws.id === workspaceId ? { ...ws, memberIds: newMemberIds } : ws) };
    persistData(newData);
  };

  const addWorkspace = (wsData) => {
    const newWs = { id: uid(), memberIds: [data.currentUser], ...wsData };
    const newData = { ...data, workspaces: [...data.workspaces, newWs] };
    persistData(newData);
    setActiveWorkspace(newWs.id);
    setActiveBoard(null);
    notify("🏢 Espacio creado");
  };

  const deleteWorkspace = (wsId) => {
    const newData = {
      ...data,
      workspaces: data.workspaces.filter(ws => ws.id !== wsId),
      boards: data.boards.filter(b => b.workspaceId !== wsId),
    };
    persistData(newData);
    if (activeWorkspace === wsId) {
      const remaining = newData.workspaces;
      setActiveWorkspace(remaining[0]?.id || null);
      const boards = remaining[0] ? newData.boards.filter(b => b.workspaceId === remaining[0].id) : [];
      setActiveBoard(boards[0]?.id || null);
    }
    notify("🗑️ Espacio eliminado");
  };

  const updateWorkspace = (wsId, updates) => {
    persistData({ ...data, workspaces: data.workspaces.map(ws => ws.id === wsId ? { ...ws, ...updates } : ws) });
  };

  const updateTicketConfig = (wsId, config) => {
    persistData({ ...data, workspaces: data.workspaces.map(ws => ws.id === wsId ? { ...ws, ...config } : ws) });
    notify("🎫 Configuración de tickets actualizada");
  };

  const updateTaskCreators = (newCreators) => {
    persistData({ ...data, taskCreators: newCreators });
    notify("📝 Permisos actualizados");
  };

  const updateDashboardViewers = (newViewers) => {
    persistData({ ...data, dashboardViewers: newViewers });
    notify("📊 Acceso a dashboard actualizado");
  };

  const addMember = (memberData) => {
    const initials = memberData.name.split(" ").map(w => w[0]).join("").toUpperCase().slice(0, 2);
    const newMember = { id: uid(), avatar: initials, role: "member", ...memberData };
    const newData = { ...data, members: [...data.members, newMember] };
    // Add to selected workspaces
    if (memberData.workspaceIds?.length > 0) {
      newData.workspaces = newData.workspaces.map(ws =>
        memberData.workspaceIds.includes(ws.id) ? { ...ws, memberIds: [...(ws.memberIds || []), newMember.id] } : ws
      );
    }
    persistData(newData);
    notify("👤 Usuario creado");
  };

  const deleteMember = (memberId) => {
    if (memberId === data.currentUser) return;
    const newData = {
      ...data,
      members: data.members.filter(m => m.id !== memberId),
      workspaces: data.workspaces.map(ws => ({ ...ws, memberIds: (ws.memberIds || []).filter(id => id !== memberId) })),
      taskCreators: (data.taskCreators || []).filter(id => id !== memberId),
      dashboardViewers: (data.dashboardViewers || []).filter(id => id !== memberId),
    };
    persistData(newData);
    notify("🗑️ Usuario eliminado");
  };

  const updateMember = (memberId, updates) => {
    persistData({ ...data, members: data.members.map(m => m.id === memberId ? { ...m, ...updates } : m) });
  };

  const switchUser = (userId) => {
    const newData = { ...data, currentUser: userId };
    persistData(newData);
    setShowUserSwitcher(false);
    // Reset to first accessible workspace for this user
    const user = data.members.find(m => m.id === userId);
    const accessible = data.workspaces.filter(ws => user?.role === "admin" || ws.memberIds?.includes(userId));
    if (accessible.length > 0) {
      setActiveWorkspace(accessible[0].id);
      const boards = data.boards.filter(b => b.workspaceId === accessible[0].id);
      setActiveBoard(boards.length > 0 ? boards[0].id : null);
    } else {
      setActiveWorkspace(null);
      setActiveBoard(null);
    }
    notify(`👤 Sesión: ${user?.name || userId}`);
  };

  // ─── DRAG & DROP ───
  const handleDragStart = (e, taskId) => { setDragState({ taskId }); e.dataTransfer.effectAllowed = "move"; e.dataTransfer.setData("text/plain", taskId); e.currentTarget.style.opacity = "0.5"; };
  const handleDragEnd = (e) => { e.currentTarget.style.opacity = "1"; setDragState(null); };
  const handleDragOver = (e) => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; };
  const handleDrop = (e, columnId) => { e.preventDefault(); const taskId = e.dataTransfer.getData("text/plain"); if (taskId && currentBoard) moveTask(currentBoard.id, taskId, columnId); setDragState(null); };

  const getStats = () => {
    if (!currentBoard) return {};
    const tasks = currentBoard.tasks;
    const total = tasks.length;
    const overdue = tasks.filter(t => t.dueDate && new Date(t.dueDate) < new Date() && t.status !== currentBoard.columns[currentBoard.columns.length - 1]?.id).length;
    return { total, overdue };
  };

  if (loadError) {
    return (
      <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100vh", background: "#0f172a", color: "#e2e8f0", fontFamily: "'DM Sans', sans-serif" }}>
        <div style={{ textAlign: "center", maxWidth: 320 }}>
          <div style={{ fontSize: 40, marginBottom: 16 }}>⚠️</div>
          <div style={{ fontSize: 15, fontWeight: 600, marginBottom: 16 }}>{loadError}</div>
          <button onClick={() => window.location.reload()} style={{ padding: "10px 20px", borderRadius: 8, border: "none", background: "#6366f1", color: "#fff", fontSize: 13, fontWeight: 600, cursor: "pointer" }}>Recargar</button>
        </div>
      </div>
    );
  }

  if (loading || !data) {
    return (
      <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100vh", background: "#0f172a", color: "#e2e8f0", fontFamily: "'DM Sans', sans-serif" }}>
        <div style={{ textAlign: "center" }}>
          <div style={{ fontSize: 40, marginBottom: 16, animation: "pulse 1.5s infinite" }}>⚡</div>
          <div style={{ fontSize: 18, fontWeight: 600 }}>Cargando UNISANT Kanban...</div>
        </div>
      </div>
    );
  }

  const stats = getStats();

  return (
    <div style={{ display: "flex", height: "100vh", fontFamily: "'DM Sans', sans-serif", background: "#0f172a", color: "#e2e8f0", overflow: "hidden", position: "relative" }}>
      <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
      <style>{`
        * { box-sizing: border-box; margin: 0; padding: 0; }
        ::-webkit-scrollbar { width: 6px; height: 6px; }
        ::-webkit-scrollbar-track { background: transparent; }
        ::-webkit-scrollbar-thumb { background: #334155; border-radius: 3px; }
        @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }
        @keyframes slideIn { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
        @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
        @keyframes notifyIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
        input, textarea, select { font-family: inherit; }
      `}</style>

      {/* NOTIFICATION */}
      {notification && (
        <div style={{ position: "fixed", top: 20, right: 20, zIndex: 9999, background: "#1e293b", border: "1px solid #334155", borderRadius: 10, padding: "12px 20px", fontSize: 14, fontWeight: 600, color: "#e2e8f0", boxShadow: "0 10px 40px rgba(0,0,0,0.4)", animation: "notifyIn 0.3s ease" }}>
          {notification}
        </div>
      )}

      {/* ─── SIDEBAR ─── */}
      <div style={{ width: sidebarCollapsed ? 60 : 260, flexShrink: 0, background: "#1e293b", borderRight: "1px solid #334155", display: "flex", flexDirection: "column", transition: "width 0.2s ease", overflow: "hidden" }}>
        {/* Logo */}
        <div style={{ padding: sidebarCollapsed ? "16px 12px" : "20px 20px", borderBottom: "1px solid #334155", display: "flex", alignItems: "center", gap: 10, cursor: "pointer" }} onClick={() => setSidebarCollapsed(!sidebarCollapsed)}>
          <div style={{ width: 34, height: 34, borderRadius: 8, background: "linear-gradient(135deg, #6366f1, #8b5cf6)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 18, fontWeight: 700, color: "#fff", flexShrink: 0 }}>U</div>
          {!sidebarCollapsed && (
            <div>
              <div style={{ fontSize: 15, fontWeight: 700, color: "#f8fafc", letterSpacing: "-0.02em" }}>UNISANT</div>
              <div style={{ fontSize: 10, color: "#64748b", fontWeight: 500 }}>Kanban System</div>
            </div>
          )}
        </div>

        {/* Workspaces - filtered by access */}
        <div style={{ flex: 1, overflowY: "auto", padding: sidebarCollapsed ? "8px 6px" : "12px" }}>
          {/* Dashboard link */}
          <div
            onClick={() => setView("dashboard")}
            style={{
              display: "flex", alignItems: "center", gap: 10,
              padding: sidebarCollapsed ? "10px 0" : "9px 10px",
              borderRadius: 8, cursor: "pointer", marginBottom: 8,
              background: view === "dashboard" ? "rgba(99,102,241,0.15)" : "transparent",
              color: view === "dashboard" ? "#a5b4fc" : "#94a3b8",
              justifyContent: sidebarCollapsed ? "center" : "flex-start",
              transition: "all 0.15s",
              borderBottom: "1px solid #334155",
              paddingBottom: 12,
            }}
            onMouseEnter={e => { if (view !== "dashboard") e.currentTarget.style.background = "rgba(255,255,255,0.04)"; }}
            onMouseLeave={e => { if (view !== "dashboard") e.currentTarget.style.background = "transparent"; }}
          >
            <span style={{ fontSize: sidebarCollapsed ? 20 : 16, flexShrink: 0 }}>📊</span>
            {!sidebarCollapsed && <span style={{ fontSize: 13, fontWeight: 600 }}>Mi Dashboard</span>}
          </div>

          {/* General Dashboard link - permission gated */}
          {canViewGeneralDash && (
            <div
              onClick={() => setView("general_dashboard")}
              style={{
                display: "flex", alignItems: "center", gap: 10,
                padding: sidebarCollapsed ? "10px 0" : "9px 10px",
                borderRadius: 8, cursor: "pointer", marginBottom: 8,
                background: view === "general_dashboard" ? "rgba(14,165,233,0.15)" : "transparent",
                color: view === "general_dashboard" ? "#7dd3fc" : "#94a3b8",
                justifyContent: sidebarCollapsed ? "center" : "flex-start",
                transition: "all 0.15s",
                borderBottom: "1px solid #334155",
                paddingBottom: 12,
              }}
              onMouseEnter={e => { if (view !== "general_dashboard") e.currentTarget.style.background = "rgba(255,255,255,0.04)"; }}
              onMouseLeave={e => { if (view !== "general_dashboard") e.currentTarget.style.background = "transparent"; }}
            >
              <span style={{ fontSize: sidebarCollapsed ? 20 : 16, flexShrink: 0 }}>👥</span>
              {!sidebarCollapsed && <span style={{ fontSize: 13, fontWeight: 600 }}>Dashboard Equipo</span>}
            </div>
          )}

          {!sidebarCollapsed && (
            <div style={{ fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", letterSpacing: "0.08em", padding: "8px 8px 6px", marginBottom: 2, display: "flex", justifyContent: "space-between", alignItems: "center" }}>
              <span>Espacios de trabajo</span>
              {isAdmin && (
                <span onClick={() => setShowAdminPanel(true)} style={{ cursor: "pointer", fontSize: 14, color: "#6366f1" }} title="Gestionar accesos">⚙️</span>
              )}
            </div>
          )}
          {accessibleWorkspaces.map(ws => (
            <div key={ws.id}>
              <div
                onClick={() => { setView("board"); setActiveWorkspace(ws.id); const b = data.boards.filter(b => b.workspaceId === ws.id); if (b.length) setActiveBoard(b[0].id); else setActiveBoard(null); }}
                style={{
                  display: "flex", alignItems: "center", gap: 10,
                  padding: sidebarCollapsed ? "10px 0" : "8px 10px",
                  borderRadius: 8, cursor: "pointer", marginBottom: 2,
                  background: activeWorkspace === ws.id ? "rgba(99,102,241,0.15)" : "transparent",
                  color: activeWorkspace === ws.id ? "#a5b4fc" : "#94a3b8",
                  justifyContent: sidebarCollapsed ? "center" : "flex-start",
                  transition: "all 0.15s",
                }}
                onMouseEnter={e => { if (activeWorkspace !== ws.id) e.currentTarget.style.background = "rgba(255,255,255,0.04)"; }}
                onMouseLeave={e => { if (activeWorkspace !== ws.id) e.currentTarget.style.background = "transparent"; }}
              >
                <span style={{ fontSize: sidebarCollapsed ? 20 : 16, flexShrink: 0 }}>{ws.icon}</span>
                {!sidebarCollapsed && (
                  <div style={{ flex: 1, overflow: "hidden" }}>
                    <span style={{ fontSize: 13, fontWeight: 500, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", display: "block" }}>{ws.name}</span>
                    <span style={{ fontSize: 10, color: "#475569" }}>{ws.memberIds?.length || 0} miembros</span>
                  </div>
                )}
              </div>
              {/* Boards under workspace */}
              {!sidebarCollapsed && activeWorkspace === ws.id && (
                <div style={{ paddingLeft: 20, marginBottom: 6 }}>
                  {workspaceBoards.map(b => (
                    <div key={b.id} onClick={() => setActiveBoard(b.id)} style={{
                      padding: "6px 10px", borderRadius: 6, cursor: "pointer",
                      fontSize: 12, fontWeight: activeBoard === b.id ? 600 : 400,
                      color: activeBoard === b.id ? "#e2e8f0" : "#64748b",
                      background: activeBoard === b.id ? "rgba(99,102,241,0.1)" : "transparent",
                      display: "flex", alignItems: "center", gap: 6, marginBottom: 1,
                    }}>
                      <span style={{ fontSize: 12 }}>{FLOW_TYPES[b.flowType]?.icon || "📋"}</span>
                      <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{b.name}</span>
                    </div>
                  ))}
                  <div onClick={() => setShowNewBoard(true)} style={{ padding: "6px 10px", borderRadius: 6, cursor: "pointer", fontSize: 12, color: "#6366f1", fontWeight: 500, display: "flex", alignItems: "center", gap: 6 }}
                    onMouseEnter={e => e.currentTarget.style.background = "rgba(99,102,241,0.08)"}
                    onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
                    <span>+</span> Nuevo tablero
                  </div>
                </div>
              )}
            </div>
          ))}
          {accessibleWorkspaces.length === 0 && !sidebarCollapsed && (
            <div style={{ padding: "20px 10px", textAlign: "center", color: "#475569", fontSize: 12 }}>
              No tienes acceso a ningún espacio.
              <br />Contacta al administrador.
            </div>
          )}
        </div>

        {/* User footer with switcher */}
        {!sidebarCollapsed && currentUser && (
          <div style={{ borderTop: "1px solid #334155" }}>
            <div onClick={() => setShowUserSwitcher(!showUserSwitcher)} style={{ padding: "12px 16px", display: "flex", alignItems: "center", gap: 10, cursor: "pointer" }}
              onMouseEnter={e => e.currentTarget.style.background = "rgba(255,255,255,0.04)"}
              onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
              <Avatar member={currentUser} size={30} />
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 12, fontWeight: 600, color: "#e2e8f0" }}>{currentUser.name}</div>
                <div style={{ fontSize: 10, color: isAdmin ? "#6366f1" : "#64748b", textTransform: "uppercase", fontWeight: 700 }}>{currentUser.role === "admin" ? "⭐ Admin" : "Miembro"}</div>
              </div>
              <span style={{ fontSize: 10, color: "#64748b", transition: "transform 0.2s", transform: showUserSwitcher ? "rotate(180deg)" : "none" }}>▲</span>
            </div>
            {/* User switcher dropdown */}
            {showUserSwitcher && (
              <div style={{ padding: "4px 8px 8px", borderTop: "1px solid #334155", background: "#0f172a" }}>
                <div style={{ fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", padding: "6px 8px 4px", letterSpacing: "0.05em" }}>Cambiar usuario (demo)</div>
                {data.members.map(m => (
                  <div key={m.id} onClick={() => switchUser(m.id)} style={{
                    display: "flex", alignItems: "center", gap: 8,
                    padding: "7px 8px", borderRadius: 6, cursor: "pointer",
                    background: data.currentUser === m.id ? "rgba(99,102,241,0.15)" : "transparent",
                    marginBottom: 1,
                  }}
                    onMouseEnter={e => { if (data.currentUser !== m.id) e.currentTarget.style.background = "rgba(255,255,255,0.04)"; }}
                    onMouseLeave={e => { if (data.currentUser !== m.id) e.currentTarget.style.background = "transparent"; }}>
                    <Avatar member={m} size={22} />
                    <span style={{ fontSize: 11, color: data.currentUser === m.id ? "#e2e8f0" : "#94a3b8", fontWeight: data.currentUser === m.id ? 600 : 400 }}>{m.name}</span>
                    {m.role === "admin" && <span style={{ fontSize: 9, color: "#6366f1" }}>⭐</span>}
                  </div>
                ))}
                {onLogout && (
                  <div onClick={onLogout} style={{
                    display: "flex", alignItems: "center", gap: 8,
                    padding: "7px 8px", borderRadius: 6, cursor: "pointer",
                    marginTop: 4, borderTop: "1px solid #334155", paddingTop: 10,
                    color: "#ef4444", fontSize: 11, fontWeight: 600,
                  }}
                    onMouseEnter={e => e.currentTarget.style.background = "rgba(239,68,68,0.08)"}
                    onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
                    🔒 Cerrar sesión
                  </div>
                )}
              </div>
            )}
          </div>
        )}
      </div>

      {/* ─── MAIN CONTENT ─── */}
      <div style={{ flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }}>
        {view === "dashboard" ? (
          <DashboardView data={data} accessibleWorkspaces={accessibleWorkspaces} onTaskClick={t => {
            const board = data.boards.find(b => b.tasks.some(bt => bt.id === t.id));
            if (board) { setActiveWorkspace(board.workspaceId); setActiveBoard(board.id); }
            setShowTaskModal(t);
          }} />
        ) : view === "general_dashboard" ? (
          <GeneralDashboardView data={data} onTaskClick={t => {
            const board = data.boards.find(b => b.tasks.some(bt => bt.id === t.id));
            if (board) { setActiveWorkspace(board.workspaceId); setActiveBoard(board.id); }
            setShowTaskModal(t);
          }} />
        ) : (
        <>
        {/* Header */}
        <div style={{ padding: "14px 24px", borderBottom: "1px solid #334155", display: "flex", alignItems: "center", justifyContent: "space-between", background: "#1e293b", flexShrink: 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
            {currentWorkspace && (
              <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                <span style={{ fontSize: 20 }}>{currentWorkspace.icon}</span>
                <span style={{ fontSize: 16, fontWeight: 700, color: "#f8fafc" }}>{currentBoard?.name || currentWorkspace.name}</span>
                {currentBoard && (
                  <Badge color={FLOW_TYPES[currentBoard.flowType]?.color} bg={FLOW_TYPES[currentBoard.flowType]?.color + "18"}>
                    {FLOW_TYPES[currentBoard.flowType]?.icon} {FLOW_TYPES[currentBoard.flowType]?.label}
                  </Badge>
                )}
              </div>
            )}
            {!currentWorkspace && <span style={{ fontSize: 16, fontWeight: 700, color: "#475569" }}>Sin acceso a espacios</span>}
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
            {currentBoard && (
              <div style={{ display: "flex", gap: 12, marginRight: 12 }}>
                <span style={{ fontSize: 12, color: "#94a3b8" }}><strong style={{ color: "#e2e8f0" }}>{stats.total}</strong> tareas</span>
                {stats.overdue > 0 && <span style={{ fontSize: 12, color: "#ef4444" }}><strong>{stats.overdue}</strong> vencidas</span>}
              </div>
            )}
            <div style={{ position: "relative" }}>
              <input type="text" placeholder="Buscar tareas..." value={searchQuery} onChange={e => setSearchQuery(e.target.value)} style={{ width: 200, padding: "7px 12px 7px 32px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 13, outline: "none" }} />
              <span style={{ position: "absolute", left: 10, top: "50%", transform: "translateY(-50%)", fontSize: 13, color: "#64748b" }}>🔍</span>
            </div>
            <select value={filterPriority} onChange={e => setFilterPriority(e.target.value)} style={{ padding: "7px 10px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 12, cursor: "pointer", outline: "none" }}>
              <option value="all">Prioridad</option>
              {Object.entries(PRIORITY_CONFIG).map(([k, v]) => <option key={k} value={k}>{v.icon} {v.label}</option>)}
            </select>
            <select value={filterAssignee} onChange={e => setFilterAssignee(e.target.value)} style={{ padding: "7px 10px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 12, cursor: "pointer", outline: "none" }}>
              <option value="all">Asignado</option>
              {data.members.map(m => <option key={m.id} value={m.id}>{m.name}</option>)}
            </select>
            <div style={{ display: "flex", background: "#0f172a", borderRadius: 8, border: "1px solid #334155", overflow: "hidden" }}>
              {[{ key: "board", icon: "▦", label: "Tablero" }, { key: "list", icon: "☰", label: "Lista" }, { key: "gantt", icon: "◫", label: "Gantt" }].map(v => (
                <button key={v.key} onClick={() => setView(v.key)} title={v.label} style={{ padding: "7px 12px", border: "none", cursor: "pointer", background: view === v.key ? "#6366f1" : "transparent", color: view === v.key ? "#fff" : "#64748b", fontSize: 14, transition: "all 0.15s" }}>{v.icon}</button>
              ))}
            </div>
          </div>
        </div>

        {/* Board Content */}
        <div style={{ flex: 1, overflow: "auto", padding: view === "board" ? "20px 16px" : "20px 24px" }}>
          {!currentBoard ? (
            <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100%", flexDirection: "column", gap: 16 }}>
              <div style={{ fontSize: 48, opacity: 0.3 }}>📋</div>
              <div style={{ fontSize: 16, color: "#64748b" }}>{accessibleWorkspaces.length === 0 ? "No tienes acceso a ningún espacio de trabajo" : "Selecciona o crea un tablero para empezar"}</div>
              {activeWorkspace && (
                <button onClick={() => setShowNewBoard(true)} style={{ padding: "10px 24px", borderRadius: 8, border: "none", background: "#6366f1", color: "#fff", fontSize: 14, fontWeight: 600, cursor: "pointer" }}>+ Nuevo tablero</button>
              )}
            </div>
          ) : view === "board" ? (
            <div style={{ display: "flex", gap: 14, height: "100%", minWidth: "max-content" }}>
              {currentBoard.columns.map(col => {
                const colTasks = filteredTasks.filter(t => t.status === col.id);
                const isCollapsed = collapsedCols[col.id];
                if (isCollapsed) {
                  return (
                    <div key={col.id}
                      onDragOver={handleDragOver} onDrop={e => handleDrop(e, col.id)}
                      onClick={() => toggleColCollapse(col.id)}
                      style={{
                        width: 48, flexShrink: 0, display: "flex", flexDirection: "column",
                        alignItems: "center", background: "#1e293b", borderRadius: 12,
                        border: "1px solid #334155", cursor: "pointer", padding: "14px 0",
                        transition: "all 0.2s",
                      }}
                      onMouseEnter={e => e.currentTarget.style.borderColor = col.color}
                      onMouseLeave={e => e.currentTarget.style.borderColor = "#334155"}>
                      <div style={{ width: 10, height: 10, borderRadius: 3, background: col.color, marginBottom: 8 }} />
                      <span style={{
                        writingMode: "vertical-rl", textOrientation: "mixed",
                        fontSize: 12, fontWeight: 700, color: "#94a3b8",
                        textTransform: "uppercase", letterSpacing: "0.05em",
                      }}>{col.title}</span>
                      <span style={{
                        fontSize: 11, fontWeight: 700, color: "#64748b",
                        background: "#0f172a", borderRadius: 10, padding: "2px 6px",
                        marginTop: 8,
                      }}>{colTasks.length}</span>
                    </div>
                  );
                }
                return (
                  <div key={col.id} onDragOver={handleDragOver} onDrop={e => handleDrop(e, col.id)} style={{ width: 290, flexShrink: 0, display: "flex", flexDirection: "column", background: "#1e293b", borderRadius: 12, border: "1px solid #334155" }}>
                    <div style={{ padding: "14px 14px 10px", display: "flex", alignItems: "center", justifyContent: "space-between", borderBottom: `2px solid ${col.color}` }}>
                      <div style={{ display: "flex", alignItems: "center", gap: 8, cursor: "pointer" }} onClick={() => toggleColCollapse(col.id)}>
                        <span style={{ fontSize: 10, color: "#475569", transition: "transform 0.2s" }}>◀</span>
                        <div style={{ width: 10, height: 10, borderRadius: 3, background: col.color }} />
                        <span style={{ fontSize: 13, fontWeight: 700, color: "#f8fafc", textTransform: "uppercase", letterSpacing: "0.03em" }}>{col.title}</span>
                        <span style={{ fontSize: 11, fontWeight: 700, color: "#64748b", background: "#0f172a", borderRadius: 10, padding: "1px 7px" }}>{colTasks.length}</span>
                      </div>
                      {canCreateTasks && <IconBtn onClick={() => setShowNewTask(col.id)} title="Agregar tarea" size={26}>+</IconBtn>}
                    </div>
                    <div style={{ flex: 1, overflowY: "auto", padding: "8px" }}>
                      {colTasks.map(task => (
                        <TaskCard key={task.id} task={task} members={data.members} tags={data.tags} board={currentBoard} onDragStart={handleDragStart} onDragEnd={handleDragEnd} onClick={() => setShowTaskModal(task)} />
                      ))}
                      {colTasks.length === 0 && (
                        <div style={{ padding: "24px 12px", textAlign: "center", color: "#475569", fontSize: 12, fontStyle: "italic", border: "2px dashed #334155", borderRadius: 8, margin: "4px 0" }}>Arrastra tareas aquí</div>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>
          ) : view === "list" ? (
            <ListView tasks={filteredTasks} board={currentBoard} data={data} onTaskClick={t => setShowTaskModal(t)} />
          ) : (
            <GanttView tasks={filteredTasks} board={currentBoard} data={data} onTaskClick={t => setShowTaskModal(t)} />
          )}
        </div>
        </>
        )}
      </div>

      {/* ─── MODALS ─── */}
      {showTaskModal && (() => {
        const taskBoard = data.boards.find(b => b.tasks.some(t => t.id === showTaskModal.id)) || currentBoard;
        return (
        <TaskDetailModal task={showTaskModal} board={taskBoard} data={data}
          onClose={() => setShowTaskModal(null)}
          onUpdate={(updates) => { updateTask(taskBoard.id, showTaskModal.id, updates); setShowTaskModal({ ...showTaskModal, ...updates }); }}
          onDelete={() => deleteTask(taskBoard.id, showTaskModal.id)}
          onAddComment={(text) => {
            const comment = { id: uid(), author: data.currentUser, text, date: new Date().toISOString() };
            const newComments = [...(showTaskModal.comments || []), comment];
            updateTask(taskBoard.id, showTaskModal.id, { comments: newComments });
            setShowTaskModal({ ...showTaskModal, comments: newComments });
          }}
        />
        );
      })()}
      {showNewTask && currentBoard && (
        <NewTaskModal columnId={showNewTask} board={currentBoard} data={data}
          onClose={() => setShowNewTask(null)}
          onSave={(taskData) => { addTask(currentBoard.id, showNewTask, taskData); setShowNewTask(null); }}
        />
      )}
      {showNewBoard && <NewBoardModal onClose={() => setShowNewBoard(false)} onSave={addBoard} />}
      {showAdminPanel && isAdmin && (
        <AdminPanel data={data} onClose={() => setShowAdminPanel(false)} onUpdateWorkspaceMembers={updateWorkspaceMembers} onAddWorkspace={addWorkspace} onDeleteWorkspace={deleteWorkspace} onUpdateWorkspace={updateWorkspace} onUpdateTaskCreators={updateTaskCreators} onUpdateDashboardViewers={updateDashboardViewers} onAddMember={addMember} onDeleteMember={deleteMember} onUpdateMember={updateMember} onUpdateTicketConfig={updateTicketConfig} onPreviewTicketForm={(wsId) => { setShowAdminPanel(false); setShowTicketForm(wsId); }} />
      )}
      {showTicketForm && <TicketFormPreview wsId={showTicketForm} data={data} onClose={() => setShowTicketForm(null)} />}
    </div>
  );
}

// ─── ADMIN PANEL ───
function AdminPanel({ data, onClose, onUpdateWorkspaceMembers, onAddWorkspace, onDeleteWorkspace, onUpdateWorkspace, onUpdateTaskCreators, onUpdateDashboardViewers, onAddMember, onDeleteMember, onUpdateMember, onUpdateTicketConfig, onPreviewTicketForm }) {
  const [tab, setTab] = useState("spaces"); // spaces | users | perms | tickets
  const [selectedWs, setSelectedWs] = useState(data.workspaces[0]?.id);
  const [showNewWs, setShowNewWs] = useState(false);
  const [newWsName, setNewWsName] = useState("");
  const [newWsIcon, setNewWsIcon] = useState("📁");
  const [newWsColor, setNewWsColor] = useState("#6366f1");
  const [editingWs, setEditingWs] = useState(null);
  const [deleteConfirm, setDeleteConfirm] = useState(null);
  // User creation
  const [showNewUser, setShowNewUser] = useState(false);
  const [nuName, setNuName] = useState("");
  const [nuEmail, setNuEmail] = useState("");
  const [nuRole, setNuRole] = useState("member");
  const [nuColor, setNuColor] = useState("#6366f1");
  const [nuWorkspaces, setNuWorkspaces] = useState([]);
  const [deleteUserConfirm, setDeleteUserConfirm] = useState(null);
  // Ticket config
  const [tSelWs, setTSelWs] = useState(null);
  const [tEnabled, setTEnabled] = useState(false);
  const [tSlug, setTSlug] = useState("");
  const [tCats, setTCats] = useState("");
  const [tMode, setTMode] = useState("rotation");
  const [tBoardId, setTBoardId] = useState("");
  const [tColId, setTColId] = useState("");
  const [newCat, setNewCat] = useState("");
  const [tAssigneeId, setTAssigneeId] = useState("");
  const [tSedes, setTSedes] = useState("");

  const ws = data.workspaces.find(w => w.id === selectedWs);
  const ICON_OPTIONS = ["📁", "⚡", "🏛️", "📚", "🤝", "🌎", "💰", "🎯", "🚀", "💡", "📊", "🔧", "🎓", "📱", "🖥️", "📝", "🏗️", "🔬"];
  const COLOR_OPTIONS = ["#6366f1", "#0891b2", "#059669", "#d97706", "#7c3aed", "#dc2626", "#ec4899", "#475569", "#f97316", "#14b8a6"];

  const handleCreateWs = () => {
    if (!newWsName.trim()) return;
    onAddWorkspace({ name: newWsName.trim(), icon: newWsIcon, color: newWsColor });
    setNewWsName(""); setNewWsIcon("📁"); setNewWsColor("#6366f1"); setShowNewWs(false);
  };

  const handleCreateUser = () => {
    if (!nuName.trim()) return;
    onAddMember({ name: nuName.trim(), email: nuEmail.trim(), role: nuRole, color: nuColor, workspaceIds: nuWorkspaces });
    setNuName(""); setNuEmail(""); setNuRole("member"); setNuColor("#6366f1"); setNuWorkspaces([]); setShowNewUser(false);
  };

  const selectTicketWs = (wsId) => {
    setTSelWs(wsId);
    const w = data.workspaces.find(ws => ws.id === wsId);
    if (w) { setTEnabled(w.ticketEnabled || false); setTSlug(w.ticketSlug || ""); setTCats((w.ticketCategories || []).join(", ")); setTMode(w.ticketAssignMode || "rotation"); setTBoardId(w.ticketBoardId || ""); setTColId(w.ticketColumnId || ""); setTAssigneeId(w.ticketAssigneeId || ""); setTSedes((w.ticketSedes || []).join(", ")); }
  };

  const saveTicketConfig = () => {
    if (!tSelWs) return;
    onUpdateTicketConfig(tSelWs, { ticketEnabled: tEnabled, ticketSlug: tSlug.trim() || null, ticketCategories: tCats.split(",").map(s => s.trim()).filter(Boolean), ticketAssignMode: tMode, ticketBoardId: tBoardId || null, ticketColumnId: tColId || null, ticketAssigneeId: tMode === "manual" ? (tAssigneeId || null) : null, ticketSedes: tSedes.split(",").map(s => s.trim()).filter(Boolean) });
  };

  const tWsBoards = tSelWs ? data.boards.filter(b => b.workspaceId === tSelWs) : [];
  const tSelBoardCols = tBoardId ? data.boards.find(b => b.id === tBoardId)?.columns || [] : [];

  const TABS = [
    { key: "spaces", label: "Espacios", icon: "🏢" },
    { key: "users", label: "Usuarios", icon: "👤" },
    { key: "perms", label: "Permisos", icon: "🔑" },
    { key: "tickets", label: "Tickets", icon: "🎫" },
  ];

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.6)", zIndex: 1000, display: "flex", alignItems: "center", justifyContent: "center", animation: "fadeIn 0.15s ease", backdropFilter: "blur(4px)" }}>
      <div onClick={e => e.stopPropagation()} style={{ width: "90%", maxWidth: 680, maxHeight: "88vh", overflowY: "auto", background: "#1e293b", borderRadius: 16, border: "1px solid #334155", boxShadow: "0 25px 60px rgba(0,0,0,0.5)", animation: "slideIn 0.2s ease" }}>
        {/* Header with tabs */}
        <div style={{ padding: "16px 24px 0", borderBottom: "1px solid #334155" }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}>
            <h2 style={{ fontSize: 18, fontWeight: 700, color: "#f8fafc", margin: 0 }}>⚙️ Administración</h2>
            <IconBtn onClick={onClose}>✕</IconBtn>
          </div>
          <div style={{ display: "flex", gap: 4 }}>
            {TABS.map(t => (
              <button key={t.key} onClick={() => setTab(t.key)} style={{
                padding: "8px 16px", borderRadius: "8px 8px 0 0", border: "none", cursor: "pointer",
                fontSize: 12, fontWeight: 600, transition: "all 0.15s",
                background: tab === t.key ? "#0f172a" : "transparent",
                color: tab === t.key ? "#e2e8f0" : "#64748b",
                borderBottom: tab === t.key ? "2px solid #6366f1" : "2px solid transparent",
              }}>{t.icon} {t.label}</button>
            ))}
          </div>
        </div>

        <div style={{ padding: "16px 24px 24px" }}>
          {/* ─── TAB: SPACES ─── */}
          {tab === "spaces" && (
            <div>
              <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 20, alignItems: "center" }}>
                {data.workspaces.map(w => (
                  <button key={w.id} onClick={() => { setSelectedWs(w.id); setShowNewWs(false); }} style={{
                    padding: "8px 14px", borderRadius: 8, border: `2px solid ${selectedWs === w.id && !showNewWs ? w.color : "#334155"}`,
                    background: selectedWs === w.id && !showNewWs ? w.color + "18" : "#0f172a",
                    color: selectedWs === w.id && !showNewWs ? "#f8fafc" : "#94a3b8",
                    fontSize: 12, fontWeight: 600, cursor: "pointer", display: "flex", alignItems: "center", gap: 6,
                  }}>{w.icon} {w.name}</button>
                ))}
                <button onClick={() => setShowNewWs(true)} style={{
                  padding: "8px 14px", borderRadius: 8, border: `2px dashed ${showNewWs ? "#6366f1" : "#475569"}`,
                  background: showNewWs ? "#6366f115" : "transparent", color: showNewWs ? "#a5b4fc" : "#64748b",
                  fontSize: 12, fontWeight: 600, cursor: "pointer", display: "flex", alignItems: "center", gap: 4,
                }}>+ Nuevo espacio</button>
              </div>

              {showNewWs && (
                <div style={{ marginBottom: 20, padding: "16px", borderRadius: 12, background: "#0f172a", border: "1px solid #334155" }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: "#e2e8f0", marginBottom: 12 }}>Crear nuevo espacio</div>
                  <input value={newWsName} onChange={e => setNewWsName(e.target.value)} placeholder="Nombre del espacio" autoFocus onKeyDown={e => { if (e.key === "Enter") handleCreateWs(); }}
                    style={{ width: "100%", padding: "10px 12px", borderRadius: 8, border: "1px solid #334155", background: "#1e293b", color: "#f8fafc", fontSize: 14, fontWeight: 600, outline: "none", marginBottom: 12 }} />
                  <div style={{ marginBottom: 10 }}>
                    <label style={{ fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Ícono</label>
                    <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                      {ICON_OPTIONS.map(icon => (
                        <span key={icon} onClick={() => setNewWsIcon(icon)} style={{ width: 34, height: 34, borderRadius: 8, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 18, cursor: "pointer", background: newWsIcon === icon ? "#6366f125" : "#1e293b", border: `2px solid ${newWsIcon === icon ? "#6366f1" : "transparent"}` }}>{icon}</span>
                      ))}
                    </div>
                  </div>
                  <div style={{ marginBottom: 14 }}>
                    <label style={{ fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Color</label>
                    <div style={{ display: "flex", gap: 6 }}>
                      {COLOR_OPTIONS.map(c => (
                        <span key={c} onClick={() => setNewWsColor(c)} style={{ width: 28, height: 28, borderRadius: 6, background: c, cursor: "pointer", border: `3px solid ${newWsColor === c ? "#fff" : "transparent"}` }} />
                      ))}
                    </div>
                  </div>
                  <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
                    <button onClick={() => setShowNewWs(false)} style={{ padding: "8px 16px", borderRadius: 8, border: "1px solid #334155", background: "transparent", color: "#94a3b8", fontSize: 12, cursor: "pointer" }}>Cancelar</button>
                    <button onClick={handleCreateWs} disabled={!newWsName.trim()} style={{ padding: "8px 20px", borderRadius: 8, border: "none", background: newWsName.trim() ? "#6366f1" : "#334155", color: newWsName.trim() ? "#fff" : "#64748b", fontSize: 12, fontWeight: 600, cursor: newWsName.trim() ? "pointer" : "not-allowed" }}>Crear</button>
                  </div>
                </div>
              )}

              {ws && !showNewWs && (
                <div>
                  <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      {editingWs === ws.id ? (
                        <input value={ws.name} onChange={e => onUpdateWorkspace(ws.id, { name: e.target.value })} onBlur={() => setEditingWs(null)} onKeyDown={e => { if (e.key === "Enter") setEditingWs(null); }} autoFocus style={{ padding: "4px 8px", borderRadius: 6, border: "1px solid #6366f1", background: "#0f172a", color: "#f8fafc", fontSize: 14, fontWeight: 600, outline: "none", width: 200 }} />
                      ) : (
                        <><span style={{ fontSize: 20 }}>{ws.icon}</span><span style={{ fontSize: 15, fontWeight: 700, color: "#e2e8f0" }}>{ws.name}</span><span style={{ fontSize: 11, color: "#64748b" }}>({ws.memberIds?.length || 0} miembros)</span></>
                      )}
                    </div>
                    <div style={{ display: "flex", gap: 4 }}>
                      <IconBtn onClick={() => setEditingWs(editingWs === ws.id ? null : ws.id)} title="Editar" size={28}>✏️</IconBtn>
                      {deleteConfirm === ws.id ? (
                        <div style={{ display: "flex", gap: 4, alignItems: "center" }}>
                          <span style={{ fontSize: 10, color: "#ef4444" }}>¿Eliminar?</span>
                          <button onClick={() => { onDeleteWorkspace(ws.id); setDeleteConfirm(null); setSelectedWs(data.workspaces.filter(w => w.id !== ws.id)[0]?.id); }} style={{ padding: "3px 8px", borderRadius: 4, border: "none", background: "#ef4444", color: "#fff", fontSize: 10, fontWeight: 600, cursor: "pointer" }}>Sí</button>
                          <button onClick={() => setDeleteConfirm(null)} style={{ padding: "3px 8px", borderRadius: 4, border: "1px solid #334155", background: "transparent", color: "#94a3b8", fontSize: 10, cursor: "pointer" }}>No</button>
                        </div>
                      ) : <IconBtn onClick={() => setDeleteConfirm(ws.id)} title="Eliminar" size={28} danger>🗑️</IconBtn>}
                    </div>
                  </div>
                  <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                    {data.members.map(member => {
                      const isMember = ws.memberIds?.includes(member.id);
                      const isAdminUser = member.role === "admin";
                      return (
                        <div key={member.id} onClick={() => { if (isAdminUser) return; const newIds = isMember ? ws.memberIds.filter(id => id !== member.id) : [...(ws.memberIds || []), member.id]; onUpdateWorkspaceMembers(ws.id, newIds); }} style={{
                          display: "flex", alignItems: "center", gap: 12, padding: "10px 14px", borderRadius: 10, cursor: isAdminUser ? "default" : "pointer",
                          background: isMember ? "rgba(99,102,241,0.08)" : "#0f172a", border: `1px solid ${isMember ? "#6366f140" : "#334155"}`, transition: "all 0.15s", opacity: isAdminUser ? 0.7 : 1,
                        }}>
                          <div style={{ width: 22, height: 22, borderRadius: 6, border: `2px solid ${isMember ? "#6366f1" : "#475569"}`, background: isMember ? "#6366f1" : "transparent", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 12, color: "#fff", flexShrink: 0 }}>{isMember && "✓"}</div>
                          <Avatar member={member} size={28} />
                          <div style={{ flex: 1 }}><div style={{ fontSize: 13, fontWeight: 600, color: "#e2e8f0" }}>{member.name}</div><div style={{ fontSize: 10, color: "#64748b" }}>{isAdminUser ? "Admin — acceso total" : "Miembro"}</div></div>
                          {isAdminUser && <span style={{ fontSize: 10, color: "#6366f1", fontWeight: 600, background: "#6366f118", padding: "2px 8px", borderRadius: 4 }}>Siempre</span>}
                        </div>
                      );
                    })}
                  </div>
                </div>
              )}
            </div>
          )}

          {/* ─── TAB: USERS ─── */}
          {tab === "users" && (
            <div>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 }}>
                <span style={{ fontSize: 13, fontWeight: 700, color: "#e2e8f0" }}>Usuarios registrados ({data.members.length})</span>
                <button onClick={() => setShowNewUser(!showNewUser)} style={{
                  padding: "7px 14px", borderRadius: 8, border: showNewUser ? "1px solid #6366f1" : "1px solid #334155",
                  background: showNewUser ? "#6366f115" : "#0f172a", color: showNewUser ? "#a5b4fc" : "#94a3b8",
                  fontSize: 12, fontWeight: 600, cursor: "pointer",
                }}>+ Nuevo usuario</button>
              </div>

              {/* Create user form */}
              {showNewUser && (
                <div style={{ marginBottom: 16, padding: "16px", borderRadius: 12, background: "#0f172a", border: "1px solid #6366f140" }}>
                  <div style={{ fontSize: 13, fontWeight: 700, color: "#e2e8f0", marginBottom: 12 }}>Crear nuevo usuario</div>
                  <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 12 }}>
                    <div>
                      <label style={{ fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 4, display: "block" }}>Nombre *</label>
                      <input value={nuName} onChange={e => setNuName(e.target.value)} placeholder="Nombre completo" autoFocus
                        style={{ width: "100%", padding: "9px 12px", borderRadius: 8, border: "1px solid #334155", background: "#1e293b", color: "#f8fafc", fontSize: 13, fontWeight: 600, outline: "none" }} />
                    </div>
                    <div>
                      <label style={{ fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 4, display: "block" }}>Email</label>
                      <input value={nuEmail} onChange={e => setNuEmail(e.target.value)} placeholder="correo@unisant.mx" type="email"
                        style={{ width: "100%", padding: "9px 12px", borderRadius: 8, border: "1px solid #334155", background: "#1e293b", color: "#f8fafc", fontSize: 13, outline: "none" }} />
                    </div>
                  </div>
                  <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 12 }}>
                    <div>
                      <label style={{ fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 4, display: "block" }}>Rol</label>
                      <select value={nuRole} onChange={e => setNuRole(e.target.value)} style={{ width: "100%", padding: "9px 12px", borderRadius: 8, border: "1px solid #334155", background: "#1e293b", color: "#e2e8f0", fontSize: 13, outline: "none", cursor: "pointer" }}>
                        <option value="member">Miembro</option>
                        <option value="admin">Admin</option>
                      </select>
                    </div>
                    <div>
                      <label style={{ fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 4, display: "block" }}>Color</label>
                      <div style={{ display: "flex", gap: 5, flexWrap: "wrap" }}>
                        {COLOR_OPTIONS.map(c => (
                          <span key={c} onClick={() => setNuColor(c)} style={{ width: 24, height: 24, borderRadius: 5, background: c, cursor: "pointer", border: `2px solid ${nuColor === c ? "#fff" : "transparent"}` }} />
                        ))}
                      </div>
                    </div>
                  </div>
                  <div style={{ marginBottom: 14 }}>
                    <label style={{ fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Asignar a espacios</label>
                    <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                      {data.workspaces.map(w => {
                        const sel = nuWorkspaces.includes(w.id);
                        return (
                          <span key={w.id} onClick={() => setNuWorkspaces(sel ? nuWorkspaces.filter(id => id !== w.id) : [...nuWorkspaces, w.id])} style={{
                            padding: "5px 10px", borderRadius: 8, cursor: "pointer", fontSize: 11, fontWeight: 500,
                            background: sel ? w.color + "20" : "#1e293b", color: sel ? "#e2e8f0" : "#64748b",
                            border: `1px solid ${sel ? w.color : "#334155"}`, display: "flex", alignItems: "center", gap: 4,
                          }}>{w.icon} {w.name} {sel && "✓"}</span>
                        );
                      })}
                    </div>
                  </div>
                  {/* Preview */}
                  {nuName.trim() && (
                    <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 14px", borderRadius: 8, background: "#1e293b", border: "1px solid #334155", marginBottom: 12 }}>
                      <div style={{ width: 32, height: 32, borderRadius: "50%", background: nuColor, display: "flex", alignItems: "center", justifyContent: "center", color: "#fff", fontSize: 12, fontWeight: 700 }}>
                        {nuName.split(" ").map(w => w[0]).join("").toUpperCase().slice(0, 2)}
                      </div>
                      <div>
                        <div style={{ fontSize: 13, fontWeight: 600, color: "#e2e8f0" }}>{nuName}</div>
                        <div style={{ fontSize: 10, color: "#64748b" }}>{nuEmail || "Sin email"} · {nuRole === "admin" ? "Admin" : "Miembro"} · {nuWorkspaces.length} espacios</div>
                      </div>
                    </div>
                  )}
                  <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
                    <button onClick={() => setShowNewUser(false)} style={{ padding: "8px 16px", borderRadius: 8, border: "1px solid #334155", background: "transparent", color: "#94a3b8", fontSize: 12, cursor: "pointer" }}>Cancelar</button>
                    <button onClick={handleCreateUser} disabled={!nuName.trim()} style={{ padding: "8px 20px", borderRadius: 8, border: "none", background: nuName.trim() ? "#6366f1" : "#334155", color: nuName.trim() ? "#fff" : "#64748b", fontSize: 12, fontWeight: 600, cursor: nuName.trim() ? "pointer" : "not-allowed" }}>Crear usuario</button>
                  </div>
                </div>
              )}

              {/* User list */}
              <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                {data.members.map(member => {
                  const memberWs = data.workspaces.filter(ws => ws.memberIds?.includes(member.id));
                  const isCurrentUser = member.id === data.currentUser;
                  return (
                    <div key={member.id} style={{
                      display: "flex", alignItems: "center", gap: 12, padding: "12px 14px", borderRadius: 10,
                      background: "#0f172a", border: "1px solid #334155",
                    }}>
                      <Avatar member={member} size={34} />
                      <div style={{ flex: 1 }}>
                        <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
                          <span style={{ fontSize: 13, fontWeight: 600, color: "#e2e8f0" }}>{member.name}</span>
                          {member.role === "admin" && <span style={{ fontSize: 9, fontWeight: 700, color: "#6366f1", background: "#6366f118", padding: "1px 6px", borderRadius: 3, textTransform: "uppercase" }}>Admin</span>}
                          {isCurrentUser && <span style={{ fontSize: 9, fontWeight: 700, color: "#22c55e", background: "#22c55e18", padding: "1px 6px", borderRadius: 3 }}>Tú</span>}
                        </div>
                        {member.email && <div style={{ fontSize: 11, color: "#64748b" }}>{member.email}</div>}
                        <div style={{ display: "flex", gap: 4, marginTop: 4, flexWrap: "wrap" }}>
                          {memberWs.slice(0, 4).map(ws => (
                            <span key={ws.id} style={{ fontSize: 9, color: "#475569", background: "#1e293b", padding: "1px 5px", borderRadius: 3 }}>{ws.icon} {ws.name}</span>
                          ))}
                          {memberWs.length > 4 && <span style={{ fontSize: 9, color: "#475569" }}>+{memberWs.length - 4}</span>}
                        </div>
                      </div>
                      {!isCurrentUser && (
                        deleteUserConfirm === member.id ? (
                          <div style={{ display: "flex", gap: 4, alignItems: "center" }}>
                            <span style={{ fontSize: 10, color: "#ef4444" }}>¿Seguro?</span>
                            <button onClick={() => { onDeleteMember(member.id); setDeleteUserConfirm(null); }} style={{ padding: "3px 8px", borderRadius: 4, border: "none", background: "#ef4444", color: "#fff", fontSize: 10, fontWeight: 600, cursor: "pointer" }}>Sí</button>
                            <button onClick={() => setDeleteUserConfirm(null)} style={{ padding: "3px 8px", borderRadius: 4, border: "1px solid #334155", background: "transparent", color: "#94a3b8", fontSize: 10, cursor: "pointer" }}>No</button>
                          </div>
                        ) : <IconBtn onClick={() => setDeleteUserConfirm(member.id)} title="Eliminar usuario" size={28} danger>🗑️</IconBtn>
                      )}
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          {/* ─── TAB: PERMISSIONS ─── */}
          {tab === "perms" && (
            <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
              <div style={{ padding: "16px", borderRadius: 12, background: "#0f172a", border: "1px solid #334155" }}>
                <div style={{ fontSize: 13, fontWeight: 700, color: "#e2e8f0", marginBottom: 4 }}>📝 Crear tareas</div>
                <div style={{ fontSize: 11, color: "#64748b", marginBottom: 12 }}>Solo estos usuarios (y Admin) pueden agregar tareas a los tableros.</div>
                <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                  {data.members.filter(m => m.role !== "admin").map(member => {
                    const isCreator = (data.taskCreators || []).includes(member.id);
                    return (
                      <span key={member.id} onClick={() => {
                        const current = data.taskCreators || [];
                        onUpdateTaskCreators(isCreator ? current.filter(id => id !== member.id) : [...current, member.id]);
                      }} style={{
                        display: "inline-flex", alignItems: "center", gap: 4, padding: "5px 10px", borderRadius: 8, cursor: "pointer",
                        fontSize: 11, fontWeight: 500, background: isCreator ? "#6366f1" : "#1e293b", color: isCreator ? "#fff" : "#64748b",
                        border: `1px solid ${isCreator ? "#6366f1" : "#334155"}`,
                      }}><Avatar member={member} size={18} />{member.name.split(" ").slice(0, 2).join(" ")}{isCreator && " ✓"}</span>
                    );
                  })}
                </div>
              </div>
              <div style={{ padding: "16px", borderRadius: 12, background: "#0f172a", border: "1px solid #334155" }}>
                <div style={{ fontSize: 13, fontWeight: 700, color: "#e2e8f0", marginBottom: 4 }}>👥 Dashboard de Equipo</div>
                <div style={{ fontSize: 11, color: "#64748b", marginBottom: 12 }}>Estos usuarios (y Admin) pueden ver el dashboard general con métricas de todo el equipo.</div>
                <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                  {data.members.filter(m => m.role !== "admin").map(member => {
                    const isViewer = (data.dashboardViewers || []).includes(member.id);
                    return (
                      <span key={member.id} onClick={() => {
                        const current = data.dashboardViewers || [];
                        onUpdateDashboardViewers(isViewer ? current.filter(id => id !== member.id) : [...current, member.id]);
                      }} style={{
                        display: "inline-flex", alignItems: "center", gap: 4, padding: "5px 10px", borderRadius: 8, cursor: "pointer",
                        fontSize: 11, fontWeight: 500, background: isViewer ? "#0891b2" : "#1e293b", color: isViewer ? "#fff" : "#64748b",
                        border: `1px solid ${isViewer ? "#0891b2" : "#334155"}`,
                      }}><Avatar member={member} size={18} />{member.name.split(" ").slice(0, 2).join(" ")}{isViewer && " ✓"}</span>
                    );
                  })}
                </div>
              </div>
              <div style={{ padding: "10px 14px", borderRadius: 8, background: "#0f172a", border: "1px solid #334155" }}>
                <div style={{ fontSize: 11, color: "#64748b", lineHeight: 1.5 }}>
                  💡 Los <strong style={{ color: "#6366f1" }}>Admin</strong> siempre tienen todos los permisos. Los cambios aplican inmediatamente.
                </div>
              </div>
            </div>
          )}

          {/* ─── TAB: TICKETS ─── */}
          {tab === "tickets" && (
            <div>
              <div style={{ fontSize: 13, fontWeight: 700, color: "#e2e8f0", marginBottom: 4 }}>🎫 Formularios de tickets</div>
              <div style={{ fontSize: 11, color: "#64748b", marginBottom: 16 }}>Configura formularios públicos para recibir solicitudes externas. Cada espacio puede tener su propia URL.</div>

              <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 20 }}>
                {data.workspaces.map(w => (
                  <button key={w.id} onClick={() => selectTicketWs(w.id)} style={{
                    padding: "8px 14px", borderRadius: 8,
                    border: `2px solid ${tSelWs === w.id ? w.color : "#334155"}`,
                    background: tSelWs === w.id ? w.color + "18" : "#0f172a",
                    color: tSelWs === w.id ? "#f8fafc" : "#94a3b8",
                    fontSize: 12, fontWeight: 600, cursor: "pointer",
                    display: "flex", alignItems: "center", gap: 6,
                  }}>
                    {w.icon} {w.name}
                    {w.ticketEnabled && <span style={{ width: 8, height: 8, borderRadius: "50%", background: "#22c55e", flexShrink: 0 }} />}
                  </button>
                ))}
              </div>

              {tSelWs && (() => {
                const tWs = data.workspaces.find(w => w.id === tSelWs);
                return (
                  <div style={{ padding: 20, borderRadius: 12, background: "#0f172a", border: "1px solid #334155" }}>
                    <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 16 }}>
                      <div style={{ fontSize: 14, fontWeight: 700, color: "#e2e8f0" }}>{tWs?.icon} {tWs?.name}</div>
                      <div onClick={() => setTEnabled(!tEnabled)} style={{ display: "flex", alignItems: "center", gap: 8, cursor: "pointer" }}>
                        <span style={{ fontSize: 12, color: tEnabled ? "#22c55e" : "#64748b", fontWeight: 600 }}>{tEnabled ? "Activo" : "Inactivo"}</span>
                        <div style={{ width: 44, height: 24, borderRadius: 12, background: tEnabled ? "#22c55e" : "#334155", padding: 2, transition: "background 0.2s" }}>
                          <div style={{ width: 20, height: 20, borderRadius: 10, background: "#fff", transform: tEnabled ? "translateX(20px)" : "translateX(0)", transition: "transform 0.2s" }} />
                        </div>
                      </div>
                    </div>

                    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 16 }}>
                      <div>
                        <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Slug de URL *</label>
                        <input value={tSlug} onChange={e => setTSlug(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ""))} placeholder="soporte-tech" style={{ width: "100%", padding: "9px 12px", borderRadius: 8, border: "1px solid #334155", background: "#1e293b", color: "#e2e8f0", fontSize: 13, outline: "none" }} />
                        {tSlug && <div style={{ fontSize: 10, color: "#64748b", marginTop: 4 }}>📎 tudominio.com/ticket/{tSlug}</div>}
                      </div>
                      <div>
                        <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Modo de asignación</label>
                        <select value={tMode} onChange={e => setTMode(e.target.value)} style={{ width: "100%", padding: "9px 12px", borderRadius: 8, border: "1px solid #334155", background: "#1e293b", color: "#e2e8f0", fontSize: 13, outline: "none", cursor: "pointer" }}>
                          <option value="rotation">🔄 Rotación automática</option>
                          <option value="manual">👤 Asignación fija</option>
                        </select>
                        {tMode === "manual" && (() => {
                          const wsMembers = data.members.filter(m => {
                            const w = data.workspaces.find(ws => ws.id === tSelWs);
                            return w?.memberIds?.includes(m.id);
                          });
                          return (
                            <div style={{ marginTop: 8 }}>
                              <label style={{ fontSize: 10, fontWeight: 600, color: "#475569", display: "block", marginBottom: 4 }}>Asignar siempre a:</label>
                              <select value={tAssigneeId} onChange={e => setTAssigneeId(e.target.value)} style={{ width: "100%", padding: "8px 10px", borderRadius: 8, border: "1px solid #334155", background: "#1e293b", color: "#e2e8f0", fontSize: 12, outline: "none", cursor: "pointer" }}>
                                <option value="">Seleccionar persona...</option>
                                {wsMembers.map(m => <option key={m.id} value={m.id}>{m.name}</option>)}
                              </select>
                            </div>
                          );
                        })()}
                      </div>
                    </div>

                    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 16 }}>
                    <div>
                      <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Categorías</label>
                      <input value={tCats} onChange={e => setTCats(e.target.value)} placeholder="Soporte técnico, Acceso a plataforma..." style={{ width: "100%", padding: "9px 12px", borderRadius: 8, border: "1px solid #334155", background: "#1e293b", color: "#e2e8f0", fontSize: 13, outline: "none" }} />
                      <div style={{ fontSize: 10, color: "#475569", marginTop: 4 }}>Separadas por coma</div>
                    </div>
                    <div>
                      <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Sedes</label>
                      <input value={tSedes} onChange={e => setTSedes(e.target.value)} placeholder="Monterrey, CDMX, Guadalajara, En línea..." style={{ width: "100%", padding: "9px 12px", borderRadius: 8, border: "1px solid #334155", background: "#1e293b", color: "#e2e8f0", fontSize: 13, outline: "none" }} />
                      <div style={{ fontSize: 10, color: "#475569", marginTop: 4 }}>Separadas por coma</div>
                    </div>
                    </div>

                    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 16 }}>
                      <div>
                        <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Tablero destino</label>
                        <select value={tBoardId} onChange={e => { setTBoardId(e.target.value); setTColId(""); }} style={{ width: "100%", padding: "9px 12px", borderRadius: 8, border: "1px solid #334155", background: "#1e293b", color: "#e2e8f0", fontSize: 13, outline: "none", cursor: "pointer" }}>
                          <option value="">Seleccionar...</option>
                          {tWsBoards.map(b => <option key={b.id} value={b.id}>{FLOW_TYPES[b.flowType]?.icon} {b.name}</option>)}
                        </select>
                      </div>
                      <div>
                        <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Columna inicial</label>
                        <select value={tColId} onChange={e => setTColId(e.target.value)} disabled={!tBoardId} style={{ width: "100%", padding: "9px 12px", borderRadius: 8, border: "1px solid #334155", background: "#1e293b", color: tBoardId ? "#e2e8f0" : "#475569", fontSize: 13, outline: "none", cursor: tBoardId ? "pointer" : "not-allowed" }}>
                          <option value="">Seleccionar...</option>
                          {tSelBoardCols.map(c => <option key={c.id} value={c.id}>{c.title}</option>)}
                        </select>
                      </div>
                    </div>

                    {tSlug && tEnabled && (
                      <div style={{ padding: 12, borderRadius: 8, background: "#6366f110", border: "1px solid #6366f130", marginBottom: 16 }}>
                        <div style={{ fontSize: 12, fontWeight: 600, color: "#6366f1", marginBottom: 4 }}>🔗 URL del formulario</div>
                        <div style={{ fontSize: 13, color: "#e2e8f0", fontWeight: 500 }}>tudominio.com/ticket/{tSlug}</div>
                        <div style={{ fontSize: 10, color: "#64748b", marginTop: 4 }}>Comparte esta URL con quienes necesiten enviar solicitudes.</div>
                      </div>
                    )}

                    <div style={{ display: "flex", justifyContent: "space-between" }}>
                      <button onClick={() => onPreviewTicketForm(tSelWs)} style={{ padding: "10px 20px", borderRadius: 8, border: "1px solid #334155", background: "transparent", color: "#94a3b8", fontSize: 12, fontWeight: 600, cursor: "pointer" }}>
                        👁️ Vista previa del formulario
                      </button>
                      <button onClick={saveTicketConfig} style={{ padding: "10px 24px", borderRadius: 8, border: "none", background: "#6366f1", color: "#fff", fontSize: 13, fontWeight: 600, cursor: "pointer" }}>
                        💾 Guardar configuración
                      </button>
                    </div>
                  </div>
                );
              })()}

              {!tSelWs && <div style={{ padding: 30, textAlign: "center", color: "#475569", fontSize: 13 }}>Selecciona un espacio para configurar su formulario de tickets.</div>}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ─── DASHBOARD VIEW ───
function DashboardView({ data, accessibleWorkspaces, onTaskClick }) {
  const userId = data.currentUser;
  const user = data.members.find(m => m.id === userId);

  // Gather all tasks from accessible workspaces
  const allTasks = useMemo(() => {
    const wsIds = new Set(accessibleWorkspaces.map(w => w.id));
    const tasks = [];
    data.boards.forEach(board => {
      if (!wsIds.has(board.workspaceId)) return;
      board.tasks.forEach(task => {
        const isAssigned = task.assignees?.includes(userId);
        const isUnassigned = !task.assignees || task.assignees.length === 0;
        if (isAssigned || isUnassigned) {
          tasks.push({ ...task, _boardId: board.id, _boardName: board.name, _wsName: accessibleWorkspaces.find(w => w.id === board.workspaceId)?.name, _columns: board.columns });
        }
      });
    });
    return tasks;
  }, [data, accessibleWorkspaces, userId]);

  // KPIs
  const kpis = useMemo(() => {
    const now = new Date();
    const total = allTasks.length;
    const completed = allTasks.filter(t => t._columns && t.status === t._columns[t._columns.length - 1]?.id).length;
    const inProgress = allTasks.filter(t => { const cols = t._columns || []; const idx = cols.findIndex(c => c.id === t.status); return idx > 0 && idx < cols.length - 1; }).length;
    const pending = allTasks.filter(t => { const cols = t._columns || []; return cols[0]?.id === t.status; }).length;
    const overdue = allTasks.filter(t => t.dueDate && new Date(t.dueDate) < now && t._columns && t.status !== t._columns[t._columns.length - 1]?.id).length;
    const completedOnTime = allTasks.filter(t => {
      if (!t.completedAt || !t.dueDate) return false;
      return new Date(t.completedAt) <= new Date(t.dueDate);
    }).length;
    const completedTotal = allTasks.filter(t => t.completedAt).length;
    const complianceRate = completedTotal > 0 ? Math.round((completedOnTime / completedTotal) * 100) : 100;
    return { total, completed, inProgress, pending, overdue, complianceRate };
  }, [allTasks]);

  // Eisenhower classification
  const eisenhower = useMemo(() => {
    const now = new Date();
    const URGENT_DAYS = 7; // tasks due within 7 days are "urgent"
    const quadrants = { q1: [], q2: [], q3: [], q4: [] };

    allTasks.forEach(task => {
      // Skip completed tasks
      if (task._columns && task.status === task._columns[task._columns.length - 1]?.id) return;

      // Importance: based on priority
      const isImportant = task.priority === "urgent" || task.priority === "high";

      // Urgency: based on due date proximity
      let isUrgent = false;
      if (task.dueDate) {
        const daysUntilDue = (new Date(task.dueDate) - now) / 86400000;
        isUrgent = daysUntilDue <= URGENT_DAYS;
      } else if (task.priority === "urgent") {
        isUrgent = true; // no date but urgent priority
      }

      if (isUrgent && isImportant) quadrants.q1.push(task);
      else if (!isUrgent && isImportant) quadrants.q2.push(task);
      else if (isUrgent && !isImportant) quadrants.q3.push(task);
      else quadrants.q4.push(task);
    });

    return quadrants;
  }, [allTasks]);

  // Weekly workload chart
  const weeklyLoad = useMemo(() => {
    const weeks = [];
    const now = new Date();
    for (let i = -2; i <= 4; i++) {
      const weekStart = new Date(now);
      weekStart.setDate(weekStart.getDate() - weekStart.getDay() + 1 + i * 7);
      weekStart.setHours(0, 0, 0, 0);
      const weekEnd = new Date(weekStart);
      weekEnd.setDate(weekEnd.getDate() + 6);
      weekEnd.setHours(23, 59, 59, 999);
      // A task was active during a week if:
      // - it was created before or during the week AND
      // - it was not completed before the week started
      const tasksInWeek = allTasks.filter(t => {
        const created = t.createdAt ? new Date(t.createdAt) : null;
        if (!created || created > weekEnd) return false; // not yet created
        const completed = t.completedAt ? new Date(t.completedAt) : null;
        if (completed && completed < weekStart) return false; // completed before this week
        return true;
      }).length;
      const label = `${weekStart.getDate()}/${weekStart.getMonth() + 1}`;
      weeks.push({ label, count: tasksInWeek, isCurrent: i === 0 });
    }
    return weeks;
  }, [allTasks]);

  // Sorted by hierarchy (priority order, then due date)
  const sortedTasks = useMemo(() => {
    const active = allTasks.filter(t => t._columns && t.status !== t._columns[t._columns.length - 1]?.id);
    const order = { urgent: 0, high: 1, medium: 2, low: 3 };
    return active.sort((a, b) => {
      const pa = order[a.priority] ?? 4;
      const pb = order[b.priority] ?? 4;
      if (pa !== pb) return pa - pb;
      const da = a.dueDate ? new Date(a.dueDate).getTime() : Infinity;
      const db = b.dueDate ? new Date(b.dueDate).getTime() : Infinity;
      return da - db;
    });
  }, [allTasks]);

  const maxWeekCount = Math.max(...weeklyLoad.map(w => w.count), 1);

  const QUAD_CONFIG = {
    q1: { title: "HACER YA", subtitle: "Urgente + Importante", color: "#ef4444", bg: "#ef444415", icon: "🔥" },
    q2: { title: "PLANIFICAR", subtitle: "Importante + No urgente", color: "#3b82f6", bg: "#3b82f615", icon: "📅" },
    q3: { title: "DELEGAR", subtitle: "Urgente + No importante", color: "#f59e0b", bg: "#f59e0b15", icon: "👋" },
    q4: { title: "ELIMINAR / POSPONER", subtitle: "No urgente + No importante", color: "#64748b", bg: "#64748b10", icon: "🗑️" },
  };

  return (
    <div style={{ flex: 1, overflow: "auto", display: "flex", flexDirection: "column" }}>
      {/* Dashboard header */}
      <div style={{ padding: "18px 24px 14px", borderBottom: "1px solid #334155", background: "#1e293b", flexShrink: 0 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <span style={{ fontSize: 24 }}>📊</span>
          <div>
            <h1 style={{ fontSize: 18, fontWeight: 700, color: "#f8fafc", margin: 0 }}>Mi Dashboard</h1>
            <p style={{ fontSize: 12, color: "#64748b", margin: 0 }}>{user?.name} · {allTasks.length} tareas en {accessibleWorkspaces.length} espacios</p>
          </div>
        </div>
      </div>

      <div style={{ flex: 1, overflow: "auto", padding: "20px 24px" }}>
        {/* KPI CARDS */}
        <div style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 12, marginBottom: 24 }}>
          {[
            { label: "Total", value: kpis.total, icon: "📋", color: "#6366f1" },
            { label: "En progreso", value: kpis.inProgress, icon: "⚡", color: "#f59e0b" },
            { label: "Pendientes", value: kpis.pending, icon: "📥", color: "#3b82f6" },
            { label: "Vencidas", value: kpis.overdue, icon: "⚠️", color: "#ef4444" },
            { label: "Cumplimiento", value: `${kpis.complianceRate}%`, icon: "✅", color: kpis.complianceRate >= 80 ? "#22c55e" : kpis.complianceRate >= 50 ? "#f59e0b" : "#ef4444" },
          ].map((kpi, i) => (
            <div key={i} style={{
              padding: "16px", borderRadius: 12, background: "#1e293b",
              border: "1px solid #334155", display: "flex", flexDirection: "column", gap: 6,
            }}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
                <span style={{ fontSize: 11, color: "#64748b", fontWeight: 600, textTransform: "uppercase" }}>{kpi.label}</span>
                <span style={{ fontSize: 16 }}>{kpi.icon}</span>
              </div>
              <span style={{ fontSize: 28, fontWeight: 800, color: kpi.color, letterSpacing: "-0.03em" }}>{kpi.value}</span>
            </div>
          ))}
        </div>

        {/* EISENHOWER MATRIX */}
        <div style={{ marginBottom: 24 }}>
          <h2 style={{ fontSize: 14, fontWeight: 700, color: "#f8fafc", marginBottom: 12, display: "flex", alignItems: "center", gap: 8 }}>
            🎯 Matriz de Eisenhower
            <span style={{ fontSize: 11, fontWeight: 400, color: "#64748b" }}>Prioridad = Importancia · Fecha {'<'} 7 días = Urgencia</span>
          </h2>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gridTemplateRows: "auto auto", gap: 10 }}>
            {/* Axis labels */}
            <div style={{ gridColumn: "1 / -1", display: "flex", justifyContent: "center", marginBottom: -4 }}>
              <span style={{ fontSize: 10, fontWeight: 700, color: "#475569", textTransform: "uppercase", letterSpacing: "0.1em" }}>← Urgente · · · No urgente →</span>
            </div>
            {["q1", "q2", "q3", "q4"].map(qKey => {
              const q = QUAD_CONFIG[qKey];
              const tasks = eisenhower[qKey];
              return (
                <div key={qKey} style={{
                  padding: "14px", borderRadius: 12, background: q.bg,
                  border: `1px solid ${q.color}25`, minHeight: 120,
                }}>
                  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 10 }}>
                    <div>
                      <div style={{ fontSize: 12, fontWeight: 700, color: q.color }}>{q.icon} {q.title}</div>
                      <div style={{ fontSize: 10, color: "#64748b" }}>{q.subtitle}</div>
                    </div>
                    <span style={{ fontSize: 18, fontWeight: 800, color: q.color }}>{tasks.length}</span>
                  </div>
                  <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                    {tasks.slice(0, 5).map(task => {
                      const isOverdue = task.dueDate && new Date(task.dueDate) < new Date();
                      return (
                        <div key={task.id} onClick={() => onTaskClick(task)} style={{
                          padding: "6px 10px", borderRadius: 6, background: "#0f172a",
                          border: "1px solid #334155", cursor: "pointer",
                          display: "flex", alignItems: "center", gap: 6,
                          transition: "all 0.15s",
                        }}
                          onMouseEnter={e => e.currentTarget.style.borderColor = q.color}
                          onMouseLeave={e => e.currentTarget.style.borderColor = "#334155"}>
                          <span style={{ fontSize: 10 }}>{PRIORITY_CONFIG[task.priority]?.icon}</span>
                          <span style={{ fontSize: 11, fontWeight: 500, color: "#e2e8f0", flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{task.title}</span>
                          {task.dueDate && (
                            <span style={{ fontSize: 9, color: isOverdue ? "#ef4444" : "#475569", whiteSpace: "nowrap" }}>
                              {new Date(task.dueDate).toLocaleDateString("es-MX", { day: "numeric", month: "short" })}
                            </span>
                          )}
                        </div>
                      );
                    })}
                    {tasks.length > 5 && <span style={{ fontSize: 10, color: "#475569", textAlign: "center", marginTop: 2 }}>+{tasks.length - 5} más</span>}
                    {tasks.length === 0 && <span style={{ fontSize: 11, color: "#334155", fontStyle: "italic", textAlign: "center", padding: 8 }}>Sin tareas</span>}
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        {/* BOTTOM ROW: Workload chart + Priority list */}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
          {/* Weekly workload */}
          <div style={{ padding: "16px 18px", borderRadius: 12, background: "#1e293b", border: "1px solid #334155" }}>
            <h3 style={{ fontSize: 13, fontWeight: 700, color: "#f8fafc", marginBottom: 14, display: "flex", alignItems: "center", gap: 6 }}>
              📈 Carga semanal
            </h3>
            <div style={{ display: "flex", alignItems: "flex-end", gap: 8, height: 120 }}>
              {weeklyLoad.map((w, i) => (
                <div key={i} style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center", gap: 4 }}>
                  <span style={{ fontSize: 10, fontWeight: 700, color: w.isCurrent ? "#6366f1" : "#64748b" }}>{w.count}</span>
                  <div style={{
                    width: "100%", borderRadius: 4,
                    height: `${Math.max((w.count / maxWeekCount) * 90, 4)}px`,
                    background: w.isCurrent ? "linear-gradient(180deg, #6366f1, #4f46e5)" : "#334155",
                    transition: "height 0.3s",
                  }} />
                  <span style={{ fontSize: 9, color: w.isCurrent ? "#a5b4fc" : "#475569", fontWeight: w.isCurrent ? 700 : 400 }}>{w.label}</span>
                </div>
              ))}
            </div>
            <div style={{ textAlign: "center", marginTop: 8 }}>
              <span style={{ fontSize: 10, color: "#475569" }}>Tareas activas (en progreso) por semana</span>
            </div>
          </div>

          {/* Priority-sorted task list */}
          <div style={{ padding: "16px 18px", borderRadius: 12, background: "#1e293b", border: "1px solid #334155", maxHeight: 350, display: "flex", flexDirection: "column" }}>
            <h3 style={{ fontSize: 13, fontWeight: 700, color: "#f8fafc", marginBottom: 10, display: "flex", alignItems: "center", gap: 6, flexShrink: 0 }}>
              📋 Tareas por jerarquía
              <span style={{ fontSize: 11, fontWeight: 400, color: "#64748b" }}>({sortedTasks.length} activas)</span>
            </h3>
            <div style={{ flex: 1, overflowY: "auto" }}>
              {sortedTasks.map((task, idx) => {
                const isOverdue = task.dueDate && new Date(task.dueDate) < new Date();
                return (
                  <div key={task.id} onClick={() => onTaskClick(task)} style={{
                    display: "flex", alignItems: "center", gap: 8,
                    padding: "8px 10px", borderRadius: 6, cursor: "pointer",
                    borderBottom: "1px solid #0f172a",
                    transition: "background 0.1s",
                  }}
                    onMouseEnter={e => e.currentTarget.style.background = "rgba(99,102,241,0.06)"}
                    onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
                    <span style={{ fontSize: 11, fontWeight: 700, color: "#334155", width: 22, textAlign: "right", flexShrink: 0 }}>#{idx + 1}</span>
                    <span style={{ fontSize: 12 }}>{PRIORITY_CONFIG[task.priority]?.icon}</span>
                    <div style={{ flex: 1, overflow: "hidden" }}>
                      <div style={{ fontSize: 12, fontWeight: 600, color: "#e2e8f0", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{task.title}</div>
                      <div style={{ fontSize: 10, color: "#475569" }}>{task._wsName} · {task._boardName}</div>
                    </div>
                    {task.dueDate && (
                      <span style={{ fontSize: 10, color: isOverdue ? "#ef4444" : "#64748b", whiteSpace: "nowrap", fontWeight: isOverdue ? 600 : 400 }}>
                        {new Date(task.dueDate).toLocaleDateString("es-MX", { day: "numeric", month: "short" })}
                        {isOverdue && " ⚠️"}
                      </span>
                    )}
                  </div>
                );
              })}
              {sortedTasks.length === 0 && <div style={{ padding: 20, textAlign: "center", color: "#475569", fontSize: 12 }}>No tienes tareas activas 🎉</div>}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── GENERAL DASHBOARD VIEW ───
function GeneralDashboardView({ data, onTaskClick }) {
  // All tasks across all boards
  const allTasks = useMemo(() => {
    const tasks = [];
    data.boards.forEach(board => {
      board.tasks.forEach(task => {
        tasks.push({ ...task, _boardId: board.id, _boardName: board.name, _wsId: board.workspaceId, _columns: board.columns });
      });
    });
    return tasks;
  }, [data]);

  // Per-member stats
  const memberStats = useMemo(() => {
    const now = new Date();
    return data.members.map(member => {
      const myTasks = allTasks.filter(t => t.assignees?.includes(member.id));
      const total = myTasks.length;
      const completed = myTasks.filter(t => t._columns && t.status === t._columns[t._columns.length - 1]?.id).length;
      const active = myTasks.filter(t => {
        const cols = t._columns || [];
        const idx = cols.findIndex(c => c.id === t.status);
        return idx > 0 && idx < cols.length - 1;
      }).length;
      const overdue = myTasks.filter(t => t.dueDate && new Date(t.dueDate) < now && t._columns && t.status !== t._columns[t._columns.length - 1]?.id).length;
      const pending = total - completed - active;
      const completedOnTime = myTasks.filter(t => t.completedAt && t.dueDate && new Date(t.completedAt) <= new Date(t.dueDate)).length;
      const completedTotal = myTasks.filter(t => t.completedAt).length;
      const compliance = completedTotal > 0 ? Math.round((completedOnTime / completedTotal) * 100) : 100;
      return { member, total, completed, active, pending: Math.max(pending, 0), overdue, compliance };
    }).sort((a, b) => b.total - a.total);
  }, [allTasks, data.members]);

  // Global KPIs
  const globalKpis = useMemo(() => {
    const now = new Date();
    const total = allTasks.length;
    const completed = allTasks.filter(t => t._columns && t.status === t._columns[t._columns.length - 1]?.id).length;
    const overdue = allTasks.filter(t => t.dueDate && new Date(t.dueDate) < now && t._columns && t.status !== t._columns[t._columns.length - 1]?.id).length;
    const unassigned = allTasks.filter(t => !t.assignees || t.assignees.length === 0).length;
    const completedOnTime = allTasks.filter(t => t.completedAt && t.dueDate && new Date(t.completedAt) <= new Date(t.dueDate)).length;
    const completedTotal = allTasks.filter(t => t.completedAt).length;
    const compliance = completedTotal > 0 ? Math.round((completedOnTime / completedTotal) * 100) : 100;
    return { total, completed, overdue, unassigned, compliance, active: total - completed };
  }, [allTasks]);

  // Overdue tasks detail
  const overdueTasks = useMemo(() => {
    const now = new Date();
    return allTasks.filter(t => t.dueDate && new Date(t.dueDate) < now && t._columns && t.status !== t._columns[t._columns.length - 1]?.id)
      .sort((a, b) => new Date(a.dueDate) - new Date(b.dueDate));
  }, [allTasks]);

  const maxTaskCount = Math.max(...memberStats.map(s => s.total), 1);

  return (
    <div style={{ flex: 1, overflow: "auto", display: "flex", flexDirection: "column" }}>
      {/* Header */}
      <div style={{ padding: "18px 24px 14px", borderBottom: "1px solid #334155", background: "#1e293b", flexShrink: 0 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <span style={{ fontSize: 24 }}>👥</span>
          <div>
            <h1 style={{ fontSize: 18, fontWeight: 700, color: "#f8fafc", margin: 0 }}>Dashboard de Equipo</h1>
            <p style={{ fontSize: 12, color: "#64748b", margin: 0 }}>{data.members.length} miembros · {allTasks.length} tareas totales · {data.workspaces.length} espacios</p>
          </div>
        </div>
      </div>

      <div style={{ flex: 1, overflow: "auto", padding: "20px 24px" }}>
        {/* GLOBAL KPIs */}
        <div style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 12, marginBottom: 24 }}>
          {[
            { label: "Total", value: globalKpis.total, icon: "📋", color: "#6366f1" },
            { label: "Activas", value: globalKpis.active, icon: "⚡", color: "#f59e0b" },
            { label: "Vencidas", value: globalKpis.overdue, icon: "⚠️", color: "#ef4444" },
            { label: "Sin asignar", value: globalKpis.unassigned, icon: "👻", color: "#8b5cf6" },
            { label: "Cumplimiento", value: `${globalKpis.compliance}%`, icon: "✅", color: globalKpis.compliance >= 80 ? "#22c55e" : globalKpis.compliance >= 50 ? "#f59e0b" : "#ef4444" },
          ].map((kpi, i) => (
            <div key={i} style={{ padding: "16px", borderRadius: 12, background: "#1e293b", border: "1px solid #334155" }}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
                <span style={{ fontSize: 11, color: "#64748b", fontWeight: 600, textTransform: "uppercase" }}>{kpi.label}</span>
                <span style={{ fontSize: 16 }}>{kpi.icon}</span>
              </div>
              <span style={{ fontSize: 28, fontWeight: 800, color: kpi.color, letterSpacing: "-0.03em" }}>{kpi.value}</span>
            </div>
          ))}
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginBottom: 24 }}>
          {/* WORKLOAD BAR CHART */}
          <div style={{ padding: "16px 18px", borderRadius: 12, background: "#1e293b", border: "1px solid #334155" }}>
            <h3 style={{ fontSize: 13, fontWeight: 700, color: "#f8fafc", marginBottom: 14 }}>📊 Carga por persona</h3>
            <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
              {memberStats.filter(s => s.total > 0).map(({ member, total, active, completed, overdue }) => (
                <div key={member.id} style={{ display: "flex", alignItems: "center", gap: 10 }}>
                  <Avatar member={member} size={26} />
                  <div style={{ width: 70, flexShrink: 0, overflow: "hidden" }}>
                    <div style={{ fontSize: 11, fontWeight: 600, color: "#e2e8f0", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{member.name.split(" ")[0]}</div>
                  </div>
                  <div style={{ flex: 1, display: "flex", height: 18, borderRadius: 4, overflow: "hidden", background: "#0f172a" }}>
                    {completed > 0 && <div style={{ width: `${(completed / maxTaskCount) * 100}%`, background: "#22c55e", transition: "width 0.3s" }} title={`${completed} completadas`} />}
                    {active > 0 && <div style={{ width: `${(active / maxTaskCount) * 100}%`, background: "#6366f1", transition: "width 0.3s" }} title={`${active} activas`} />}
                    {overdue > 0 && <div style={{ width: `${(overdue / maxTaskCount) * 100}%`, background: "#ef4444", transition: "width 0.3s" }} title={`${overdue} vencidas`} />}
                  </div>
                  <span style={{ fontSize: 11, fontWeight: 700, color: "#94a3b8", width: 24, textAlign: "right" }}>{total}</span>
                </div>
              ))}
              {memberStats.every(s => s.total === 0) && <div style={{ padding: 16, textAlign: "center", color: "#475569", fontSize: 12 }}>Sin tareas asignadas</div>}
            </div>
            <div style={{ display: "flex", gap: 14, marginTop: 12, justifyContent: "center" }}>
              {[{ label: "Completadas", color: "#22c55e" }, { label: "Activas", color: "#6366f1" }, { label: "Vencidas", color: "#ef4444" }].map(l => (
                <span key={l.label} style={{ fontSize: 10, color: "#64748b", display: "flex", alignItems: "center", gap: 4 }}>
                  <span style={{ width: 8, height: 8, borderRadius: 2, background: l.color, display: "inline-block" }} />{l.label}
                </span>
              ))}
            </div>
          </div>

          {/* OVERDUE ALERTS */}
          <div style={{ padding: "16px 18px", borderRadius: 12, background: "#1e293b", border: "1px solid #334155", maxHeight: 350, display: "flex", flexDirection: "column" }}>
            <h3 style={{ fontSize: 13, fontWeight: 700, color: "#ef4444", marginBottom: 10, display: "flex", alignItems: "center", gap: 6, flexShrink: 0 }}>
              🚨 Tareas vencidas ({overdueTasks.length})
            </h3>
            <div style={{ flex: 1, overflowY: "auto" }}>
              {overdueTasks.slice(0, 15).map(task => {
                const daysLate = Math.floor((new Date() - new Date(task.dueDate)) / 86400000);
                return (
                  <div key={task.id} onClick={() => onTaskClick(task)} style={{
                    display: "flex", alignItems: "center", gap: 8,
                    padding: "8px 10px", borderRadius: 6, cursor: "pointer",
                    borderBottom: "1px solid #0f172a",
                  }}
                    onMouseEnter={e => e.currentTarget.style.background = "rgba(239,68,68,0.06)"}
                    onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
                    <span style={{ fontSize: 12 }}>{PRIORITY_CONFIG[task.priority]?.icon}</span>
                    <div style={{ flex: 1, overflow: "hidden" }}>
                      <div style={{ fontSize: 12, fontWeight: 600, color: "#e2e8f0", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{task.title}</div>
                      <div style={{ fontSize: 10, color: "#475569" }}>
                        {task.assignees?.map(aId => data.members.find(m => m.id === aId)?.name?.split(" ")[0]).filter(Boolean).join(", ") || "Sin asignar"}
                        {" · "}{task._boardName}
                      </div>
                    </div>
                    <span style={{ fontSize: 10, fontWeight: 700, color: "#ef4444", whiteSpace: "nowrap", background: "#ef444415", padding: "2px 8px", borderRadius: 4 }}>
                      {daysLate}d atraso
                    </span>
                  </div>
                );
              })}
              {overdueTasks.length === 0 && <div style={{ padding: 20, textAlign: "center", color: "#22c55e", fontSize: 13, fontWeight: 600 }}>🎉 Sin tareas vencidas</div>}
            </div>
          </div>
        </div>

        {/* MEMBER TABLE */}
        <div style={{ padding: "16px 18px", borderRadius: 12, background: "#1e293b", border: "1px solid #334155" }}>
          <h3 style={{ fontSize: 13, fontWeight: 700, color: "#f8fafc", marginBottom: 14 }}>📋 Resumen por miembro</h3>
          <table style={{ width: "100%", borderCollapse: "collapse" }}>
            <thead>
              <tr style={{ borderBottom: "2px solid #334155" }}>
                {["Miembro", "Total", "Activas", "Pendientes", "Completadas", "Vencidas", "Cumplimiento"].map(h => (
                  <th key={h} style={{ padding: "8px 12px", textAlign: "left", fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", letterSpacing: "0.05em" }}>{h}</th>
                ))}
              </tr>
            </thead>
            <tbody>
              {memberStats.map(({ member, total, active, pending, completed, overdue, compliance }) => (
                <tr key={member.id} style={{ borderBottom: "1px solid #0f172a" }}
                  onMouseEnter={e => e.currentTarget.style.background = "#0f172a"}
                  onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
                  <td style={{ padding: "10px 12px" }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <Avatar member={member} size={26} />
                      <div>
                        <div style={{ fontSize: 12, fontWeight: 600, color: "#e2e8f0" }}>{member.name}</div>
                        <div style={{ fontSize: 10, color: "#475569" }}>{member.role === "admin" ? "Admin" : "Miembro"}</div>
                      </div>
                    </div>
                  </td>
                  <td style={{ padding: "10px 12px", fontSize: 13, fontWeight: 700, color: "#e2e8f0" }}>{total}</td>
                  <td style={{ padding: "10px 12px", fontSize: 13, fontWeight: 600, color: "#6366f1" }}>{active}</td>
                  <td style={{ padding: "10px 12px", fontSize: 13, color: "#94a3b8" }}>{pending}</td>
                  <td style={{ padding: "10px 12px", fontSize: 13, fontWeight: 600, color: "#22c55e" }}>{completed}</td>
                  <td style={{ padding: "10px 12px" }}>
                    {overdue > 0 ? (
                      <span style={{ fontSize: 12, fontWeight: 700, color: "#ef4444", background: "#ef444415", padding: "2px 8px", borderRadius: 4 }}>{overdue}</span>
                    ) : <span style={{ fontSize: 12, color: "#334155" }}>0</span>}
                  </td>
                  <td style={{ padding: "10px 12px" }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <div style={{ flex: 1, height: 6, background: "#0f172a", borderRadius: 3, overflow: "hidden", maxWidth: 80 }}>
                        <div style={{ height: "100%", width: `${compliance}%`, background: compliance >= 80 ? "#22c55e" : compliance >= 50 ? "#f59e0b" : "#ef4444", borderRadius: 3, transition: "width 0.3s" }} />
                      </div>
                      <span style={{ fontSize: 11, fontWeight: 600, color: compliance >= 80 ? "#22c55e" : compliance >= 50 ? "#f59e0b" : "#ef4444" }}>{compliance}%</span>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

// ─── LIST VIEW ───
function ListView({ tasks, board, data, onTaskClick }) {
  const [sortField, setSortField] = useState("status");
  const [sortDir, setSortDir] = useState("asc");

  const sortedTasks = useMemo(() => {
    const sorted = [...tasks].sort((a, b) => {
      let va, vb;
      if (sortField === "status") {
        const colIds = board.columns.map(c => c.id);
        va = colIds.indexOf(a.status); vb = colIds.indexOf(b.status);
      } else if (sortField === "priority") {
        const order = { urgent: 0, high: 1, medium: 2, low: 3 };
        va = order[a.priority] ?? 4; vb = order[b.priority] ?? 4;
      } else if (sortField === "dueDate") {
        va = a.dueDate ? new Date(a.dueDate).getTime() : Infinity;
        vb = b.dueDate ? new Date(b.dueDate).getTime() : Infinity;
      } else if (sortField === "title") {
        va = a.title.toLowerCase(); vb = b.title.toLowerCase();
      }
      if (va < vb) return sortDir === "asc" ? -1 : 1;
      if (va > vb) return sortDir === "asc" ? 1 : -1;
      return 0;
    });
    return sorted;
  }, [tasks, sortField, sortDir, board]);

  const toggleSort = (field) => {
    if (sortField === field) setSortDir(d => d === "asc" ? "desc" : "asc");
    else { setSortField(field); setSortDir("asc"); }
  };
  const arrow = (field) => sortField === field ? (sortDir === "asc" ? " ▲" : " ▼") : "";

  return (
    <div style={{ maxWidth: 1100 }}>
      <table style={{ width: "100%", borderCollapse: "collapse" }}>
        <thead>
          <tr style={{ borderBottom: "2px solid #334155" }}>
            {[
              { key: "title", label: "Tarea" }, { key: "status", label: "Estado" },
              { key: "priority", label: "Prioridad" }, { key: null, label: "Asignados" },
              { key: "dueDate", label: "Fecha límite" }, { key: null, label: "Tiempo" }, { key: null, label: "Progreso" }, { key: null, label: "Tags" },
            ].map((h, i) => (
              <th key={i} onClick={() => h.key && toggleSort(h.key)} style={{ padding: "10px 12px", textAlign: "left", fontSize: 11, fontWeight: 700, color: sortField === h.key ? "#a5b4fc" : "#64748b", textTransform: "uppercase", letterSpacing: "0.05em", cursor: h.key ? "pointer" : "default", userSelect: "none", whiteSpace: "nowrap" }}>
                {h.label}{h.key ? arrow(h.key) : ""}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {sortedTasks.map(task => {
            const col = board.columns.find(c => c.id === task.status);
            const isOverdue = task.dueDate && new Date(task.dueDate) < new Date();
            const subDone = task.subtasks?.filter(s => s.done).length || 0;
            const subTotal = task.subtasks?.length || 0;
            return (
              <tr key={task.id} onClick={() => onTaskClick(task)} style={{ borderBottom: "1px solid #1e293b", cursor: "pointer", transition: "background 0.1s" }}
                onMouseEnter={e => e.currentTarget.style.background = "#1e293b"}
                onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
                <td style={{ padding: "12px", fontSize: 13, fontWeight: 600, color: "#f8fafc", maxWidth: 280 }}>
                  <div style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{task.title}</div>
                </td>
                <td style={{ padding: "12px" }}><Badge color={col?.color} bg={col?.color + "20"}>{col?.title}</Badge></td>
                <td style={{ padding: "12px" }}><Badge color={PRIORITY_CONFIG[task.priority]?.color} bg={PRIORITY_CONFIG[task.priority]?.bg}>{PRIORITY_CONFIG[task.priority]?.icon} {PRIORITY_CONFIG[task.priority]?.label}</Badge></td>
                <td style={{ padding: "12px" }}>
                  <div style={{ display: "flex" }}>
                    {task.assignees?.slice(0, 3).map(aId => { const m = data.members.find(m => m.id === aId); return m ? <Avatar key={aId} member={m} size={24} /> : null; })}
                    {task.assignees?.length > 3 && <span style={{ fontSize: 10, color: "#64748b", alignSelf: "center", marginLeft: 2 }}>+{task.assignees.length - 3}</span>}
                  </div>
                </td>
                <td style={{ padding: "12px", fontSize: 12, color: isOverdue ? "#ef4444" : "#94a3b8", fontWeight: isOverdue ? 600 : 400, whiteSpace: "nowrap" }}>
                  {task.dueDate ? new Date(task.dueDate).toLocaleDateString("es-MX", { day: "numeric", month: "short" }) : "—"}
                  {isOverdue && " ⚠️"}
                </td>
                <td style={{ padding: "12px" }}>
                  {(() => {
                    const { isDone: d, elapsed: e } = getTimeInfo(task, board);
                    return e ? (
                      <span style={{ fontSize: 11, color: d ? "#22c55e" : "#94a3b8", fontWeight: 500, whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: 3 }}>
                        {d ? "✅" : "⏱️"} {e}
                      </span>
                    ) : <span style={{ fontSize: 10, color: "#334155" }}>—</span>;
                  })()}
                </td>
                <td style={{ padding: "12px" }}>
                  {subTotal > 0 ? (
                    <div style={{ display: "flex", alignItems: "center", gap: 6, minWidth: 80 }}>
                      <div style={{ flex: 1, height: 4, background: "#334155", borderRadius: 2, overflow: "hidden" }}>
                        <div style={{ height: "100%", width: `${(subDone / subTotal) * 100}%`, background: subDone === subTotal ? "#22c55e" : "#6366f1", borderRadius: 2 }} />
                      </div>
                      <span style={{ fontSize: 10, color: "#64748b", whiteSpace: "nowrap" }}>{subDone}/{subTotal}</span>
                    </div>
                  ) : <span style={{ fontSize: 10, color: "#334155" }}>—</span>}
                </td>
                <td style={{ padding: "12px" }}>
                  <div style={{ display: "flex", gap: 4, flexWrap: "wrap" }}>
                    {task.tags?.slice(0, 2).map(tId => { const tag = data.tags.find(t => t.id === tId); return tag ? <Badge key={tId} color={tag.color} bg={tag.color + "18"} small>{tag.label}</Badge> : null; })}
                    {task.tags?.length > 2 && <Badge small>+{task.tags.length - 2}</Badge>}
                  </div>
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
      {tasks.length === 0 && <div style={{ padding: 40, textAlign: "center", color: "#475569" }}>No hay tareas que coincidan con los filtros</div>}
    </div>
  );
}

// ─── GANTT VIEW ───
function GanttView({ tasks, board, data, onTaskClick }) {
  const [ganttScale, setGanttScale] = useState("weeks"); // weeks | months

  const ganttData = useMemo(() => {
    const tasksWithDates = tasks.filter(t => t.dueDate || t.createdAt);
    if (tasksWithDates.length === 0) return null;

    const now = new Date();
    let minDate = new Date(now); minDate.setDate(minDate.getDate() - 14);
    let maxDate = new Date(now); maxDate.setDate(maxDate.getDate() + 60);

    tasksWithDates.forEach(t => {
      const start = new Date(t.createdAt || t.dueDate);
      const end = t.dueDate ? new Date(t.dueDate) : new Date(start.getTime() + 7 * 86400000);
      if (start < minDate) minDate = new Date(start.getTime() - 3 * 86400000);
      if (end > maxDate) maxDate = new Date(end.getTime() + 7 * 86400000);
    });

    // Generate day columns
    const days = [];
    const d = new Date(minDate);
    d.setHours(0, 0, 0, 0);
    while (d <= maxDate) {
      days.push(new Date(d));
      d.setDate(d.getDate() + 1);
    }

    // Group by weeks/months for header
    const groups = [];
    let lastKey = "";
    days.forEach((day, i) => {
      const key = ganttScale === "weeks"
        ? `S${getWeekNumber(day)} - ${day.toLocaleDateString("es-MX", { month: "short" })}`
        : day.toLocaleDateString("es-MX", { month: "long", year: "numeric" });
      if (key !== lastKey) {
        groups.push({ key, startIdx: i, count: 1 });
        lastKey = key;
      } else {
        groups[groups.length - 1].count++;
      }
    });

    return { days, groups, minDate, maxDate, tasksWithDates };
  }, [tasks, ganttScale]);

  if (!ganttData || ganttData.tasksWithDates.length === 0) {
    return (
      <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: 300, flexDirection: "column", gap: 12 }}>
        <div style={{ fontSize: 40, opacity: 0.3 }}>📊</div>
        <div style={{ fontSize: 14, color: "#64748b" }}>Agrega fechas a las tareas para ver el diagrama de Gantt</div>
      </div>
    );
  }

  const { days, groups, tasksWithDates } = ganttData;
  const dayWidth = ganttScale === "weeks" ? 32 : 14;
  const rowHeight = 44;
  const headerHeight = 56;
  const labelWidth = 260;
  const todayIdx = days.findIndex(d => d.toDateString() === new Date().toDateString());

  return (
    <div>
      {/* Scale toggle */}
      <div style={{ display: "flex", gap: 6, marginBottom: 14, alignItems: "center" }}>
        <span style={{ fontSize: 11, color: "#64748b", fontWeight: 600, textTransform: "uppercase", marginRight: 4 }}>Escala:</span>
        {[{ key: "weeks", label: "Semanas" }, { key: "months", label: "Meses" }].map(s => (
          <button key={s.key} onClick={() => setGanttScale(s.key)} style={{
            padding: "5px 12px", borderRadius: 6, border: `1px solid ${ganttScale === s.key ? "#6366f1" : "#334155"}`,
            background: ganttScale === s.key ? "#6366f1" + "20" : "transparent",
            color: ganttScale === s.key ? "#a5b4fc" : "#64748b",
            fontSize: 11, fontWeight: 600, cursor: "pointer",
          }}>{s.label}</button>
        ))}
        <div style={{ marginLeft: "auto", display: "flex", gap: 12 }}>
          <span style={{ fontSize: 11, color: "#64748b", display: "flex", alignItems: "center", gap: 4 }}>
            <span style={{ width: 8, height: 8, borderRadius: 2, background: "#6366f1", display: "inline-block" }} /> En progreso
          </span>
          <span style={{ fontSize: 11, color: "#64748b", display: "flex", alignItems: "center", gap: 4 }}>
            <span style={{ width: 8, height: 8, borderRadius: 2, background: "#22c55e", display: "inline-block" }} /> Completado
          </span>
          <span style={{ fontSize: 11, color: "#64748b", display: "flex", alignItems: "center", gap: 4 }}>
            <span style={{ width: 8, height: 8, borderRadius: 2, background: "#ef4444", display: "inline-block" }} /> Vencido
          </span>
        </div>
      </div>

      <div style={{ display: "flex", border: "1px solid #334155", borderRadius: 12, overflow: "hidden", background: "#1e293b" }}>
        {/* Left: task labels */}
        <div style={{ width: labelWidth, flexShrink: 0, borderRight: "2px solid #334155", zIndex: 2, background: "#1e293b" }}>
          <div style={{ height: headerHeight, padding: "0 14px", display: "flex", alignItems: "center", borderBottom: "2px solid #334155" }}>
            <span style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase" }}>Tarea</span>
          </div>
          {tasksWithDates.map((task, i) => {
            const col = board.columns.find(c => c.id === task.status);
            return (
              <div key={task.id} onClick={() => onTaskClick(task)} style={{
                height: rowHeight, padding: "0 14px", display: "flex", alignItems: "center", gap: 8,
                borderBottom: "1px solid #0f172a", cursor: "pointer",
                background: i % 2 === 0 ? "transparent" : "rgba(0,0,0,0.15)",
              }}
                onMouseEnter={e => e.currentTarget.style.background = "rgba(99,102,241,0.08)"}
                onMouseLeave={e => e.currentTarget.style.background = i % 2 === 0 ? "transparent" : "rgba(0,0,0,0.15)"}>
                <div style={{ width: 8, height: 8, borderRadius: 2, background: col?.color || "#64748b", flexShrink: 0 }} />
                <div style={{ overflow: "hidden", flex: 1 }}>
                  <div style={{ fontSize: 12, fontWeight: 600, color: "#e2e8f0", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{task.title}</div>
                  <div style={{ fontSize: 10, color: "#475569" }}>
                    {task.assignees?.slice(0, 2).map(aId => data.members.find(m => m.id === aId)?.name?.split(" ")[0]).filter(Boolean).join(", ") || "Sin asignar"}
                  </div>
                </div>
                <Badge color={PRIORITY_CONFIG[task.priority]?.color} bg={PRIORITY_CONFIG[task.priority]?.bg} small>
                  {PRIORITY_CONFIG[task.priority]?.icon}
                </Badge>
              </div>
            );
          })}
        </div>

        {/* Right: timeline */}
        <div style={{ flex: 1, overflowX: "auto" }}>
          <div style={{ minWidth: days.length * dayWidth, position: "relative" }}>
            {/* Header row 1 - groups */}
            <div style={{ height: 28, display: "flex", borderBottom: "1px solid #334155" }}>
              {groups.map((g, i) => (
                <div key={i} style={{
                  width: g.count * dayWidth, flexShrink: 0,
                  display: "flex", alignItems: "center", justifyContent: "center",
                  fontSize: 11, fontWeight: 700, color: "#94a3b8",
                  borderRight: "1px solid #334155", textTransform: "capitalize",
                }}>{g.key}</div>
              ))}
            </div>
            {/* Header row 2 - days */}
            <div style={{ height: 28, display: "flex", borderBottom: "2px solid #334155" }}>
              {days.map((day, i) => {
                const isToday = day.toDateString() === new Date().toDateString();
                const isWeekend = day.getDay() === 0 || day.getDay() === 6;
                return (
                  <div key={i} style={{
                    width: dayWidth, flexShrink: 0,
                    display: "flex", alignItems: "center", justifyContent: "center",
                    fontSize: 9, fontWeight: isToday ? 700 : 400,
                    color: isToday ? "#6366f1" : isWeekend ? "#334155" : "#475569",
                    background: isToday ? "#6366f1" + "15" : "transparent",
                    borderRight: "1px solid #1e293b20",
                  }}>{day.getDate()}</div>
                );
              })}
            </div>

            {/* Task rows */}
            {tasksWithDates.map((task, rowIdx) => {
              const start = new Date(task.createdAt || task.dueDate);
              const end = task.dueDate ? new Date(task.dueDate) : new Date(start.getTime() + 7 * 86400000);
              const isLastCol = task.status === board.columns[board.columns.length - 1]?.id;
              const isOverdue = task.dueDate && new Date(task.dueDate) < new Date() && !isLastCol;
              const col = board.columns.find(c => c.id === task.status);

              const startIdx = days.findIndex(d => d.toDateString() === new Date(start.getFullYear(), start.getMonth(), start.getDate()).toDateString());
              const endIdx = days.findIndex(d => d.toDateString() === new Date(end.getFullYear(), end.getMonth(), end.getDate()).toDateString());
              const barStart = Math.max(0, startIdx === -1 ? 0 : startIdx);
              const barEnd = endIdx === -1 ? days.length - 1 : endIdx;
              const barLeft = barStart * dayWidth;
              const barWidth = Math.max((barEnd - barStart + 1) * dayWidth - 6, dayWidth);

              let barColor = col?.color || "#6366f1";
              if (isLastCol) barColor = "#22c55e";
              if (isOverdue) barColor = "#ef4444";

              const subProgress = task.subtasks?.length > 0 ? task.subtasks.filter(s => s.done).length / task.subtasks.length : null;

              return (
                <div key={task.id} style={{
                  height: rowHeight, position: "relative",
                  borderBottom: "1px solid #0f172a",
                  background: rowIdx % 2 === 0 ? "transparent" : "rgba(0,0,0,0.15)",
                }}>
                  {/* Today marker */}
                  {todayIdx >= 0 && (
                    <div style={{
                      position: "absolute", left: todayIdx * dayWidth + dayWidth / 2 - 1, top: 0,
                      width: 2, height: "100%", background: "#6366f1", opacity: 0.3, zIndex: 0,
                    }} />
                  )}
                  {/* Bar */}
                  <div onClick={() => onTaskClick(task)} style={{
                    position: "absolute", left: barLeft + 3, top: 8,
                    width: barWidth, height: rowHeight - 16,
                    background: barColor + "30", border: `1.5px solid ${barColor}`,
                    borderRadius: 6, cursor: "pointer",
                    display: "flex", alignItems: "center", overflow: "hidden",
                    transition: "all 0.15s", zIndex: 1,
                  }}
                    onMouseEnter={e => { e.currentTarget.style.background = barColor + "50"; e.currentTarget.style.transform = "scaleY(1.08)"; }}
                    onMouseLeave={e => { e.currentTarget.style.background = barColor + "30"; e.currentTarget.style.transform = "none"; }}>
                    {/* Progress fill */}
                    {subProgress !== null && (
                      <div style={{
                        position: "absolute", left: 0, top: 0, height: "100%",
                        width: `${subProgress * 100}%`, background: barColor + "25",
                        borderRadius: "5px 0 0 5px",
                      }} />
                    )}
                    {barWidth > 60 && (
                      <span style={{ fontSize: 10, fontWeight: 600, color: "#e2e8f0", padding: "0 8px", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", position: "relative", zIndex: 1 }}>
                        {task.title}
                      </span>
                    )}
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </div>
  );
}

function getWeekNumber(date) {
  const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
  const dayNum = d.getUTCDay() || 7;
  d.setUTCDate(d.getUTCDate() + 4 - dayNum);
  const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
  return Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
}

// ─── TASK CARD ───
function TaskCard({ task, members, tags, board, onDragStart, onDragEnd, onClick }) {
  const isOverdue = task.dueDate && new Date(task.dueDate) < new Date();
  const subtaskProgress = task.subtasks?.length > 0 ? task.subtasks.filter(s => s.done).length / task.subtasks.length : null;
  const { isDone, elapsed } = getTimeInfo(task, board);
  return (
    <div draggable onDragStart={e => onDragStart(e, task.id)} onDragEnd={onDragEnd} onClick={onClick}
      style={{ background: "#0f172a", borderRadius: 10, padding: "12px 14px", marginBottom: 8, cursor: "grab", border: isDone ? "1px solid #22c55e40" : "1px solid #334155", transition: "all 0.15s", animation: "slideIn 0.2s ease" }}
      onMouseEnter={e => { e.currentTarget.style.borderColor = "#6366f1"; e.currentTarget.style.transform = "translateY(-1px)"; e.currentTarget.style.boxShadow = "0 4px 12px rgba(0,0,0,0.3)"; }}
      onMouseLeave={e => { e.currentTarget.style.borderColor = isDone ? "#22c55e40" : "#334155"; e.currentTarget.style.transform = "none"; e.currentTarget.style.boxShadow = "none"; }}>
      {task.tags?.length > 0 && (
        <div style={{ display: "flex", gap: 4, flexWrap: "wrap", marginBottom: 8 }}>
          {task.tags.map(tId => { const tag = tags.find(t => t.id === tId); return tag ? <span key={tId} style={{ fontSize: 10, fontWeight: 600, color: tag.color, background: tag.color + "18", padding: "1px 6px", borderRadius: 3 }}>{tag.label}</span> : null; })}
        </div>
      )}
      <div style={{ fontSize: 13, fontWeight: 600, color: isDone ? "#94a3b8" : "#f8fafc", marginBottom: 8, lineHeight: 1.4, textDecoration: isDone ? "line-through" : "none" }}>{task.title}</div>
      {subtaskProgress !== null && (
        <div style={{ marginBottom: 8 }}>
          <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4 }}>
            <span style={{ fontSize: 10, color: "#64748b" }}>Subtareas</span>
            <span style={{ fontSize: 10, color: "#94a3b8", fontWeight: 600 }}>{task.subtasks.filter(s => s.done).length}/{task.subtasks.length}</span>
          </div>
          <div style={{ height: 3, background: "#334155", borderRadius: 2, overflow: "hidden" }}>
            <div style={{ height: "100%", width: `${subtaskProgress * 100}%`, background: isDone ? "#22c55e" : "#6366f1", borderRadius: 2, transition: "width 0.3s" }} />
          </div>
        </div>
      )}
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
          <Badge color={PRIORITY_CONFIG[task.priority]?.color} bg={PRIORITY_CONFIG[task.priority]?.bg} small>{PRIORITY_CONFIG[task.priority]?.icon}</Badge>
          {task.dueDate && <span style={{ fontSize: 11, color: isOverdue && !isDone ? "#ef4444" : "#64748b", fontWeight: isOverdue && !isDone ? 600 : 400 }}>📅 {task.dueDate.slice(5)}</span>}
          {task.comments?.length > 0 && <span style={{ fontSize: 11, color: "#64748b" }}>💬 {task.comments.length}</span>}
          {task.attachments?.length > 0 && <span style={{ fontSize: 11, color: "#64748b" }}>📎 {task.attachments.length}</span>}
          {elapsed && (
            <span style={{ fontSize: 10, color: isDone ? "#22c55e" : "#64748b", fontWeight: 500, display: "flex", alignItems: "center", gap: 2 }}>
              {isDone ? "✅" : "⏱️"} {elapsed}
            </span>
          )}
        </div>
        <div style={{ display: "flex", marginLeft: "auto" }}>
          {task.assignees?.slice(0, 3).map(aId => { const m = members.find(m => m.id === aId); return m ? <Avatar key={aId} member={m} size={22} /> : null; })}
          {task.assignees?.length > 3 && <span style={{ fontSize: 10, color: "#64748b", alignSelf: "center", marginLeft: 4 }}>+{task.assignees.length - 3}</span>}
        </div>
      </div>
    </div>
  );
}

// ─── TASK DETAIL MODAL ───
function TaskDetailModal({ task, board, data, onClose, onUpdate, onDelete, onAddComment }) {
  const [editTitle, setEditTitle] = useState(task.title);
  const [editDesc, setEditDesc] = useState(task.description || "");
  const [newComment, setNewComment] = useState("");
  const [editingTitle, setEditingTitle] = useState(false);
  const [editingDesc, setEditingDesc] = useState(false);
  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
  const [newSubtask, setNewSubtask] = useState("");
  const col = board?.columns?.find(c => c.id === task.status);
  const isAdmin = data.members.find(m => m.id === data.currentUser)?.role === "admin";
  const { isDone, elapsed } = getTimeInfo(task, board);
  const createdDate = task.createdAt ? new Date(task.createdAt).toLocaleDateString("es-MX", { day: "numeric", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit" }) : null;
  const completedDate = task.completedAt ? new Date(task.completedAt).toLocaleDateString("es-MX", { day: "numeric", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit" }) : null;

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.6)", zIndex: 1000, display: "flex", alignItems: "center", justifyContent: "center", animation: "fadeIn 0.15s ease", backdropFilter: "blur(4px)" }}>
      <div onClick={e => e.stopPropagation()} style={{ width: "90%", maxWidth: 700, maxHeight: "85vh", overflowY: "auto", background: "#1e293b", borderRadius: 16, border: "1px solid #334155", boxShadow: "0 25px 60px rgba(0,0,0,0.5)", animation: "slideIn 0.2s ease" }}>
        <div style={{ padding: "20px 24px 12px", borderBottom: "1px solid #334155", display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
          <div style={{ flex: 1 }}>
            {editingTitle ? (
              <input value={editTitle} onChange={e => setEditTitle(e.target.value)}
                onBlur={() => { onUpdate({ title: editTitle }); setEditingTitle(false); }}
                onKeyDown={e => { if (e.key === "Enter") { onUpdate({ title: editTitle }); setEditingTitle(false); } }}
                autoFocus style={{ width: "100%", fontSize: 18, fontWeight: 700, color: "#f8fafc", background: "transparent", border: "1px solid #6366f1", borderRadius: 6, padding: "4px 8px", outline: "none" }} />
            ) : (
              <h2 onClick={() => setEditingTitle(true)} style={{ fontSize: 18, fontWeight: 700, color: "#f8fafc", cursor: "pointer", margin: 0 }}>{task.title}</h2>
            )}
            <div style={{ display: "flex", gap: 8, marginTop: 10, flexWrap: "wrap" }}>
              <Badge color={col?.color} bg={col?.color + "20"}>{col?.title}</Badge>
              <Badge color={PRIORITY_CONFIG[task.priority]?.color} bg={PRIORITY_CONFIG[task.priority]?.bg}>{PRIORITY_CONFIG[task.priority]?.icon} {PRIORITY_CONFIG[task.priority]?.label}</Badge>
              {elapsed && (
                <Badge color={isDone ? "#22c55e" : "#94a3b8"} bg={isDone ? "#22c55e18" : "#94a3b818"}>
                  {isDone ? "✅" : "⏱️"} {isDone ? "Completada en " : "En progreso: "}{elapsed}
                </Badge>
              )}
            </div>
          </div>
          <div style={{ display: "flex", gap: 4 }}>
            {isAdmin && (showDeleteConfirm ? (
              <div style={{ display: "flex", gap: 4, alignItems: "center" }}>
                <span style={{ fontSize: 11, color: "#ef4444" }}>¿Segura?</span>
                <button onClick={onDelete} style={{ padding: "4px 10px", borderRadius: 6, border: "none", background: "#ef4444", color: "#fff", fontSize: 11, fontWeight: 600, cursor: "pointer" }}>Sí</button>
                <button onClick={() => setShowDeleteConfirm(false)} style={{ padding: "4px 10px", borderRadius: 6, border: "1px solid #334155", background: "transparent", color: "#94a3b8", fontSize: 11, cursor: "pointer" }}>No</button>
              </div>
            ) : <IconBtn onClick={() => setShowDeleteConfirm(true)} title="Eliminar" danger>🗑️</IconBtn>)}
            <IconBtn onClick={onClose}>✕</IconBtn>
          </div>
        </div>
        <div style={{ padding: "16px 24px 24px", display: "flex", flexDirection: "column", gap: 20 }}>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
            <div>
              <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Estado</label>
              <select value={task.status} onChange={e => onUpdate({ status: e.target.value })} style={{ width: "100%", padding: "8px 10px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 13, cursor: "pointer", outline: "none" }}>
                {board?.columns?.map(c => <option key={c.id} value={c.id}>{c.title}</option>)}
              </select>
            </div>
            <div>
              <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Prioridad</label>
              <select value={task.priority} onChange={e => onUpdate({ priority: e.target.value })} style={{ width: "100%", padding: "8px 10px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 13, cursor: "pointer", outline: "none" }}>
                {Object.entries(PRIORITY_CONFIG).map(([k, v]) => <option key={k} value={k}>{v.icon} {v.label}</option>)}
              </select>
            </div>
            <div>
              <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Fecha límite</label>
              <input type="date" value={task.dueDate || ""} onChange={e => onUpdate({ dueDate: e.target.value })} style={{ width: "100%", padding: "8px 10px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 13, outline: "none" }} />
            </div>
            <div>
              <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Asignados</label>
              <select onChange={e => { if (e.target.value && !task.assignees?.includes(e.target.value)) onUpdate({ assignees: [...(task.assignees || []), e.target.value] }); e.target.value = ""; }} style={{ width: "100%", padding: "8px 10px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 13, cursor: "pointer", outline: "none" }}>
                <option value="">+ Agregar</option>
                {data.members.filter(m => !task.assignees?.includes(m.id)).map(m => <option key={m.id} value={m.id}>{m.name}</option>)}
              </select>
              <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginTop: 8 }}>
                {task.assignees?.map(aId => { const m = data.members.find(m => m.id === aId); return m ? <span key={aId} onClick={() => onUpdate({ assignees: task.assignees.filter(a => a !== aId) })} style={{ display: "inline-flex", alignItems: "center", gap: 4, padding: "3px 8px", borderRadius: 6, background: "#0f172a", fontSize: 11, color: "#e2e8f0", cursor: "pointer", border: "1px solid #334155" }}><Avatar member={m} size={16} />{m.name} ✕</span> : null; })}
              </div>
            </div>
          </div>
          {/* Time tracking */}
          <div style={{ display: "flex", gap: 12, padding: "12px 14px", borderRadius: 10, background: "#0f172a", border: "1px solid #334155" }}>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 4 }}>Creada</div>
              <div style={{ fontSize: 12, color: "#94a3b8" }}>{createdDate || "—"}</div>
            </div>
            {isDone && completedDate && (
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 4 }}>Completada</div>
                <div style={{ fontSize: 12, color: "#22c55e" }}>{completedDate}</div>
              </div>
            )}
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 10, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 4 }}>{isDone ? "Tiempo total" : "Tiempo transcurrido"}</div>
              <div style={{ fontSize: 14, fontWeight: 700, color: isDone ? "#22c55e" : "#e2e8f0", display: "flex", alignItems: "center", gap: 4 }}>
                {isDone ? "✅" : "⏱️"} {elapsed || "—"}
              </div>
              <div style={{ fontSize: 9, color: "#475569", marginTop: 2 }}>Días hábiles (sin fines de semana ni festivos MX)</div>
            </div>
          </div>
          <div>
            <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Etiquetas</label>
            <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
              {data.tags.map(tag => {
                const active = task.tags?.includes(tag.id);
                return <span key={tag.id} onClick={() => { const newTags = active ? task.tags.filter(t => t !== tag.id) : [...(task.tags || []), tag.id]; onUpdate({ tags: newTags }); }} style={{ padding: "4px 10px", borderRadius: 6, fontSize: 11, fontWeight: 600, cursor: "pointer", transition: "all 0.15s", color: active ? "#fff" : tag.color, background: active ? tag.color : tag.color + "18", border: `1px solid ${active ? tag.color : "transparent"}` }}>{tag.label}</span>;
              })}
            </div>
          </div>
          <div>
            <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Descripción</label>
            {editingDesc ? (
              <div>
                <textarea value={editDesc} onChange={e => setEditDesc(e.target.value)} rows={4} style={{ width: "100%", padding: "10px 12px", borderRadius: 8, border: "1px solid #6366f1", background: "#0f172a", color: "#e2e8f0", fontSize: 13, outline: "none", resize: "vertical" }} />
                <div style={{ display: "flex", gap: 6, marginTop: 6 }}>
                  <button onClick={() => { onUpdate({ description: editDesc }); setEditingDesc(false); }} style={{ padding: "6px 14px", borderRadius: 6, border: "none", background: "#6366f1", color: "#fff", fontSize: 12, fontWeight: 600, cursor: "pointer" }}>Guardar</button>
                  <button onClick={() => setEditingDesc(false)} style={{ padding: "6px 14px", borderRadius: 6, border: "1px solid #334155", background: "transparent", color: "#94a3b8", fontSize: 12, cursor: "pointer" }}>Cancelar</button>
                </div>
              </div>
            ) : (
              <div onClick={() => setEditingDesc(true)} style={{ padding: "10px 12px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: task.description ? "#e2e8f0" : "#475569", fontSize: 13, cursor: "pointer", minHeight: 60, lineHeight: 1.5, whiteSpace: "pre-wrap" }}>
                {task.description || "Clic para agregar descripción..."}
              </div>
            )}
          </div>
          <div>
            <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 6, display: "block" }}>Subtareas {task.subtasks?.length > 0 && `(${task.subtasks.filter(s => s.done).length}/${task.subtasks.length})`}</label>
            {task.subtasks?.map(st => (
              <div key={st.id} style={{ display: "flex", alignItems: "center", gap: 8, padding: "6px 0", borderBottom: "1px solid #1e293b" }}>
                <input type="checkbox" checked={st.done} onChange={() => { const newSub = task.subtasks.map(s => s.id === st.id ? { ...s, done: !s.done } : s); onUpdate({ subtasks: newSub }); }} style={{ cursor: "pointer", accentColor: "#6366f1" }} />
                <span style={{ fontSize: 13, color: st.done ? "#475569" : "#e2e8f0", textDecoration: st.done ? "line-through" : "none", flex: 1 }}>{st.title}</span>
                <IconBtn size={22} onClick={() => onUpdate({ subtasks: task.subtasks.filter(s => s.id !== st.id) })} danger>✕</IconBtn>
              </div>
            ))}
            <div style={{ display: "flex", gap: 6, marginTop: 8 }}>
              <input value={newSubtask} onChange={e => setNewSubtask(e.target.value)} placeholder="Nueva subtarea..."
                onKeyDown={e => { if (e.key === "Enter" && newSubtask.trim()) { onUpdate({ subtasks: [...(task.subtasks || []), { id: uid(), title: newSubtask.trim(), done: false }] }); setNewSubtask(""); } }}
                style={{ flex: 1, padding: "7px 10px", borderRadius: 6, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 12, outline: "none" }} />
              <button onClick={() => { if (newSubtask.trim()) { onUpdate({ subtasks: [...(task.subtasks || []), { id: uid(), title: newSubtask.trim(), done: false }] }); setNewSubtask(""); } }} style={{ padding: "7px 12px", borderRadius: 6, border: "none", background: "#6366f1", color: "#fff", fontSize: 12, fontWeight: 600, cursor: "pointer" }}>+</button>
            </div>
          </div>
          {/* Attachments */}
          <div>
            <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 8, display: "block" }}>
              Archivos adjuntos ({task.attachments?.length || 0})
              <span style={{ fontWeight: 400, textTransform: "none", marginLeft: 6 }}>— máx. 15 MB por archivo</span>
            </label>
            {task.attachments?.length > 0 && (
              <div style={{ display: "flex", flexDirection: "column", gap: 6, marginBottom: 10 }}>
                {task.attachments.map(att => (
                  <div key={att.id} style={{
                    display: "flex", alignItems: "center", gap: 10,
                    padding: "8px 12px", borderRadius: 8, background: "#0f172a",
                    border: "1px solid #334155",
                  }}>
                    <span style={{ fontSize: 22, flexShrink: 0 }}>{getFileIcon(att.type)}</span>
                    <div style={{ flex: 1, overflow: "hidden" }}>
                      <div style={{ fontSize: 12, fontWeight: 600, color: "#e2e8f0", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{att.name}</div>
                      <div style={{ fontSize: 10, color: "#64748b" }}>
                        {formatFileSize(att.size)} · {new Date(att.uploadedAt).toLocaleDateString("es-MX", { day: "numeric", month: "short" })}
                        {att.uploadedBy && (() => { const u = data.members.find(m => m.id === att.uploadedBy); return u ? ` · ${u.name.split(" ")[0]}` : ""; })()}
                      </div>
                    </div>
                    <a href={att.dataUrl} download={att.name}
                      onClick={e => e.stopPropagation()}
                      style={{ padding: "4px 10px", borderRadius: 5, background: "#334155", color: "#e2e8f0", fontSize: 10, fontWeight: 600, textDecoration: "none", cursor: "pointer", flexShrink: 0 }}>
                      ⬇ Descargar
                    </a>
                    <IconBtn size={24} onClick={() => {
                      onUpdate({ attachments: task.attachments.filter(a => a.id !== att.id) });
                    }} danger>✕</IconBtn>
                  </div>
                ))}
              </div>
            )}
            <label style={{
              display: "flex", alignItems: "center", justifyContent: "center", gap: 8,
              padding: "14px", borderRadius: 8,
              border: "2px dashed #334155", cursor: "pointer",
              color: "#64748b", fontSize: 12, fontWeight: 500,
              transition: "all 0.15s",
            }}
              onMouseEnter={e => { e.currentTarget.style.borderColor = "#6366f1"; e.currentTarget.style.color = "#a5b4fc"; }}
              onMouseLeave={e => { e.currentTarget.style.borderColor = "#334155"; e.currentTarget.style.color = "#64748b"; }}
            >
              <span style={{ fontSize: 18 }}>📎</span>
              Arrastra o haz clic para adjuntar archivos
              <input type="file" multiple style={{ display: "none" }} onChange={e => {
                const files = Array.from(e.target.files || []);
                const MAX_SIZE = 15 * 1024 * 1024; // 15 MB
                const valid = [];
                const rejected = [];
                files.forEach(f => {
                  if (f.size > MAX_SIZE) rejected.push(f.name);
                  else valid.push(f);
                });
                if (rejected.length > 0) {
                  alert(`Archivos rechazados (>15 MB):\n${rejected.join("\n")}`);
                }
                if (valid.length === 0) return;
                // Read files as base64
                Promise.all(valid.map(f => new Promise((resolve) => {
                  const reader = new FileReader();
                  reader.onload = () => resolve({
                    id: uid(), name: f.name, size: f.size, type: f.type,
                    dataUrl: reader.result,
                    uploadedAt: new Date().toISOString(),
                    uploadedBy: data.currentUser,
                  });
                  reader.readAsDataURL(f);
                }))).then(newAttachments => {
                  onUpdate({ attachments: [...(task.attachments || []), ...newAttachments] });
                });
                e.target.value = "";
              }} />
            </label>
          </div>
          <div>
            <label style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 8, display: "block" }}>Comentarios ({task.comments?.length || 0})</label>
            {task.comments?.map(c => {
              const author = data.members.find(m => m.id === c.author);
              return (
                <div key={c.id} style={{ display: "flex", gap: 10, padding: "10px 0", borderBottom: "1px solid #1e293b" }}>
                  <Avatar member={author} size={28} />
                  <div style={{ flex: 1 }}>
                    <div style={{ display: "flex", gap: 8, alignItems: "baseline", marginBottom: 4 }}>
                      <span style={{ fontSize: 12, fontWeight: 600, color: "#e2e8f0" }}>{author?.name || "?"}</span>
                      <span style={{ fontSize: 10, color: "#475569" }}>{new Date(c.date).toLocaleDateString("es-MX", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" })}</span>
                    </div>
                    <div style={{ fontSize: 13, color: "#cbd5e1", lineHeight: 1.5 }}>{c.text}</div>
                  </div>
                </div>
              );
            })}
            <div style={{ display: "flex", gap: 8, marginTop: 10 }}>
              <input value={newComment} onChange={e => setNewComment(e.target.value)} placeholder="Escribe un comentario..."
                onKeyDown={e => { if (e.key === "Enter" && newComment.trim()) { onAddComment(newComment.trim()); setNewComment(""); } }}
                style={{ flex: 1, padding: "9px 12px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 13, outline: "none" }} />
              <button onClick={() => { if (newComment.trim()) { onAddComment(newComment.trim()); setNewComment(""); } }} style={{ padding: "9px 16px", borderRadius: 8, border: "none", background: "#6366f1", color: "#fff", fontSize: 13, fontWeight: 600, cursor: "pointer" }}>Enviar</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── NEW TASK MODAL ───
function NewTaskModal({ columnId, board, data, onClose, onSave }) {
  const [title, setTitle] = useState("");
  const [desc, setDesc] = useState("");
  const [priority, setPriority] = useState("medium");
  const [dueDate, setDueDate] = useState("");
  const [assignees, setAssignees] = useState([]);
  const [selectedTags, setSelectedTags] = useState([]);

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.6)", zIndex: 1000, display: "flex", alignItems: "center", justifyContent: "center", animation: "fadeIn 0.15s ease", backdropFilter: "blur(4px)" }}>
      <div onClick={e => e.stopPropagation()} style={{ width: "90%", maxWidth: 520, background: "#1e293b", borderRadius: 16, border: "1px solid #334155", boxShadow: "0 25px 60px rgba(0,0,0,0.5)", animation: "slideIn 0.2s ease" }}>
        <div style={{ padding: "20px 24px 12px", borderBottom: "1px solid #334155", display: "flex", justifyContent: "space-between" }}>
          <h3 style={{ fontSize: 16, fontWeight: 700, color: "#f8fafc", margin: 0 }}>Nueva tarea</h3>
          <IconBtn onClick={onClose}>✕</IconBtn>
        </div>
        <div style={{ padding: "16px 24px 24px", display: "flex", flexDirection: "column", gap: 14 }}>
          <input value={title} onChange={e => setTitle(e.target.value)} placeholder="Título de la tarea *" autoFocus style={{ width: "100%", padding: "10px 12px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#f8fafc", fontSize: 14, fontWeight: 600, outline: "none" }} />
          <textarea value={desc} onChange={e => setDesc(e.target.value)} placeholder="Descripción (opcional)" rows={3} style={{ width: "100%", padding: "10px 12px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 13, outline: "none", resize: "vertical" }} />
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
            <div>
              <label style={{ fontSize: 11, fontWeight: 600, color: "#64748b", marginBottom: 4, display: "block" }}>Prioridad</label>
              <select value={priority} onChange={e => setPriority(e.target.value)} style={{ width: "100%", padding: "8px 10px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 12, outline: "none" }}>
                {Object.entries(PRIORITY_CONFIG).map(([k, v]) => <option key={k} value={k}>{v.icon} {v.label}</option>)}
              </select>
            </div>
            <div>
              <label style={{ fontSize: 11, fontWeight: 600, color: "#64748b", marginBottom: 4, display: "block" }}>Fecha límite</label>
              <input type="date" value={dueDate} onChange={e => setDueDate(e.target.value)} style={{ width: "100%", padding: "8px 10px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 12, outline: "none" }} />
            </div>
          </div>
          <div>
            <label style={{ fontSize: 11, fontWeight: 600, color: "#64748b", marginBottom: 4, display: "block" }}>Asignados</label>
            <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
              {data.members.map(m => {
                const active = assignees.includes(m.id);
                return <span key={m.id} onClick={() => setAssignees(active ? assignees.filter(a => a !== m.id) : [...assignees, m.id])} style={{ display: "inline-flex", alignItems: "center", gap: 4, padding: "4px 8px", borderRadius: 6, cursor: "pointer", fontSize: 11, fontWeight: 500, transition: "all 0.15s", background: active ? "#6366f1" : "#0f172a", color: active ? "#fff" : "#94a3b8", border: `1px solid ${active ? "#6366f1" : "#334155"}` }}>
                  <Avatar member={m} size={16} />{m.name.split(" ")[0]}
                </span>;
              })}
            </div>
          </div>
          <div>
            <label style={{ fontSize: 11, fontWeight: 600, color: "#64748b", marginBottom: 4, display: "block" }}>Etiquetas</label>
            <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
              {data.tags.map(tag => {
                const active = selectedTags.includes(tag.id);
                return <span key={tag.id} onClick={() => setSelectedTags(active ? selectedTags.filter(t => t !== tag.id) : [...selectedTags, tag.id])} style={{ padding: "3px 8px", borderRadius: 5, fontSize: 11, fontWeight: 600, cursor: "pointer", color: active ? "#fff" : tag.color, background: active ? tag.color : tag.color + "15", border: `1px solid ${active ? tag.color : "transparent"}`, transition: "all 0.15s" }}>{tag.label}</span>;
              })}
            </div>
          </div>
          <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 6 }}>
            <button onClick={onClose} style={{ padding: "10px 20px", borderRadius: 8, border: "1px solid #334155", background: "transparent", color: "#94a3b8", fontSize: 13, cursor: "pointer" }}>Cancelar</button>
            <button onClick={() => { if (!title.trim()) return; onSave({ title: title.trim(), description: desc, priority, dueDate, assignees, tags: selectedTags }); }} disabled={!title.trim()} style={{ padding: "10px 24px", borderRadius: 8, border: "none", background: title.trim() ? "#6366f1" : "#334155", color: title.trim() ? "#fff" : "#64748b", fontSize: 13, fontWeight: 600, cursor: title.trim() ? "pointer" : "not-allowed" }}>Crear tarea</button>
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── NEW BOARD MODAL ───
function NewBoardModal({ onClose, onSave }) {
  const [name, setName] = useState("");
  const [flowType, setFlowType] = useState("tech");
  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.6)", zIndex: 1000, display: "flex", alignItems: "center", justifyContent: "center", animation: "fadeIn 0.15s ease", backdropFilter: "blur(4px)" }}>
      <div onClick={e => e.stopPropagation()} style={{ width: "90%", maxWidth: 440, background: "#1e293b", borderRadius: 16, border: "1px solid #334155", boxShadow: "0 25px 60px rgba(0,0,0,0.5)", animation: "slideIn 0.2s ease" }}>
        <div style={{ padding: "20px 24px 12px", borderBottom: "1px solid #334155" }}>
          <h3 style={{ fontSize: 16, fontWeight: 700, color: "#f8fafc", margin: 0 }}>Nuevo tablero</h3>
        </div>
        <div style={{ padding: "16px 24px 24px", display: "flex", flexDirection: "column", gap: 14 }}>
          <input value={name} onChange={e => setName(e.target.value)} placeholder="Nombre del tablero" autoFocus style={{ width: "100%", padding: "10px 12px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#f8fafc", fontSize: 14, fontWeight: 600, outline: "none" }} />
          <div>
            <label style={{ fontSize: 11, fontWeight: 600, color: "#64748b", marginBottom: 6, display: "block" }}>Tipo de flujo</label>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
              {Object.entries(FLOW_TYPES).map(([key, ft]) => (
                <div key={key} onClick={() => setFlowType(key)} style={{ padding: "12px", borderRadius: 10, cursor: "pointer", border: `2px solid ${flowType === key ? ft.color : "#334155"}`, background: flowType === key ? ft.color + "15" : "#0f172a", transition: "all 0.15s" }}>
                  <div style={{ fontSize: 20, marginBottom: 4 }}>{ft.icon}</div>
                  <div style={{ fontSize: 12, fontWeight: 600, color: "#e2e8f0" }}>{ft.label}</div>
                  <div style={{ fontSize: 10, color: "#64748b", marginTop: 2 }}>{DEFAULT_COLUMNS[key].length} columnas</div>
                </div>
              ))}
            </div>
          </div>
          <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 6 }}>
            <button onClick={onClose} style={{ padding: "10px 20px", borderRadius: 8, border: "1px solid #334155", background: "transparent", color: "#94a3b8", fontSize: 13, cursor: "pointer" }}>Cancelar</button>
            <button onClick={() => { if (!name.trim()) return; onSave({ name: name.trim(), flowType }); }} disabled={!name.trim()} style={{ padding: "10px 24px", borderRadius: 8, border: "none", background: name.trim() ? "#6366f1" : "#334155", color: name.trim() ? "#fff" : "#64748b", fontSize: 13, fontWeight: 600, cursor: name.trim() ? "pointer" : "not-allowed" }}>Crear tablero</button>
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── TICKET FORM PREVIEW ───
function TicketFormPreview({ wsId, data, onClose }) {
  const ws = data.workspaces.find(w => w.id === wsId);
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [title, setTitle] = useState("");
  const [desc, setDesc] = useState("");
  const [priority, setPriority] = useState("medium");
  const [category, setCategory] = useState("");
  const [sede, setSede] = useState("");
  const [submitted, setSubmitted] = useState(null);

  if (!ws) return null;

  const categories = ws.ticketCategories || [];
  const sedes = ws.ticketSedes || [];
  const members = data.members.filter(m => ws.memberIds?.includes(m.id));

  const PRIO_OPTS = [
    { value: "low", label: "Baja", icon: "🟢", desc: "No es urgente, puede esperar" },
    { value: "medium", label: "Media", icon: "🟡", desc: "Atención en los próximos días" },
    { value: "high", label: "Alta", icon: "🟠", desc: "Necesita atención pronto" },
    { value: "urgent", label: "Urgente", icon: "🔴", desc: "Requiere atención inmediata" },
  ];

  const handleSubmit = () => {
    if (!name.trim() || !email.trim() || !title.trim()) return;
    const folio = `TKT-${String(Math.floor(Math.random() * 9000) + 1000).padStart(4, "0")}`;
    const queuePos = Math.floor(Math.random() * 5) + 1;
    let assigneeName = null;
    if (ws.ticketAssignMode === "manual" && ws.ticketAssigneeId) {
      assigneeName = data.members.find(m => m.id === ws.ticketAssigneeId)?.name;
    } else if (ws.ticketAssignMode === "rotation" && members.length > 0) {
      assigneeName = members[Math.floor(Math.random() * members.length)].name;
    }
    setSubmitted({ folio, queuePos, title: title.trim(), assigneeName, sede: sede || null });
  };

  const S = {
    label: { fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", letterSpacing: "0.05em", display: "block", marginBottom: 6 },
    input: { width: "100%", padding: "11px 14px", borderRadius: 10, border: "1px solid #334155", background: "#0f172a", color: "#f8fafc", fontSize: 14, outline: "none", boxSizing: "border-box" },
    textarea: { width: "100%", padding: "11px 14px", borderRadius: 10, border: "1px solid #334155", background: "#0f172a", color: "#f8fafc", fontSize: 14, outline: "none", resize: "vertical", minHeight: 100, boxSizing: "border-box", fontFamily: "inherit" },
    select: { width: "100%", padding: "11px 14px", borderRadius: 10, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 14, outline: "none", cursor: "pointer" },
  };

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "linear-gradient(135deg, #0f172a 0%, #1e1b4b 40%, #0f172a 100%)", zIndex: 2000, display: "flex", alignItems: "center", justifyContent: "center", padding: 20, overflow: "auto" }}>
      <div onClick={e => e.stopPropagation()} style={{ width: "100%", maxWidth: 540, background: "#1e293b", borderRadius: 20, border: "1px solid #334155", boxShadow: "0 25px 60px rgba(0,0,0,0.5)", overflow: "hidden" }}>
        {/* Demo banner */}
        <div style={{ padding: "8px 16px", background: "#f59e0b20", borderBottom: "1px solid #f59e0b40", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
          <span style={{ fontSize: 12, fontWeight: 600, color: "#f59e0b" }}>👁️ Vista previa — Este formulario no envía datos reales</span>
          <button onClick={onClose} style={{ background: "none", border: "none", color: "#f59e0b", cursor: "pointer", fontSize: 16 }}>✕</button>
        </div>

        {submitted ? (
          /* SUCCESS */
          <div style={{ padding: "40px 32px", textAlign: "center" }}>
            <div style={{ fontSize: 56, marginBottom: 16 }}>✅</div>
            <h2 style={{ fontSize: 22, fontWeight: 700, color: "#f8fafc", margin: "0 0 8px" }}>¡Ticket registrado!</h2>
            <p style={{ color: "#94a3b8", fontSize: 14, margin: "0 0 24px" }}>Tu solicitud fue recibida exitosamente.</p>
            <div style={{ background: "#0f172a", borderRadius: 12, padding: 20, marginBottom: 20 }}>
              <div style={{ fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 8 }}>Número de folio</div>
              <div style={{ fontSize: 32, fontWeight: 800, color: "#6366f1" }}>{submitted.folio}</div>
              <div style={{ marginTop: 12, fontSize: 11, fontWeight: 700, color: "#64748b", textTransform: "uppercase", marginBottom: 4 }}>Posición en cola</div>
              <div style={{ fontSize: 20, fontWeight: 700, color: "#f59e0b" }}>#{submitted.queuePos}</div>
            </div>
            <div style={{ background: "#0f172a", borderRadius: 12, padding: 16, marginBottom: 20, textAlign: "left" }}>
              <div style={{ fontSize: 13, color: "#e2e8f0", fontWeight: 600 }}>{submitted.title}</div>
              {submitted.assigneeName && <div style={{ fontSize: 12, color: "#64748b", marginTop: 4 }}>Asignado a: {submitted.assigneeName}</div>}
              {submitted.sede && <div style={{ fontSize: 12, color: "#64748b", marginTop: 2 }}>📍 Sede: {submitted.sede}</div>}
            </div>
            <p style={{ fontSize: 13, color: "#64748b", lineHeight: 1.6 }}>
              📧 El solicitante recibiría un correo de confirmación con el folio y posición en cola.
            </p>
            <button onClick={() => { setSubmitted(null); setTitle(""); setDesc(""); setPriority("medium"); setCategory(""); setSede(""); }}
              style={{ padding: "12px 24px", borderRadius: 10, border: "none", background: "#334155", color: "#e2e8f0", fontSize: 14, fontWeight: 600, cursor: "pointer", marginTop: 16 }}>
              Enviar otro ticket
            </button>
          </div>
        ) : (
          /* FORM */
          <>
            <div style={{ padding: "28px 32px 20px", borderBottom: "1px solid #334155", textAlign: "center" }}>
              <div style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", width: 48, height: 48, borderRadius: 12, background: ws.color || "#6366f1", fontSize: 24, marginBottom: 12 }}>{ws.icon}</div>
              <h1 style={{ fontSize: 20, fontWeight: 700, color: "#f8fafc", margin: "0 0 4px" }}>Nueva solicitud</h1>
              <p style={{ fontSize: 13, color: "#64748b", margin: 0 }}>{ws.name} — Universidad Santander</p>
            </div>
            <div style={{ padding: "24px 32px 32px" }}>
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 18 }}>
                <div><label style={S.label}>Tu nombre *</label><input value={name} onChange={e => setName(e.target.value)} placeholder="Nombre completo" style={S.input} /></div>
                <div><label style={S.label}>Tu correo *</label><input type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="correo@ejemplo.com" style={S.input} /></div>
              </div>
              <div style={{ marginBottom: 18 }}><label style={S.label}>Asunto de la solicitud *</label><input value={title} onChange={e => setTitle(e.target.value)} placeholder="Describe brevemente tu solicitud" style={S.input} /></div>
              <div style={{ marginBottom: 18 }}><label style={S.label}>Descripción</label><textarea value={desc} onChange={e => setDesc(e.target.value)} placeholder="Detalla tu solicitud..." style={S.textarea} /></div>
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 18 }}>
                <div>
                  <label style={S.label}>Prioridad</label>
                  <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                    {PRIO_OPTS.map(po => (
                      <div key={po.value} onClick={() => setPriority(po.value)} style={{
                        padding: "8px 12px", borderRadius: 8, cursor: "pointer",
                        border: `2px solid ${priority === po.value ? "#6366f1" : "#334155"}`,
                        background: priority === po.value ? "#6366f115" : "#0f172a",
                        display: "flex", alignItems: "center", gap: 8,
                      }}>
                        <span style={{ fontSize: 14 }}>{po.icon}</span>
                        <div>
                          <div style={{ fontSize: 12, fontWeight: 600, color: priority === po.value ? "#e2e8f0" : "#94a3b8" }}>{po.label}</div>
                          <div style={{ fontSize: 10, color: "#475569" }}>{po.desc}</div>
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
                <div>
                  {categories.length > 0 && (
                    <div style={{ marginBottom: 16 }}>
                      <label style={S.label}>Categoría</label>
                      <select value={category} onChange={e => setCategory(e.target.value)} style={S.select}>
                        <option value="">Seleccionar...</option>
                        {categories.map(c => <option key={c} value={c}>{c}</option>)}
                      </select>
                    </div>
                  )}
                  {sedes.length > 0 && (
                    <div style={{ marginBottom: 16 }}>
                      <label style={S.label}>Sede</label>
                      <select value={sede} onChange={e => setSede(e.target.value)} style={S.select}>
                        <option value="">Seleccionar sede...</option>
                        {sedes.map(s => <option key={s} value={s}>{s}</option>)}
                      </select>
                    </div>
                  )}
                  <div style={{ padding: "12px", borderRadius: 8, background: "#0f172a", border: "1px solid #334155", marginBottom: 16 }}>
                    <div style={{ fontSize: 11, fontWeight: 600, color: "#64748b" }}>
                      {ws.ticketAssignMode === "rotation" ? "🔄 Asignación por rotación" : "👤 Asignación fija"}
                    </div>
                    <div style={{ fontSize: 10, color: "#475569", marginTop: 4 }}>
                      {ws.ticketAssignMode === "rotation"
                        ? "El ticket se asignará automáticamente al siguiente miembro disponible."
                        : `Todos los tickets se asignan a: ${data.members.find(m => m.id === ws.ticketAssigneeId)?.name || "Sin definir"}`}
                    </div>
                  </div>
                  <div style={{ padding: "12px", borderRadius: 8, background: "#0f172a", border: "1px solid #334155" }}>
                    <div style={{ fontSize: 11, fontWeight: 600, color: "#64748b", marginBottom: 6 }}>📎 Archivos adjuntos</div>
                    <div style={{ padding: "16px", border: "2px dashed #334155", borderRadius: 8, textAlign: "center", color: "#475569", fontSize: 11 }}>
                      Arrastra o haz clic<br />(máx. 15 MB)
                    </div>
                  </div>
                </div>
              </div>
              <button onClick={handleSubmit} disabled={!name.trim() || !email.trim() || !title.trim()}
                style={{ width: "100%", padding: "14px", borderRadius: 10, border: "none", background: name.trim() && email.trim() && title.trim() ? "linear-gradient(135deg, #6366f1, #4f46e5)" : "#334155", color: name.trim() && email.trim() && title.trim() ? "#fff" : "#64748b", fontSize: 15, fontWeight: 700, cursor: name.trim() && email.trim() && title.trim() ? "pointer" : "not-allowed" }}>
                📨 Enviar solicitud
              </button>
              <p style={{ fontSize: 11, color: "#475569", textAlign: "center", marginTop: 16, lineHeight: 1.5 }}>
                📧 Al enviar, el solicitante recibe correo de confirmación con folio y posición en cola.
                Se notifica por correo ante cambios de estado, comentarios y resolución.
              </p>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

// ─── ACCESS GATE ───
// Shown before the board loads. Verifies the code against the server (POST-less: it
// just tries a real /api/data fetch with the code attached, since that's the only
// protected route there is) and only then mounts the real app.
function AccessGate() {
  const [checking, setChecking] = useState(true);
  const [authed, setAuthed] = useState(false);
  const [code, setCode] = useState("");
  const [error, setError] = useState("");
  const [submitting, setSubmitting] = useState(false);

  useEffect(() => {
    (async () => {
      const stored = getAccessCode();
      if (stored) {
        const ok = await verifyAccessCode(stored);
        setAuthed(ok);
        if (!ok) clearAccessCode();
      }
      setChecking(false);
    })();
  }, []);

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!code.trim()) return;
    setSubmitting(true);
    setError("");
    const ok = await verifyAccessCode(code.trim());
    setSubmitting(false);
    if (ok) {
      setAccessCode(code.trim());
      setAuthed(true);
    } else {
      setError("Código incorrecto. Verifica con el administrador.");
    }
  };

  const handleLogout = () => {
    clearAccessCode();
    setAuthed(false);
    setCode("");
  };

  if (checking) {
    return (
      <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100vh", background: "#0f172a", color: "#e2e8f0", fontFamily: "'DM Sans', sans-serif" }}>
        <div style={{ fontSize: 40, animation: "pulse 1.5s infinite" }}>⚡</div>
      </div>
    );
  }

  if (authed) {
    return <KanbanApp onUnauthorized={() => setAuthed(false)} onLogout={handleLogout} />;
  }

  return (
    <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100vh", background: "#0f172a", color: "#e2e8f0", fontFamily: "'DM Sans', sans-serif" }}>
      <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap" rel="stylesheet" />
      <style>{`@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }`}</style>
      <form onSubmit={handleSubmit} style={{ width: 320, background: "#1e293b", border: "1px solid #334155", borderRadius: 16, padding: 32, textAlign: "center" }}>
        <div style={{ width: 48, height: 48, borderRadius: 10, background: "linear-gradient(135deg, #6366f1, #8b5cf6)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 24, fontWeight: 700, color: "#fff", margin: "0 auto 16px" }}>U</div>
        <div style={{ fontSize: 17, fontWeight: 700, marginBottom: 4 }}>UNISANT Kanban</div>
        <div style={{ fontSize: 12, color: "#64748b", marginBottom: 20 }}>Ingresa el código de acceso del equipo</div>
        <input
          type="password"
          autoFocus
          value={code}
          onChange={e => setCode(e.target.value)}
          placeholder="Código de acceso"
          style={{ width: "100%", padding: "11px 14px", borderRadius: 8, border: "1px solid #334155", background: "#0f172a", color: "#e2e8f0", fontSize: 14, outline: "none", marginBottom: 12, textAlign: "center" }}
        />
        {error && <div style={{ color: "#ef4444", fontSize: 12, marginBottom: 12 }}>{error}</div>}
        <button type="submit" disabled={submitting || !code.trim()} style={{ width: "100%", padding: "11px", borderRadius: 8, border: "none", background: submitting || !code.trim() ? "#334155" : "#6366f1", color: "#fff", fontSize: 14, fontWeight: 700, cursor: submitting || !code.trim() ? "not-allowed" : "pointer" }}>
          {submitting ? "Verificando..." : "Entrar"}
        </button>
      </form>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<AccessGate />);
