"use client";

import { useState, useEffect, useCallback, useRef, useMemo } from "react";
import { motion, AnimatePresence } from "framer-motion";
import {
  FileText, FileImage, FileVideo, FileAudio, File, Archive,
  Star, Trash2, Search, Upload, Grid3X3, List,
  Tag, FolderOpen, Clock, Heart, Briefcase, Banknote,
  X, ChevronDown, SlidersHorizontal, Eye,
  Download, Sparkles, AlignLeft, CalendarDays, NotebookPen,
  AlarmClock, BookOpen, RotateCcw, AlertTriangle, Menu, Home,
} from "lucide-react";
import { DocFile, Category, Metadata } from "@/lib/types";

const ACCENT    = "#ff8091";
const TEXT      = "#1d1d1f";
const TEXT2     = "#6e6e73";
const GLASS     = "rgba(255,255,255,0.75)";
const GLASS_MD  = "rgba(255,255,255,0.65)";
const GLASS_SM  = "rgba(255,255,255,0.60)";
const BLUR      = "blur(32px) saturate(160%)";
const STAGGER_LIMIT = 30;

function formatSize(bytes: number) {
  if (bytes < 1024) return bytes + " B";
  if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
  return (bytes / (1024 * 1024)).toFixed(1) + " MB";
}

function formatDate(iso: string) {
  return new Date(iso).toLocaleDateString("de-AT", { day: "2-digit", month: "short", year: "numeric" });
}

function formatDocDate(iso: string | null | undefined) {
  if (!iso) return null;
  return new Date(iso).toLocaleDateString("de-AT", { day: "2-digit", month: "short", year: "numeric" });
}

const CATEGORY_ICONS: Record<string, React.ReactNode> = {
  briefcase:   <Briefcase size={14} />,
  user:        <Heart size={14} />,
  banknote:    <Banknote size={14} />,
  heart:       <Heart size={14} />,
  folder:      <FolderOpen size={14} />,
  "book-open": <BookOpen size={14} />,
};

function fristStatus(iso: string | null | undefined): "overdue" | "soon" | "ok" | null {
  if (!iso) return null;
  const days = Math.ceil((new Date(iso).getTime() - Date.now()) / 86_400_000);
  if (days < 0)  return "overdue";
  if (days <= 7) return "soon";
  return "ok";
}

const FRIST_STYLE = {
  overdue: { bg: "#FFF1F2", text: "#F43F5E", border: "#FECDD3", label: "Überfällig" },
  soon:    { bg: "#FFF7ED", text: "#F97316", border: "#FED7AA", label: "Bald fällig" },
  ok:      { bg: "#F0FDF4", text: "#22C55E", border: "#BBF7D0", label: "" },
};

function FileIcon({ type, size = 32 }: { type: string; size?: number }) {
  const s = { width: size, height: size };
  if (type.startsWith("image/"))                           return <FileImage style={s} className="text-purple-500" />;
  if (type.startsWith("video/"))                           return <FileVideo style={s} className="text-pink-500" />;
  if (type.startsWith("audio/"))                           return <FileAudio style={s} className="text-indigo-500" />;
  if (type === "application/pdf")                          return <FileText  style={s} className="text-red-500" />;
  if (type.includes("word") || type.includes("document")) return <AlignLeft style={s} className="text-blue-500" />;
  if (type.includes("sheet") || type.includes("excel"))   return <FileText  style={s} className="text-green-500" />;
  if (type.includes("zip") || type.includes("rar"))       return <Archive   style={s} className="text-yellow-600" />;
  if (type.includes("text"))                               return <FileText  style={s} className="text-gray-500" />;
  return <File style={s} className="text-gray-400" />;
}

function FileVisual({ file, size = 36 }: { file: DocFile; size?: number }) {
  if (file.hasThumbnail) {
    return <img src={`/api/preview/${file.id}?thumb=1`} alt="" loading="lazy" decoding="async" className="w-full h-full object-cover" />;
  }
  if (file.type.startsWith("image/")) {
    return <img src={`/api/preview/${file.id}`} alt="" loading="lazy" decoding="async" className="w-full h-full object-cover" />;
  }
  return <FileIcon type={file.type} size={size} />;
}

const TAG_COLORS = [
  { bg: "#EFF6FF", text: "#3B82F6", border: "#BFDBFE" },
  { bg: "#F0FDF4", text: "#22C55E", border: "#BBF7D0" },
  { bg: "#FFF7ED", text: "#F97316", border: "#FED7AA" },
  { bg: "#FDF4FF", text: "#A855F7", border: "#E9D5FF" },
  { bg: "#FFF1F2", text: "#F43F5E", border: "#FECDD3" },
];

function tagColor(tag: string) {
  let h = 0;
  for (let i = 0; i < tag.length; i++) h = (h * 31 + tag.charCodeAt(i)) >>> 0;
  return TAG_COLORS[h % TAG_COLORS.length];
}

function TagChips({ tags, onRemove, max = 99 }: { tags: string[]; onRemove?: (t: string) => void; max?: number }) {
  if (!tags?.length) return null;
  const visible = tags.slice(0, max);
  const hidden  = tags.length - visible.length;
  return (
    <div className="flex flex-wrap gap-1">
      {visible.map(tag => {
        const c = tagColor(tag);
        return (
          <span key={tag} className="inline-flex items-center gap-0.5 text-[10px] px-1.5 py-0.5 rounded-full border font-medium"
            style={{ background: c.bg, color: c.text, borderColor: c.border }}>
            {tag}
            {onRemove && (
              <button onClick={() => onRemove(tag)} className="ml-0.5 opacity-60 hover:opacity-100"><X size={8} /></button>
            )}
          </span>
        );
      })}
      {hidden > 0 && (
        <span className="text-[10px] px-1.5 py-0.5 rounded-full border" style={{ color: TEXT2, borderColor: "rgba(0,0,0,0.1)" }}>+{hidden}</span>
      )}
    </div>
  );
}

type ViewMode    = "grid" | "list";
type SidebarView = "all" | "recent" | "starred" | "fristen" | "trash" | string;

interface UploadModal {
  open: boolean;
  fileIds: string[];
  categoryId: string | null;
  documentDate: string;
  frist: string;
  fileCount: number;
}

interface ConfirmDialog {
  title: string;
  message: string;
  confirmLabel: string;
  destructive?: boolean;
  onConfirm: () => void;
}

interface Toast {
  id: number;
  message: string;
  kind: "info" | "error" | "success";
}

function useIsMobile() {
  const [mobile, setMobile] = useState(false);
  useEffect(() => {
    const mq = window.matchMedia("(max-width: 767px)");
    setMobile(mq.matches);
    const h = (e: MediaQueryListEvent) => setMobile(e.matches);
    mq.addEventListener("change", h);
    return () => mq.removeEventListener("change", h);
  }, []);
  return mobile;
}

export default function DocumentManager() {
  const [metadata,        setMetadata]        = useState<Metadata>({ files: {}, categories: [] });
  const [loading,         setLoading]         = useState(true);
  const [view,            setView]            = useState<ViewMode>("grid");
  const [sidebar,         setSidebar]         = useState<SidebarView>("all");
  const [search,          setSearch]          = useState("");
  const [sortBy,          setSortBy]          = useState<"date" | "name" | "size">("date");
  const [sortDesc,        setSortDesc]        = useState(true);
  const [dragging,        setDragging]        = useState(false);
  const [uploading,       setUploading]       = useState(false);
  const [focusedId,       setFocusedId]       = useState<string | null>(null);
  const [selectedIds,     setSelectedIds]     = useState<Set<string>>(new Set());
  const [lastClickedId,   setLastClickedId]   = useState<string | null>(null);
  const [contextMenu,     setContextMenu]     = useState<{ x: number; y: number; fileId: string } | null>(null);
  const [confirmDialog,   setConfirmDialog]   = useState<ConfirmDialog | null>(null);
  const [toasts,          setToasts]          = useState<Toast[]>([]);
  const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
  const [uploadModal,     setUploadModal]     = useState<UploadModal>({
    open: false, fileIds: [], categoryId: null, documentDate: "", frist: "", fileCount: 0,
  });
  const fileInputRef  = useRef<HTMLInputElement>(null);
  const searchRef     = useRef<HTMLInputElement>(null);
  const toastIdRef    = useRef(0);
  const isMobile      = useIsMobile();

  function showToast(message: string, kind: Toast["kind"] = "info") {
    const id = ++toastIdRef.current;
    setToasts(t => [...t, { id, message, kind }]);
    setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 4500);
  }

  const load = useCallback(async () => {
    try {
      const res = await fetch("/api/files");
      if (!res.ok) throw new Error("load failed");
      const data: Metadata = await res.json();
      setMetadata(data);
    } catch {
      showToast("Konnte Daten nicht laden", "error");
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => { load(); }, [load]);

  useEffect(() => {
    const h = () => setContextMenu(null);
    window.addEventListener("click", h);
    return () => window.removeEventListener("click", h);
  }, []);

  useEffect(() => {
    if (!isMobile) setMobileSidebarOpen(false);
  }, [isMobile]);

  const allFiles      = useMemo(() => Object.values(metadata.files), [metadata.files]);
  const liveFiles     = useMemo(() => allFiles.filter(f => !f.deletedAt), [allFiles]);
  const trashedFiles  = useMemo(() => allFiles.filter(f => !!f.deletedAt), [allFiles]);

  const countByCategory = useMemo(() => {
    const m = new Map<string, number>();
    for (const f of liveFiles) {
      if (f.categoryId) m.set(f.categoryId, (m.get(f.categoryId) ?? 0) + 1);
    }
    return m;
  }, [liveFiles]);

  const filtered = useMemo(() => {
    const base = sidebar === "trash" ? trashedFiles : liveFiles;
    const q = search.trim().toLowerCase();
    const list = base.filter(f => {
      if (q) {
        if (!f.name.toLowerCase().includes(q) && !(f.tags ?? []).some(t => t.toLowerCase().includes(q))) return false;
      }
      if (sidebar === "starred")  return f.starred;
      if (sidebar === "recent")   return Date.now() - new Date(f.uploadedAt).getTime() < 7 * 86400_000;
      if (sidebar === "fristen")  return !!f.frist;
      if (sidebar === "trash")    return true;
      if (sidebar !== "all")      return f.categoryId === sidebar;
      return true;
    });
    list.sort((a, b) => {
      if (sidebar === "fristen") {
        return new Date(a.frist!).getTime() - new Date(b.frist!).getTime();
      }
      let d = 0;
      if (sortBy === "date") d = new Date(a.uploadedAt).getTime() - new Date(b.uploadedAt).getTime();
      else if (sortBy === "name") d = a.name.localeCompare(b.name);
      else d = a.size - b.size;
      return sortDesc ? -d : d;
    });
    return list;
  }, [liveFiles, trashedFiles, sidebar, search, sortBy, sortDesc]);

  const updateLocal = useCallback((id: string, updates: Partial<DocFile>) => {
    setMetadata(m => {
      const cur = m.files[id]; if (!cur) return m;
      return { ...m, files: { ...m.files, [id]: { ...cur, ...updates } } };
    });
  }, []);
  const removeLocal = useCallback((id: string) => {
    setMetadata(m => {
      const f = { ...m.files }; delete f[id]; return { ...m, files: f };
    });
  }, []);
  const insertLocal = useCallback((file: DocFile) => {
    setMetadata(m => ({ ...m, files: { ...m.files, [file.id]: file } }));
  }, []);

  async function patch(id: string, updates: Partial<DocFile>, opts: { silent?: boolean } = {}) {
    const prev = metadata.files[id];
    if (!prev) return;
    updateLocal(id, updates);
    try {
      const res = await fetch("/api/files", {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ id, ...updates }),
      });
      if (!res.ok) throw new Error();
      const fresh: DocFile = await res.json();
      updateLocal(id, fresh);
    } catch {
      updateLocal(id, prev);
      if (!opts.silent) showToast("Speichern fehlgeschlagen", "error");
    }
  }

  async function softDelete(ids: string[]) {
    const stamp = new Date().toISOString();
    const snapshot = new Map(ids.map(id => [id, metadata.files[id]] as const));
    ids.forEach(id => updateLocal(id, { deletedAt: stamp }));
    if (focusedId && ids.includes(focusedId)) setFocusedId(null);
    setSelectedIds(new Set());
    try {
      await Promise.all(ids.map(id => fetch(`/api/files?id=${id}`, { method: "DELETE" })));
      showToast(ids.length === 1 ? "In Papierkorb verschoben" : `${ids.length} Dateien in Papierkorb`, "success");
    } catch {
      snapshot.forEach((f, id) => f && updateLocal(id, { deletedAt: f.deletedAt ?? null }));
      showToast("Löschen fehlgeschlagen", "error");
    }
  }

  async function restore(ids: string[]) {
    ids.forEach(id => updateLocal(id, { deletedAt: null }));
    setSelectedIds(new Set());
    try {
      await Promise.all(ids.map(id => fetch("/api/files", {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ id, deletedAt: null }),
      })));
      showToast("Wiederhergestellt", "success");
    } catch {
      showToast("Wiederherstellen fehlgeschlagen", "error");
      load();
    }
  }

  async function purge(ids: string[]) {
    ids.forEach(id => removeLocal(id));
    setSelectedIds(new Set());
    if (focusedId && ids.includes(focusedId)) setFocusedId(null);
    try {
      await Promise.all(ids.map(id => fetch(`/api/files?id=${id}&permanent=1`, { method: "DELETE" })));
      showToast("Endgültig gelöscht");
    } catch {
      showToast("Löschen fehlgeschlagen", "error");
      load();
    }
  }

  function confirmPurge(ids: string[]) {
    setConfirmDialog({
      title: "Endgültig löschen?",
      message: ids.length === 1
        ? "Diese Datei wird unwiderruflich gelöscht."
        : `${ids.length} Dateien werden unwiderruflich gelöscht.`,
      confirmLabel: "Endgültig löschen",
      destructive: true,
      onConfirm: () => { purge(ids); setConfirmDialog(null); },
    });
  }

  async function uploadFiles(fileList: FileList) {
    setUploading(true);
    const form = new FormData();
    Array.from(fileList).forEach(f => form.append("files", f));
    try {
      const res  = await fetch("/api/upload", { method: "POST", body: form });
      if (!res.ok) {
        showToast("Upload fehlgeschlagen", "error");
        return;
      }
      const data: { files: DocFile[]; rejected?: { name: string; reason: string }[] } = await res.json();
      data.files.forEach(insertLocal);
      if (data.rejected?.length) {
        data.rejected.forEach(r => showToast(`${r.name}: ${r.reason}`, "error"));
      }
      const ids = data.files.map(f => f.id);
      if (ids.length) {
        setUploadModal({ open: true, fileIds: ids, categoryId: null, documentDate: "", frist: "", fileCount: ids.length });
      }
    } catch {
      showToast("Upload fehlgeschlagen", "error");
    } finally {
      setUploading(false);
    }
  }

  async function confirmUploadModal() {
    const { fileIds, categoryId, documentDate, frist } = uploadModal;
    setUploadModal({ open: false, fileIds: [], categoryId: null, documentDate: "", frist: "", fileCount: 0 });
    await Promise.all(fileIds.map(id => patch(id, {
      categoryId,
      documentDate: documentDate || null,
      frist:        frist        || null,
    }, { silent: true })));
  }

  function downloadFile(file: DocFile) {
    const a   = document.createElement("a");
    a.href    = `/api/preview/${file.id}?download=1`;
    a.target  = "_blank";
    a.rel     = "noopener noreferrer";
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  }

  function openPreview(file: DocFile) {
    const a  = document.createElement("a");
    a.href   = `/api/preview/${file.id}`;
    a.target = "_blank";
    a.rel    = "noopener noreferrer";
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  }

  function handleDrop(e: React.DragEvent) {
    e.preventDefault();
    setDragging(false);
    if (e.dataTransfer.files.length) uploadFiles(e.dataTransfer.files);
  }

  const filteredIds = useMemo(() => filtered.map(f => f.id), [filtered]);

  function selectFile(file: DocFile, e: React.MouseEvent) {
    if (e.shiftKey && lastClickedId) {
      const a = filteredIds.indexOf(lastClickedId);
      const b = filteredIds.indexOf(file.id);
      if (a !== -1 && b !== -1) {
        const [s, t] = a < b ? [a, b] : [b, a];
        const range = filteredIds.slice(s, t + 1);
        setSelectedIds(prev => {
          const n = new Set(prev);
          range.forEach(id => n.add(id));
          return n;
        });
      }
    } else if (e.metaKey || e.ctrlKey) {
      setSelectedIds(prev => {
        const n = new Set(prev);
        if (n.has(file.id)) n.delete(file.id); else n.add(file.id);
        return n;
      });
      setLastClickedId(file.id);
    } else {
      const wasOnlyOne = selectedIds.size === 1 && selectedIds.has(file.id);
      if (wasOnlyOne && focusedId === file.id) {
        setSelectedIds(new Set());
        setFocusedId(null);
      } else {
        setSelectedIds(new Set([file.id]));
        setFocusedId(file.id);
        setLastClickedId(file.id);
      }
    }
  }

  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      const target = e.target as HTMLElement | null;
      const inEditable = !!target && (
        target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable
      );

      if (e.key === "Escape") {
        if (uploadModal.open)         setUploadModal(m => ({ ...m, open: false }));
        else if (confirmDialog)       setConfirmDialog(null);
        else if (contextMenu)         setContextMenu(null);
        else if (mobileSidebarOpen)   setMobileSidebarOpen(false);
        else if (focusedId)           setFocusedId(null);
        else if (selectedIds.size)    setSelectedIds(new Set());
        else if (search)              setSearch("");
        return;
      }

      if (inEditable) return;

      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "f") {
        e.preventDefault();
        searchRef.current?.focus();
        searchRef.current?.select();
        return;
      }
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "a") {
        e.preventDefault();
        setSelectedIds(new Set(filteredIds));
        return;
      }
      if (e.key === "Delete" || e.key === "Backspace") {
        if (selectedIds.size === 0) return;
        e.preventDefault();
        const ids = [...selectedIds];
        if (sidebar === "trash") confirmPurge(ids);
        else                     softDelete(ids);
        return;
      }
      if (e.key === "ArrowDown" || e.key === "ArrowRight" || e.key === "ArrowUp" || e.key === "ArrowLeft") {
        if (filteredIds.length === 0) return;
        const cur = lastClickedId ?? filteredIds[0];
        const idx = filteredIds.indexOf(cur);
        const dir = (e.key === "ArrowDown" || e.key === "ArrowRight") ? 1 : -1;
        const next = filteredIds[Math.max(0, Math.min(filteredIds.length - 1, idx + dir))];
        if (next) {
          e.preventDefault();
          setSelectedIds(new Set([next]));
          setFocusedId(next);
          setLastClickedId(next);
        }
      }
    };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [selectedIds, filteredIds, sidebar, focusedId, contextMenu, confirmDialog, uploadModal.open, lastClickedId, search, mobileSidebarOpen]);

  const focusedFile  = focusedId ? metadata.files[focusedId] : null;
  const selectionCount = selectedIds.size;
  const inTrash      = sidebar === "trash";

  const sectionTitle =
    sidebar === "all"     ? "Alle Dokumente"
    : sidebar === "recent"  ? "Zuletzt hinzugefügt"
    : sidebar === "starred" ? "Favoriten"
    : sidebar === "fristen" ? "Fristen"
    : sidebar === "trash"   ? "Papierkorb"
    : metadata.categories.find(c => c.id === sidebar)?.name ?? "";

  function navigateSidebar(v: SidebarView) {
    setSidebar(v);
    setSelectedIds(new Set());
    setFocusedId(null);
    setMobileSidebarOpen(false);
  }

  return (
    <div className="flex h-[100dvh] overflow-hidden" style={{ position: "relative", zIndex: 1 }}>

      <input ref={fileInputRef} type="file" multiple className="hidden"
        onChange={e => e.target.files && uploadFiles(e.target.files)} />

      {/* Desktop sidebar */}
      <aside className="hidden md:flex w-60 flex-shrink-0 flex-col overflow-y-auto"
        style={{ background: GLASS, backdropFilter: BLUR, WebkitBackdropFilter: BLUR, borderRight: "1px solid rgba(0,0,0,0.1)" }}>
        <div className="px-5 pt-6 pb-4 flex items-center gap-2">
          <img src="/favicon.svg" alt="DocMaster" className="w-8 h-8" />
          <span className="font-bold text-[15px] tracking-tight" style={{ color: TEXT }}>DocMaster</span>
        </div>
        <SidebarInner
          sidebar={sidebar} navigate={navigateSidebar}
          liveFiles={liveFiles} trashedFiles={trashedFiles} countByCategory={countByCategory}
          categories={metadata.categories} uploading={uploading}
          onUploadClick={() => fileInputRef.current?.click()}
        />
        <div className="px-4 py-4" style={{ borderTop: "1px solid rgba(0,0,0,0.08)" }}>
          <p className="text-[11px]" style={{ color: TEXT2 }}>
            {liveFiles.length} Dokumente · {formatSize(liveFiles.reduce((s, f) => s + f.size, 0))}
          </p>
        </div>
      </aside>

      {/* Mobile sidebar drawer */}
      <AnimatePresence>
        {mobileSidebarOpen && (
          <div className="md:hidden fixed inset-0 z-[55]">
            <motion.div
              initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
              className="absolute inset-0"
              style={{ background: "rgba(0,0,0,0.4)", backdropFilter: "blur(4px)" }}
              onClick={() => setMobileSidebarOpen(false)} />
            <motion.aside
              initial={{ x: -300 }} animate={{ x: 0 }} exit={{ x: -300 }}
              transition={{ type: "spring", damping: 28, stiffness: 320 }}
              className="absolute left-0 top-0 bottom-0 w-[280px] flex flex-col overflow-y-auto"
              style={{ background: "rgba(255,255,255,0.96)", backdropFilter: BLUR, WebkitBackdropFilter: BLUR }}>
              <div className="px-5 pt-6 pb-4 flex items-center justify-between">
                <div className="flex items-center gap-2">
                  <img src="/favicon.svg" alt="DocMaster" className="w-8 h-8" />
                  <span className="font-bold text-[15px] tracking-tight" style={{ color: TEXT }}>DocMaster</span>
                </div>
                <button onClick={() => setMobileSidebarOpen(false)} className="p-2 -mr-2" style={{ color: TEXT2 }}>
                  <X size={22} />
                </button>
              </div>
              <SidebarInner
                sidebar={sidebar} navigate={navigateSidebar}
                liveFiles={liveFiles} trashedFiles={trashedFiles} countByCategory={countByCategory}
                categories={metadata.categories} uploading={uploading}
                onUploadClick={() => { fileInputRef.current?.click(); setMobileSidebarOpen(false); }}
                touch
              />
              <div className="px-4 py-4 mt-auto" style={{ borderTop: "1px solid rgba(0,0,0,0.08)" }}>
                <p className="text-[11px]" style={{ color: TEXT2 }}>
                  {liveFiles.length} Dokumente · {formatSize(liveFiles.reduce((s, f) => s + f.size, 0))}
                </p>
              </div>
            </motion.aside>
          </div>
        )}
      </AnimatePresence>

      {/* Main */}
      <div className="flex-1 flex flex-col overflow-hidden min-w-0"
        onDragOver={e => { e.preventDefault(); setDragging(true); }}
        onDragLeave={() => setDragging(false)}
        onDrop={handleDrop}>

        {/* Mobile header */}
        <div className="flex md:hidden items-center gap-3 px-4 py-3 flex-shrink-0"
          style={{ background: GLASS_MD, backdropFilter: BLUR, WebkitBackdropFilter: BLUR, borderBottom: "1px solid rgba(0,0,0,0.08)" }}>
          <button onClick={() => setMobileSidebarOpen(true)} className="p-1 -ml-1" style={{ color: TEXT }}>
            <Menu size={22} />
          </button>
          <span className="flex-1 text-[15px] font-semibold truncate" style={{ color: TEXT }}>{sectionTitle}</span>
          {selectionCount > 0 && (
            <button onClick={() => setSelectedIds(new Set())}
              className="text-xs font-medium px-2 py-1 rounded-lg flex items-center gap-1"
              style={{ color: ACCENT, background: ACCENT + "15" }}>
              {selectionCount} <X size={12} />
            </button>
          )}
        </div>

        {/* Desktop toolbar */}
        <div className="hidden md:flex items-center gap-3 px-6 py-3 flex-shrink-0"
          style={{ background: GLASS_MD, backdropFilter: BLUR, WebkitBackdropFilter: BLUR, borderBottom: "1px solid rgba(0,0,0,0.1)" }}>
          <div className="relative flex-1 max-w-md">
            <Search size={14} className="absolute left-3 top-1/2 -translate-y-1/2" style={{ color: TEXT2 }} />
            <input ref={searchRef} value={search} onChange={e => setSearch(e.target.value)} placeholder="Name oder Tag suchen…  (⌘F)"
              className="w-full pl-8 pr-4 py-1.5 rounded-xl text-sm outline-none transition-colors"
              style={{ background: "rgba(0,0,0,0.06)", color: TEXT }}
              onFocus={e => (e.target.style.background = "rgba(255,255,255,0.9)")}
              onBlur={e => (e.target.style.background  = "rgba(0,0,0,0.06)")} />
            {search && <button onClick={() => setSearch("")} className="absolute right-2 top-1/2 -translate-y-1/2" style={{ color: TEXT2 }}><X size={13} /></button>}
          </div>
          <div className="relative">
            <select value={sortBy} onChange={e => setSortBy(e.target.value as "date"|"name"|"size")}
              className="appearance-none text-sm pl-3 pr-7 py-1.5 rounded-xl outline-none cursor-pointer"
              style={{ background: "rgba(0,0,0,0.06)", color: TEXT }}>
              <option value="date">Datum</option>
              <option value="name">Name</option>
              <option value="size">Größe</option>
            </select>
            <ChevronDown size={12} className="absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none" style={{ color: TEXT2 }} />
          </div>
          <button onClick={() => setSortDesc(d => !d)} className="p-1.5 rounded-lg transition-colors hover:bg-black/10"
            style={{ background: "rgba(0,0,0,0.06)", color: TEXT2 }} title={sortDesc ? "Absteigend" : "Aufsteigend"}>
            <SlidersHorizontal size={14} />
          </button>
          <div className="flex rounded-xl overflow-hidden" style={{ border: "1px solid rgba(0,0,0,0.1)", background: "rgba(0,0,0,0.04)" }}>
            {(["grid","list"] as ViewMode[]).map(v => (
              <button key={v} onClick={() => setView(v)} className="p-1.5 transition-colors"
                style={view === v ? { background: "rgba(255,255,255,0.9)", color: ACCENT } : { color: TEXT2 }}>
                {v === "grid" ? <Grid3X3 size={15} /> : <List size={15} />}
              </button>
            ))}
          </div>
        </div>

        {/* Mobile search bar */}
        <div className="flex md:hidden items-center gap-2 px-4 py-2 flex-shrink-0"
          style={{ background: GLASS_MD, backdropFilter: BLUR, WebkitBackdropFilter: BLUR, borderBottom: "1px solid rgba(0,0,0,0.06)" }}>
          <div className="relative flex-1">
            <Search size={14} className="absolute left-3 top-1/2 -translate-y-1/2" style={{ color: TEXT2 }} />
            <input value={search} onChange={e => setSearch(e.target.value)} placeholder="Suchen…"
              className="w-full pl-9 pr-9 py-2.5 rounded-xl text-[15px] outline-none"
              style={{ background: "rgba(0,0,0,0.06)", color: TEXT }} />
            {search && <button onClick={() => setSearch("")} className="absolute right-2 top-1/2 -translate-y-1/2 p-1" style={{ color: TEXT2 }}><X size={14} /></button>}
          </div>
          <div className="relative">
            <select value={sortBy} onChange={e => setSortBy(e.target.value as "date"|"name"|"size")}
              className="appearance-none text-sm pl-3 pr-7 py-2.5 rounded-xl outline-none cursor-pointer"
              style={{ background: "rgba(0,0,0,0.06)", color: TEXT }}>
              <option value="date">Datum</option>
              <option value="name">Name</option>
              <option value="size">Größe</option>
            </select>
            <ChevronDown size={12} className="absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none" style={{ color: TEXT2 }} />
          </div>
          <button onClick={() => setView(view === "grid" ? "list" : "grid")}
            className="p-2.5 rounded-xl flex-shrink-0"
            style={{ background: "rgba(0,0,0,0.06)", color: ACCENT }}>
            {view === "grid" ? <List size={16} /> : <Grid3X3 size={16} />}
          </button>
        </div>

        {/* Desktop title */}
        <div className="hidden md:flex px-6 pt-4 pb-2 flex-shrink-0 items-baseline gap-3">
          <h1 className="text-xl font-semibold" style={{ color: TEXT }}>{sectionTitle}</h1>
          {filtered.length > 0 && <p className="text-sm" style={{ color: TEXT2 }}>{filtered.length} Dokumente</p>}
        </div>

        {/* Selection bar */}
        <AnimatePresence>
          {selectionCount > 0 && (
            <motion.div initial={{ opacity: 0, y: -6 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -6 }}
              className="mx-4 md:mx-6 mb-2 flex items-center gap-2 px-3 md:px-4 py-2 rounded-xl flex-wrap"
              style={{ background: GLASS, backdropFilter: BLUR, border: "1px solid rgba(0,0,0,0.08)" }}>
              <span className="text-sm font-medium" style={{ color: TEXT }}>{selectionCount} ausgewählt</span>
              <span className="flex-1" />
              {!inTrash && (
                <BulkCategoryPicker categories={metadata.categories}
                  onPick={cat => {
                    [...selectedIds].forEach(id => patch(id, { categoryId: cat }, { silent: true }));
                    showToast(cat ? "Kategorie zugewiesen" : "Kategorie entfernt", "success");
                  }} />
              )}
              {inTrash ? (
                <>
                  <button onClick={() => restore([...selectedIds])}
                    className="text-xs px-3 py-1.5 rounded-lg flex items-center gap-1 hover:bg-black/5"
                    style={{ background: "rgba(0,0,0,0.04)", color: TEXT }}>
                    <RotateCcw size={12} /> <span className="hidden sm:inline">Wiederherstellen</span>
                  </button>
                  <button onClick={() => confirmPurge([...selectedIds])}
                    className="text-xs px-3 py-1.5 rounded-lg flex items-center gap-1 text-red-500 hover:bg-red-50">
                    <Trash2 size={12} /> <span className="hidden sm:inline">Endgültig</span>
                  </button>
                </>
              ) : (
                <button onClick={() => softDelete([...selectedIds])}
                  className="text-xs px-3 py-1.5 rounded-lg flex items-center gap-1 text-red-500 hover:bg-red-50">
                  <Trash2 size={12} /> <span className="hidden sm:inline">Papierkorb</span>
                </button>
              )}
              <button onClick={() => setSelectedIds(new Set())}
                className="text-xs px-3 py-1.5 rounded-lg hover:bg-black/5 hidden sm:inline-flex"
                style={{ color: TEXT2 }}>
                Aufheben
              </button>
            </motion.div>
          )}
        </AnimatePresence>

        {/* Drag overlay */}
        <AnimatePresence>
          {dragging && (
            <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
              className="absolute inset-0 z-50 flex items-center justify-center pointer-events-none"
              style={{ background: "rgba(255,128,145,0.08)", border: "2px dashed rgba(255,128,145,0.5)" }}>
              <div className="bg-white rounded-2xl px-8 py-6 shadow-2xl flex flex-col items-center gap-3">
                <Upload size={32} style={{ color: ACCENT }} />
                <p className="font-semibold" style={{ color: TEXT }}>Dateien loslassen zum Hochladen</p>
              </div>
            </motion.div>
          )}
        </AnimatePresence>

        <div className="flex-1 overflow-y-auto px-4 md:px-6 pb-32 md:pb-6">
          {loading ? (
            <div className="flex items-center justify-center h-full">
              <div className="w-8 h-8 border-2 border-t-transparent rounded-full animate-spin"
                style={{ borderColor: "rgba(0,0,0,0.15)", borderTopColor: "transparent" }} />
            </div>
          ) : filtered.length === 0 ? (
            <EmptyState sidebar={sidebar} search={search} onUpload={() => fileInputRef.current?.click()} />
          ) : view === "grid" ? (
            <GridView files={filtered} categories={metadata.categories}
              selectedIds={selectedIds} focusedId={focusedId} inTrash={inTrash} isMobile={isMobile}
              onSelect={selectFile} onStar={id => patch(id, { starred: !metadata.files[id].starred }, { silent: true })}
              onPurge={id => confirmPurge([id])}
              onRestore={id => restore([id])} onOpen={openPreview} onContextMenu={setContextMenu} />
          ) : (
            <ListView files={filtered} categories={metadata.categories}
              selectedIds={selectedIds} focusedId={focusedId} inTrash={inTrash} isMobile={isMobile}
              onSelect={selectFile} onStar={id => patch(id, { starred: !metadata.files[id].starred }, { silent: true })}
              onSoftDelete={id => softDelete([id])} onPurge={id => confirmPurge([id])}
              onRestore={id => restore([id])} onOpen={openPreview} onDownload={downloadFile}
              onContextMenu={setContextMenu} />
          )}
        </div>
      </div>

      {/* Desktop detail panel */}
      <AnimatePresence>
        {focusedFile && !isMobile && (
          <motion.aside key="detail-desktop"
            initial={{ x: 280, opacity: 0 }} animate={{ x: 0, opacity: 1 }} exit={{ x: 280, opacity: 0 }}
            transition={{ type: "spring", damping: 26, stiffness: 300 }}
            className="w-72 flex-shrink-0 flex flex-col overflow-y-auto"
            style={{ background: GLASS, backdropFilter: BLUR, WebkitBackdropFilter: BLUR, borderLeft: "1px solid rgba(0,0,0,0.1)" }}>
            <DetailContent
              file={focusedFile} categories={metadata.categories}
              onClose={() => setFocusedId(null)}
              onPatch={patch} onSoftDelete={softDelete} onRestore={restore}
              onConfirmPurge={confirmPurge} onDownload={downloadFile} onPreview={openPreview}
            />
          </motion.aside>
        )}
      </AnimatePresence>

      {/* Mobile detail bottom sheet */}
      <AnimatePresence>
        {focusedFile && isMobile && (
          <div className="md:hidden fixed inset-0 z-[55]">
            <motion.div
              initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
              className="absolute inset-0"
              style={{ background: "rgba(0,0,0,0.35)", backdropFilter: "blur(4px)" }}
              onClick={() => setFocusedId(null)} />
            <motion.div
              initial={{ y: "100%" }} animate={{ y: 0 }} exit={{ y: "100%" }}
              transition={{ type: "spring", damping: 30, stiffness: 320 }}
              className="absolute inset-x-0 bottom-0 flex flex-col rounded-t-3xl overflow-hidden"
              style={{
                background: "rgba(255,255,255,0.97)", backdropFilter: BLUR, WebkitBackdropFilter: BLUR,
                maxHeight: "90dvh", paddingBottom: "env(safe-area-inset-bottom)",
                boxShadow: "0 -8px 32px rgba(0,0,0,0.15)",
              }}>
              <div className="flex justify-center pt-3 pb-1 flex-shrink-0">
                <div className="w-10 h-1 rounded-full" style={{ background: "rgba(0,0,0,0.18)" }} />
              </div>
              <div className="overflow-y-auto flex-1">
                <DetailContent
                  file={focusedFile} categories={metadata.categories}
                  onClose={() => setFocusedId(null)}
                  onPatch={patch} onSoftDelete={softDelete} onRestore={restore}
                  onConfirmPurge={confirmPurge} onDownload={downloadFile} onPreview={openPreview}
                />
              </div>
            </motion.div>
          </div>
        )}
      </AnimatePresence>

      {/* Mobile bottom nav */}
      <nav className="md:hidden fixed bottom-0 inset-x-0 z-30 flex items-stretch"
        style={{
          background: GLASS, backdropFilter: BLUR, WebkitBackdropFilter: BLUR,
          borderTop: "1px solid rgba(0,0,0,0.08)",
          paddingBottom: "env(safe-area-inset-bottom)",
        }}>
        {([
          { id: "all",     label: "Alle",       icon: <Home size={20} /> },
          { id: "starred", label: "Favoriten",  icon: <Star size={20} /> },
          { id: "fristen", label: "Fristen",    icon: <AlarmClock size={20} /> },
          { id: "trash",   label: "Papierkorb", icon: <Trash2 size={20} /> },
        ] as const).map(item => {
          const active = sidebar === item.id;
          return (
            <button key={item.id}
              onClick={() => navigateSidebar(item.id)}
              className="flex-1 flex flex-col items-center justify-center gap-0.5 py-2 transition-colors active:bg-black/5"
              style={{ color: active ? ACCENT : TEXT2 }}>
              {item.icon}
              <span className="text-[10px] font-medium">{item.label}</span>
            </button>
          );
        })}
        <button
          onClick={() => setMobileSidebarOpen(true)}
          className="flex-1 flex flex-col items-center justify-center gap-0.5 py-2 transition-colors active:bg-black/5"
          style={{ color: TEXT2 }}>
          <Menu size={20} />
          <span className="text-[10px] font-medium">Menü</span>
        </button>
      </nav>

      {/* Mobile upload FAB */}
      <button
        className="md:hidden fixed z-40 w-14 h-14 rounded-full flex items-center justify-center transition-transform active:scale-90"
        style={{
          background: ACCENT,
          bottom: "calc(72px + env(safe-area-inset-bottom))",
          right: "16px",
          boxShadow: "0 8px 24px rgba(255,128,145,0.45)",
        }}
        onClick={() => fileInputRef.current?.click()}
        disabled={uploading}
        aria-label="Hochladen">
        {uploading
          ? <div className="w-5 h-5 border-2 border-white/60 border-t-white rounded-full animate-spin" />
          : <Upload size={22} color="white" />}
      </button>

      {/* Upload modal */}
      <AnimatePresence>
        {uploadModal.open && (
          <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
            className="fixed inset-0 z-[60] flex items-end md:items-center justify-center"
            style={{ background: "rgba(0,0,0,0.35)", backdropFilter: "blur(8px)" }}>
            <motion.div
              initial={{ y: "100%", opacity: 0 }} animate={{ y: 0, opacity: 1 }} exit={{ y: "100%", opacity: 0 }}
              transition={{ type: "spring", damping: 28, stiffness: 320 }}
              className="w-full md:w-[420px] md:max-w-[420px] rounded-t-3xl md:rounded-2xl shadow-2xl overflow-hidden md:max-h-[90vh] max-h-[95dvh] flex flex-col"
              style={{ background: "rgba(255,255,255,0.97)", backdropFilter: "blur(24px)" }}>
              <div className="flex justify-center pt-3 pb-1 md:hidden flex-shrink-0">
                <div className="w-10 h-1 rounded-full" style={{ background: "rgba(0,0,0,0.18)" }} />
              </div>
              <div className="px-6 pt-4 md:pt-6 pb-4 flex-shrink-0">
                <h2 className="text-lg font-semibold mb-0.5" style={{ color: TEXT }}>Dokument einordnen</h2>
                <p className="text-sm" style={{ color: TEXT2 }}>
                  {uploadModal.fileCount === 1 ? "1 Datei hochgeladen" : `${uploadModal.fileCount} Dateien hochgeladen`} — Kategorie und Datum wählen
                </p>
              </div>
              <div className="px-6 pb-6 space-y-5 overflow-y-auto" style={{ paddingBottom: "calc(24px + env(safe-area-inset-bottom))" }}>
                <div>
                  <p className="text-[11px] font-semibold uppercase tracking-wider mb-2.5" style={{ color: TEXT2 }}>Kategorie</p>
                  <div className="grid grid-cols-3 gap-2">
                    {metadata.categories.map(cat => (
                      <button key={cat.id} onClick={() => setUploadModal(m => ({ ...m, categoryId: m.categoryId === cat.id ? null : cat.id }))}
                        className="flex flex-col items-center gap-1.5 py-3 px-2 rounded-xl border text-xs font-medium transition-all"
                        style={uploadModal.categoryId === cat.id
                          ? { background: cat.color + "18", borderColor: cat.color, color: cat.color, transform: "scale(1.03)" }
                          : { background: "rgba(0,0,0,0.03)", borderColor: "rgba(0,0,0,0.1)", color: TEXT2 }}>
                        <span className="w-7 h-7 rounded-full flex items-center justify-center"
                          style={{ background: cat.color + "20", color: cat.color }}>
                          {CATEGORY_ICONS[cat.icon] ?? <Tag size={14} />}
                        </span>
                        {cat.name}
                      </button>
                    ))}
                  </div>
                </div>
                <div>
                  <p className="text-[11px] font-semibold uppercase tracking-wider mb-1.5 flex items-center gap-1" style={{ color: TEXT2 }}>
                    <CalendarDays size={10} /> Dokumentdatum <span className="normal-case font-normal">(optional)</span>
                  </p>
                  <input type="date" value={uploadModal.documentDate}
                    onChange={e => setUploadModal(m => ({ ...m, documentDate: e.target.value }))}
                    className="w-full text-[15px] md:text-sm px-3 py-3 md:py-2 rounded-xl outline-none"
                    style={{ border: "1px solid rgba(0,0,0,0.15)", background: "rgba(0,0,0,0.03)", color: TEXT }} />
                </div>
                <div>
                  <p className="text-[11px] font-semibold uppercase tracking-wider mb-1.5 flex items-center gap-1" style={{ color: TEXT2 }}>
                    <AlarmClock size={10} /> Frist <span className="normal-case font-normal">(optional)</span>
                  </p>
                  <input type="date" value={uploadModal.frist}
                    onChange={e => setUploadModal(m => ({ ...m, frist: e.target.value }))}
                    className="w-full text-[15px] md:text-sm px-3 py-3 md:py-2 rounded-xl outline-none"
                    style={{ border: "1px solid rgba(0,0,0,0.15)", background: "rgba(0,0,0,0.03)", color: TEXT }} />
                </div>
                <div className="flex gap-2 pt-1">
                  <button onClick={() => setUploadModal(m => ({ ...m, open: false }))}
                    className="flex-1 py-3 rounded-xl text-sm font-medium transition-colors hover:bg-black/10"
                    style={{ background: "rgba(0,0,0,0.06)", color: TEXT2 }}>
                    Überspringen
                  </button>
                  <button onClick={confirmUploadModal}
                    disabled={!uploadModal.categoryId && !uploadModal.documentDate && !uploadModal.frist}
                    className="flex-1 py-3 rounded-xl text-sm font-medium text-white transition-opacity hover:opacity-90 disabled:cursor-not-allowed"
                    style={{
                      background: (uploadModal.categoryId || uploadModal.documentDate || uploadModal.frist) ? ACCENT : "#bbb",
                      opacity: (uploadModal.categoryId || uploadModal.documentDate || uploadModal.frist) ? 1 : 0.6,
                    }}>
                    Speichern
                  </button>
                </div>
              </div>
            </motion.div>
          </motion.div>
        )}
      </AnimatePresence>

      {/* Context menu */}
      <AnimatePresence>
        {contextMenu && metadata.files[contextMenu.fileId] && (() => {
          const f = metadata.files[contextMenu.fileId];
          const isTrashed = !!f.deletedAt;
          const items: { label: string; icon: React.ReactNode; action: () => void }[] = [];
          if (!isTrashed) {
            items.push(
              { label: "Info",          icon: <Eye size={14} />,      action: () => { setFocusedId(f.id); } },
              { label: "Vorschau",      icon: <Eye size={14} />,      action: () => openPreview(f) },
              { label: "Herunterladen", icon: <Download size={14} />, action: () => downloadFile(f) },
              { label: "Favorit",       icon: <Star size={14} />,     action: () => patch(f.id, { starred: !f.starred }, { silent: true }) },
            );
          } else {
            items.push(
              { label: "Wiederherstellen", icon: <RotateCcw size={14} />, action: () => restore([f.id]) },
              { label: "Vorschau",         icon: <Eye size={14} />,       action: () => openPreview(f) },
              { label: "Herunterladen",    icon: <Download size={14} />,  action: () => downloadFile(f) },
            );
          }
          return (
            <motion.div initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.95 }}
              className="fixed z-50 rounded-xl shadow-xl overflow-hidden"
              style={{
                top: Math.min(contextMenu.y, window.innerHeight - 240),
                left: Math.min(contextMenu.x, window.innerWidth - 200),
                minWidth: 180,
                background: "rgba(255,255,255,0.95)", backdropFilter: "blur(20px)", border: "1px solid rgba(0,0,0,0.1)",
              }}
              onClick={e => e.stopPropagation()}>
              {items.map(item => (
                <button key={item.label} onClick={() => { item.action(); setContextMenu(null); }}
                  className="w-full flex items-center gap-2 px-3 py-2 text-sm transition-colors hover:text-white"
                  style={{ color: TEXT }}
                  onMouseEnter={e => (e.currentTarget.style.background = ACCENT)}
                  onMouseLeave={e => (e.currentTarget.style.background = "transparent")}>
                  {item.icon} {item.label}
                </button>
              ))}
              <div className="h-px mx-2" style={{ background: "rgba(0,0,0,0.07)" }} />
              {isTrashed ? (
                <button onClick={() => { confirmPurge([f.id]); setContextMenu(null); }}
                  className="w-full flex items-center gap-2 px-3 py-2 text-sm text-red-500 hover:bg-red-500 hover:text-white transition-colors">
                  <Trash2 size={14} /> Endgültig löschen
                </button>
              ) : (
                <button onClick={() => { softDelete([f.id]); setContextMenu(null); }}
                  className="w-full flex items-center gap-2 px-3 py-2 text-sm text-red-500 hover:bg-red-500 hover:text-white transition-colors">
                  <Trash2 size={14} /> In Papierkorb
                </button>
              )}
            </motion.div>
          );
        })()}
      </AnimatePresence>

      {/* Confirm dialog */}
      <AnimatePresence>
        {confirmDialog && (
          <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
            className="fixed inset-0 z-[70] flex items-center justify-center px-4"
            style={{ background: "rgba(0,0,0,0.35)", backdropFilter: "blur(8px)" }}
            onClick={() => setConfirmDialog(null)}>
            <motion.div initial={{ scale: 0.94, opacity: 0 }} animate={{ scale: 1, opacity: 1 }}
              exit={{ scale: 0.94, opacity: 0 }} transition={{ type: "spring", damping: 24, stiffness: 320 }}
              onClick={e => e.stopPropagation()}
              className="w-full max-w-[380px] rounded-2xl shadow-2xl overflow-hidden p-6"
              style={{ background: "rgba(255,255,255,0.98)" }}>
              <div className="flex items-start gap-3 mb-4">
                <div className="w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0"
                  style={{ background: confirmDialog.destructive ? "#FEE2E2" : "#FEF3C7" }}>
                  <AlertTriangle size={18} style={{ color: confirmDialog.destructive ? "#DC2626" : "#D97706" }} />
                </div>
                <div className="flex-1">
                  <h3 className="font-semibold text-[15px]" style={{ color: TEXT }}>{confirmDialog.title}</h3>
                  <p className="text-sm mt-1" style={{ color: TEXT2 }}>{confirmDialog.message}</p>
                </div>
              </div>
              <div className="flex gap-2 justify-end">
                <button onClick={() => setConfirmDialog(null)}
                  className="px-4 py-2.5 rounded-xl text-sm font-medium hover:bg-black/5"
                  style={{ color: TEXT2 }}>Abbrechen</button>
                <button onClick={confirmDialog.onConfirm} autoFocus
                  className="px-4 py-2.5 rounded-xl text-sm font-medium text-white"
                  style={{ background: confirmDialog.destructive ? "#DC2626" : ACCENT }}>
                  {confirmDialog.confirmLabel}
                </button>
              </div>
            </motion.div>
          </motion.div>
        )}
      </AnimatePresence>

      {/* Toasts */}
      <div className="fixed right-4 z-[80] flex flex-col gap-2 max-w-sm pointer-events-none bottom-24 md:bottom-4">
        <AnimatePresence>
          {toasts.map(t => (
            <motion.div key={t.id} initial={{ opacity: 0, y: 16, scale: 0.96 }}
              animate={{ opacity: 1, y: 0, scale: 1 }} exit={{ opacity: 0, y: 16, scale: 0.96 }}
              className="px-4 py-2.5 rounded-xl shadow-lg text-sm font-medium pointer-events-auto"
              style={{
                background: t.kind === "error" ? "#FEE2E2" : t.kind === "success" ? "#DCFCE7" : "rgba(255,255,255,0.95)",
                color:      t.kind === "error" ? "#991B1B" : t.kind === "success" ? "#166534" : TEXT,
                border:     "1px solid rgba(0,0,0,0.08)",
                backdropFilter: "blur(20px)",
              }}>
              {t.message}
            </motion.div>
          ))}
        </AnimatePresence>
      </div>
    </div>
  );
}

interface SidebarInnerProps {
  sidebar: SidebarView;
  navigate: (v: SidebarView) => void;
  liveFiles: DocFile[];
  trashedFiles: DocFile[];
  countByCategory: Map<string, number>;
  categories: Category[];
  uploading: boolean;
  onUploadClick: () => void;
  touch?: boolean;
}

function SidebarInner({ sidebar, navigate, liveFiles, trashedFiles, countByCategory, categories, uploading, onUploadClick, touch }: SidebarInnerProps) {
  const padY = touch ? "py-2.5" : "py-1.5";
  const txt  = touch ? "text-[15px]" : "text-sm";
  return (
    <>
      <div className="px-3 mb-4">
        <button onClick={onUploadClick} disabled={uploading}
          className={`w-full flex items-center justify-center gap-2 ${touch ? "py-3" : "py-2"} px-3 rounded-lg text-white text-sm font-medium transition-all active:scale-95`}
          style={{ background: uploading ? "#bbb" : ACCENT, boxShadow: "0 2px 8px rgba(255,128,145,0.35)" }}>
          <Upload size={touch ? 16 : 14} />
          {uploading ? "Lädt hoch…" : "Hochladen"}
        </button>
      </div>

      <nav className="px-2 flex-1">
        <p className="px-3 text-[11px] font-semibold uppercase tracking-wider mb-1" style={{ color: TEXT2 }}>Bibliothek</p>
        {[
          { id: "all",     label: "Alle Dokumente", icon: <FolderOpen size={touch ? 18 : 15} />, count: liveFiles.length },
          { id: "recent",  label: "Zuletzt",        icon: <Clock      size={touch ? 18 : 15} />, count: null },
          { id: "starred", label: "Favoriten",      icon: <Star       size={touch ? 18 : 15} />, count: liveFiles.filter(f => f.starred).length },
          { id: "fristen", label: "Fristen",        icon: <AlarmClock size={touch ? 18 : 15} />, count: liveFiles.filter(f => f.frist).length },
        ].map(item => (
          <button key={item.id} onClick={() => navigate(item.id)}
            className={`w-full flex items-center gap-2 px-3 ${padY} rounded-lg ${txt} mb-0.5 transition-colors`}
            style={sidebar === item.id
              ? { background: ACCENT + "18", color: ACCENT, fontWeight: 500 }
              : { color: TEXT }}>
            <span style={{ color: sidebar === item.id ? ACCENT : TEXT2 }}>{item.icon}</span>
            <span className="flex-1 text-left">{item.label}</span>
            {item.count !== null && <span className="text-[11px]" style={{ color: TEXT2 }}>{item.count}</span>}
          </button>
        ))}

        <p className="px-3 text-[11px] font-semibold uppercase tracking-wider mt-4 mb-1" style={{ color: TEXT2 }}>Kategorien</p>

        {categories.map(cat => (
          <button key={cat.id} onClick={() => navigate(cat.id)}
            className={`w-full flex items-center gap-2 px-3 ${padY} rounded-lg ${txt} mb-0.5 transition-colors`}
            style={sidebar === cat.id
              ? { background: cat.color + "18", color: cat.color, fontWeight: 500 }
              : { color: TEXT }}>
            <span className={`${touch ? "w-5 h-5" : "w-4 h-4"} rounded-full flex items-center justify-center flex-shrink-0`}
              style={{ background: cat.color + "22", color: cat.color }}>
              {CATEGORY_ICONS[cat.icon] ?? <Tag size={10} />}
            </span>
            <span className="flex-1 text-left truncate">{cat.name}</span>
            <span className="text-[11px]" style={{ color: TEXT2 }}>{countByCategory.get(cat.id) ?? 0}</span>
          </button>
        ))}

        <p className="px-3 text-[11px] font-semibold uppercase tracking-wider mt-4 mb-1" style={{ color: TEXT2 }}>System</p>
        <button onClick={() => navigate("trash")}
          className={`w-full flex items-center gap-2 px-3 ${padY} rounded-lg ${txt} mb-0.5 transition-colors`}
          style={sidebar === "trash"
            ? { background: "#9CA3AF22", color: "#374151", fontWeight: 500 }
            : { color: TEXT }}>
          <span style={{ color: sidebar === "trash" ? "#374151" : TEXT2 }}><Trash2 size={touch ? 18 : 15} /></span>
          <span className="flex-1 text-left">Papierkorb</span>
          <span className="text-[11px]" style={{ color: TEXT2 }}>{trashedFiles.length}</span>
        </button>
      </nav>
    </>
  );
}

interface DetailContentProps {
  file: DocFile;
  categories: Category[];
  onClose: () => void;
  onPatch: (id: string, updates: Partial<DocFile>, opts?: { silent?: boolean }) => Promise<void>;
  onSoftDelete: (ids: string[]) => Promise<void>;
  onRestore: (ids: string[]) => Promise<void>;
  onConfirmPurge: (ids: string[]) => void;
  onDownload: (file: DocFile) => void;
  onPreview: (file: DocFile) => void;
}

function DetailContent({ file, categories, onClose, onPatch, onSoftDelete, onRestore, onConfirmPurge, onDownload, onPreview }: DetailContentProps) {
  return (
    <>
      <div className="flex items-center justify-between px-4 pt-4 pb-2">
        <span className="text-sm font-semibold" style={{ color: TEXT }}>Info</span>
        <button onClick={onClose} className="p-2 -mr-2" style={{ color: TEXT2 }}><X size={18} /></button>
      </div>

      <div className="mx-4 mb-4 rounded-xl h-44 md:h-40 flex items-center justify-center overflow-hidden"
        style={{ background: "rgba(0,0,0,0.05)", border: "1px solid rgba(0,0,0,0.07)" }}>
        <FileVisual file={file} size={48} />
      </div>

      <div className="px-4 space-y-4 pb-6">
        <div>
          <p className="text-[11px] uppercase tracking-wider mb-1" style={{ color: TEXT2 }}>Name</p>
          <EditableText value={file.name} onSave={name => onPatch(file.id, { name })} />
        </div>
        <div>
          <p className="text-[11px] uppercase tracking-wider mb-0.5" style={{ color: TEXT2 }}>Hochgeladen</p>
          <p className="text-sm" style={{ color: TEXT }}>{formatDate(file.uploadedAt)}</p>
        </div>
        <div>
          <p className="text-[11px] uppercase tracking-wider mb-1 flex items-center gap-1" style={{ color: TEXT2 }}>
            <CalendarDays size={10} /> Dokumentdatum
          </p>
          <input type="date" value={file.documentDate ?? ""}
            onChange={e => onPatch(file.id, { documentDate: e.target.value || null })}
            className="w-full text-[15px] md:text-sm px-3 py-2.5 md:py-1.5 rounded-lg outline-none"
            style={{ border: "1px solid rgba(0,0,0,0.15)", background: "rgba(255,255,255,0.7)", color: TEXT }} />
        </div>
        <div>
          <p className="text-[11px] uppercase tracking-wider mb-1 flex items-center gap-1" style={{ color: TEXT2 }}>
            <AlarmClock size={10} /> Frist
          </p>
          <input type="date" value={file.frist ?? ""}
            onChange={e => onPatch(file.id, { frist: e.target.value || null })}
            className="w-full text-[15px] md:text-sm px-3 py-2.5 md:py-1.5 rounded-lg outline-none"
            style={{ border: "1px solid rgba(0,0,0,0.15)", background: "rgba(255,255,255,0.7)", color: TEXT }} />
          {file.frist && (() => {
            const s = fristStatus(file.frist);
            const c = s ? FRIST_STYLE[s] : null;
            return c ? (
              <span className="inline-flex items-center gap-1 mt-1.5 text-[11px] px-2 py-0.5 rounded-full border font-medium"
                style={{ background: c.bg, color: c.text, borderColor: c.border }}>
                <AlarmClock size={9} />
                {s === "overdue" ? "Überfällig" : s === "soon" ? "Bald fällig" : formatDocDate(file.frist)}
              </span>
            ) : null;
          })()}
        </div>
        <div>
          <p className="text-[11px] uppercase tracking-wider mb-1.5" style={{ color: TEXT2 }}>Kategorie</p>
          <div className="flex flex-wrap gap-1.5">
            <button onClick={() => onPatch(file.id, { categoryId: null })}
              className="text-xs px-3 py-1.5 rounded-full border transition-colors"
              style={!file.categoryId
                ? { background: ACCENT + "15", borderColor: ACCENT, color: ACCENT, fontWeight: 500 }
                : { borderColor: "rgba(0,0,0,0.15)", color: TEXT2 }}>
              Keine
            </button>
            {categories.map(cat => (
              <button key={cat.id} onClick={() => onPatch(file.id, { categoryId: cat.id })}
                className="text-xs px-3 py-1.5 rounded-full border transition-all"
                style={file.categoryId === cat.id
                  ? { background: cat.color + "20", borderColor: cat.color, color: cat.color, fontWeight: 500 }
                  : { borderColor: "rgba(0,0,0,0.15)", color: TEXT2 }}>
                {cat.name}
              </button>
            ))}
          </div>
        </div>
        <div>
          <p className="text-[11px] uppercase tracking-wider mb-1.5 flex items-center gap-1" style={{ color: TEXT2 }}>
            <Tag size={10} /> Tags
          </p>
          <TagChips tags={file.tags ?? []}
            onRemove={tag => onPatch(file.id, { tags: (file.tags ?? []).filter(t => t !== tag) })} />
          <TagInput onAdd={tag => {
            const cur = file.tags ?? [];
            if (!cur.includes(tag)) onPatch(file.id, { tags: [...cur, tag] });
          }} />
        </div>
        <div>
          <p className="text-[11px] uppercase tracking-wider mb-1 flex items-center gap-1" style={{ color: TEXT2 }}>
            <NotebookPen size={10} /> Notiz
          </p>
          <textarea
            defaultValue={file.note ?? ""}
            key={file.id}
            onBlur={e => onPatch(file.id, { note: e.target.value || undefined })}
            placeholder="Notiz hinzufügen…"
            rows={3}
            className="w-full text-[15px] md:text-sm px-3 py-2 rounded-lg outline-none resize-none"
            style={{ border: "1px solid rgba(0,0,0,0.15)", background: "rgba(255,255,255,0.7)", color: TEXT }}
          />
        </div>
        <div className="flex gap-2">
          <button onClick={() => onPatch(file.id, { starred: !file.starred }, { silent: true })}
            className="flex-1 flex items-center justify-center gap-1.5 py-3 md:py-2 rounded-xl text-sm transition-colors"
            style={file.starred
              ? { background: "#FF9F0A20", color: "#FF9F0A" }
              : { background: "rgba(0,0,0,0.06)", color: TEXT2 }}>
            <Star size={14} fill={file.starred ? "#FF9F0A" : "none"} />
            {file.starred ? "Favorit" : "Favorisieren"}
          </button>
          <button onClick={() => onDownload(file)}
            className="flex items-center justify-center p-3 md:p-2 rounded-xl transition-colors hover:bg-black/10"
            style={{ background: "rgba(0,0,0,0.06)", color: TEXT2 }} title="Herunterladen">
            <Download size={16} />
          </button>
          {file.deletedAt ? (
            <button onClick={() => onRestore([file.id])}
              className="flex items-center justify-center p-3 md:p-2 rounded-xl bg-emerald-50 text-emerald-600 hover:bg-emerald-100 transition-colors"
              title="Wiederherstellen">
              <RotateCcw size={16} />
            </button>
          ) : (
            <button onClick={() => onSoftDelete([file.id])}
              className="flex items-center justify-center p-3 md:p-2 rounded-xl bg-red-50 text-red-500 hover:bg-red-100 transition-colors"
              title="In Papierkorb">
              <Trash2 size={16} />
            </button>
          )}
        </div>
        {file.deletedAt && (
          <button onClick={() => onConfirmPurge([file.id])}
            className="w-full flex items-center justify-center gap-2 py-3 md:py-2 rounded-xl text-sm text-red-500 hover:bg-red-50 transition-colors">
            <Trash2 size={14} /> Endgültig löschen
          </button>
        )}
        <button onClick={() => onPreview(file)}
          className="w-full flex items-center justify-center gap-2 py-3 md:py-2 rounded-xl text-sm transition-colors hover:bg-black/10"
          style={{ background: "rgba(0,0,0,0.06)", color: TEXT }}>
          <Eye size={14} /> In neuem Tab öffnen
        </button>
      </div>
    </>
  );
}

function EditableText({ value, onSave }: { value: string; onSave: (v: string) => void }) {
  const [editing, setEditing] = useState(false);
  const [draft,   setDraft]   = useState(value);
  useEffect(() => { setDraft(value); }, [value]);
  function commit() { setEditing(false); if (draft.trim() && draft !== value) onSave(draft.trim()); else setDraft(value); }
  if (editing) return (
    <input autoFocus value={draft} onChange={e => setDraft(e.target.value)}
      onBlur={commit} onKeyDown={e => { if (e.key === "Enter") commit(); if (e.key === "Escape") { setDraft(value); setEditing(false); } }}
      className="w-full text-[15px] md:text-sm px-3 py-2 md:py-1 rounded-lg outline-none bg-white"
      style={{ border: `1px solid ${ACCENT}`, color: TEXT }} />
  );
  return (
    <button onClick={() => setEditing(true)}
      className="text-sm font-medium break-all text-left w-full transition-opacity hover:opacity-70"
      style={{ color: TEXT }} title="Klicken zum Bearbeiten">
      {value}
    </button>
  );
}

function TagInput({ onAdd }: { onAdd: (tag: string) => void }) {
  const [val, setVal] = useState("");
  function submit() { const t = val.trim(); if (t) { onAdd(t); setVal(""); } }
  return (
    <div className="flex gap-1 mt-1.5">
      <input value={val} onChange={e => setVal(e.target.value)}
        onKeyDown={e => { if (e.key === "Enter") submit(); }}
        placeholder="Tag hinzufügen…"
        className="flex-1 text-[13px] px-2.5 py-2 md:py-1 rounded-lg outline-none"
        style={{ border: "1px solid rgba(0,0,0,0.15)", background: "rgba(255,255,255,0.7)", color: TEXT }} />
      <button onClick={submit} className="px-3 py-2 md:py-1 text-xs rounded-lg text-white font-medium hover:opacity-80 transition-opacity"
        style={{ background: ACCENT }}>
        +
      </button>
    </div>
  );
}

function BulkCategoryPicker({ categories, onPick }: { categories: Category[]; onPick: (id: string | null) => void }) {
  const [open, setOpen] = useState(false);
  return (
    <div className="relative">
      <button onClick={() => setOpen(o => !o)}
        className="text-xs px-3 py-1.5 rounded-lg flex items-center gap-1 hover:bg-black/5"
        style={{ background: "rgba(0,0,0,0.04)", color: TEXT }}>
        <Tag size={12} /> <span className="hidden sm:inline">Kategorie</span> <ChevronDown size={11} />
      </button>
      {open && (
        <>
          <div className="fixed inset-0 z-10" onClick={() => setOpen(false)} />
          <div className="absolute right-0 mt-1 z-20 rounded-xl shadow-xl overflow-hidden min-w-[180px]"
            style={{ background: "rgba(255,255,255,0.97)", backdropFilter: "blur(20px)", border: "1px solid rgba(0,0,0,0.08)" }}>
            <button onClick={() => { onPick(null); setOpen(false); }}
              className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-black/5 text-left" style={{ color: TEXT2 }}>
              <X size={12} /> Keine
            </button>
            <div className="h-px mx-2" style={{ background: "rgba(0,0,0,0.07)" }} />
            {categories.map(cat => (
              <button key={cat.id} onClick={() => { onPick(cat.id); setOpen(false); }}
                className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-black/5 text-left" style={{ color: TEXT }}>
                <span className="w-3 h-3 rounded-full" style={{ background: cat.color }} />
                {cat.name}
              </button>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

function GridView({ files, categories, selectedIds, focusedId, inTrash, isMobile, onSelect, onStar, onPurge, onRestore, onOpen, onContextMenu }: {
  files: DocFile[]; categories: Category[]; selectedIds: Set<string>; focusedId: string | null; inTrash: boolean; isMobile: boolean;
  onSelect: (f: DocFile, e: React.MouseEvent) => void; onStar: (id: string) => void;
  onPurge: (id: string) => void; onRestore: (id: string) => void;
  onOpen: (f: DocFile) => void; onContextMenu: (m: { x: number; y: number; fileId: string }) => void;
}) {
  return (
    <div className="grid gap-3 pt-2"
      style={{ gridTemplateColumns: isMobile
        ? "repeat(auto-fill, minmax(150px, 1fr))"
        : "repeat(auto-fill, minmax(160px, 1fr))" }}>
      {files.map((file, i) => {
        const cat = categories.find(c => c.id === file.categoryId);
        const isSelected = selectedIds.has(file.id);
        const isFocused  = focusedId === file.id;
        return (
          <motion.div key={file.id}
            initial={i < STAGGER_LIMIT ? { opacity: 0, y: 12 } : false}
            animate={{ opacity: 1, y: 0 }}
            transition={i < STAGGER_LIMIT ? { delay: i * 0.03 } : { duration: 0 }}
            onClick={e => onSelect(file, e)}
            onDoubleClick={() => onOpen(file)}
            onContextMenu={e => { e.preventDefault(); onContextMenu({ x: e.clientX, y: e.clientY, fileId: file.id }); }}
            className="relative flex flex-col items-center p-3 md:p-4 rounded-2xl cursor-pointer transition-all group"
            style={isSelected
              ? { background: "rgba(255,255,255,0.92)", border: `1.5px solid ${ACCENT}${isFocused ? "" : "80"}`, boxShadow: "0 4px 20px rgba(255,128,145,0.15)", backdropFilter: BLUR }
              : { background: GLASS_SM, border: "1px solid rgba(255,255,255,0.5)", backdropFilter: BLUR }}>
            <button onClick={e => { e.stopPropagation(); onStar(file.id); }}
              className={`absolute top-2 right-2 p-1 transition-all ${file.starred ? "opacity-100" : isMobile ? "opacity-60" : "opacity-0 group-hover:opacity-100"}`}>
              <Star size={14} fill={file.starred ? "#FF9F0A" : "none"} style={{ color: file.starred ? "#FF9F0A" : TEXT2 }} />
            </button>
            <div className="w-16 h-16 flex items-center justify-center mb-3 rounded-xl overflow-hidden" style={{ background: "rgba(0,0,0,0.05)" }}>
              <FileVisual file={file} size={36} />
            </div>
            <p className="text-xs font-medium text-center line-clamp-2 leading-tight" style={{ color: TEXT }}>{file.name}</p>
            {file.documentDate && (
              <p className="text-[10px] mt-0.5 flex items-center gap-0.5" style={{ color: TEXT2 }}>
                <CalendarDays size={9} />{formatDocDate(file.documentDate)}
              </p>
            )}
            {file.frist && (() => {
              const s = fristStatus(file.frist);
              const c = s ? FRIST_STYLE[s] : null;
              return c ? (
                <span className="mt-0.5 text-[10px] px-1.5 py-0.5 rounded-full border font-medium flex items-center gap-0.5"
                  style={{ background: c.bg, color: c.text, borderColor: c.border }}>
                  <AlarmClock size={8} />
                  {s === "overdue" ? "Überfällig" : s === "soon" ? "Bald fällig" : formatDocDate(file.frist)}
                </span>
              ) : null;
            })()}
            {file.tags?.length > 0 && <div className="mt-1.5 w-full flex justify-center"><TagChips tags={file.tags} max={2} /></div>}
            {cat && (
              <span className="mt-1 text-[10px] px-1.5 py-0.5 rounded-full font-medium"
                style={{ background: cat.color + "20", color: cat.color }}>{cat.name}</span>
            )}
            {inTrash && (
              <div className={`mt-1.5 flex gap-1 ${isMobile ? "opacity-100" : "opacity-0 group-hover:opacity-100"} transition-opacity`}>
                <button onClick={e => { e.stopPropagation(); onRestore(file.id); }}
                  className="p-1.5 rounded-lg hover:bg-emerald-50" title="Wiederherstellen" style={{ color: "#059669" }}>
                  <RotateCcw size={13} />
                </button>
                <button onClick={e => { e.stopPropagation(); onPurge(file.id); }}
                  className="p-1.5 rounded-lg hover:bg-red-50" title="Endgültig löschen" style={{ color: "#DC2626" }}>
                  <Trash2 size={13} />
                </button>
              </div>
            )}
          </motion.div>
        );
      })}
    </div>
  );
}

function ListView({ files, categories, selectedIds, focusedId, inTrash, isMobile, onSelect, onStar, onSoftDelete, onPurge, onRestore, onOpen, onDownload, onContextMenu }: {
  files: DocFile[]; categories: Category[]; selectedIds: Set<string>; focusedId: string | null; inTrash: boolean; isMobile: boolean;
  onSelect: (f: DocFile, e: React.MouseEvent) => void; onStar: (id: string) => void;
  onSoftDelete: (id: string) => void; onPurge: (id: string) => void; onRestore: (id: string) => void;
  onOpen: (f: DocFile) => void; onDownload: (f: DocFile) => void;
  onContextMenu: (m: { x: number; y: number; fileId: string }) => void;
}) {
  return (
    <div className="flex flex-col gap-1 pt-2">
      {files.map((file, i) => {
        const cat = categories.find(c => c.id === file.categoryId);
        const isSelected = selectedIds.has(file.id);
        return (
          <motion.div key={file.id}
            initial={i < STAGGER_LIMIT ? { opacity: 0, x: -8 } : false}
            animate={{ opacity: 1, x: 0 }}
            transition={i < STAGGER_LIMIT ? { delay: i * 0.02 } : { duration: 0 }}
            onClick={e => onSelect(file, e)}
            onDoubleClick={() => onOpen(file)}
            onContextMenu={e => { e.preventDefault(); onContextMenu({ x: e.clientX, y: e.clientY, fileId: file.id }); }}
            className="flex items-center gap-3 px-3 md:px-4 py-3 md:py-2.5 rounded-xl cursor-pointer transition-all group"
            style={isSelected
              ? { background: "rgba(255,255,255,0.92)", border: `1.5px solid ${ACCENT}${focusedId === file.id ? "" : "80"}`, backdropFilter: BLUR }
              : { background: GLASS_SM, border: "1px solid rgba(255,255,255,0.5)", backdropFilter: BLUR }}>
            <div className="w-10 h-10 md:w-8 md:h-8 flex items-center justify-center rounded-lg flex-shrink-0 overflow-hidden" style={{ background: "rgba(0,0,0,0.05)" }}>
              <FileVisual file={file} size={isMobile ? 22 : 18} />
            </div>
            <div className="flex-1 min-w-0">
              <p className="text-sm font-medium truncate" style={{ color: TEXT }}>{file.name}</p>
              <div className="flex items-center gap-2 mt-0.5 flex-wrap">
                <p className="text-[11px]" style={{ color: TEXT2 }}>{formatDate(file.uploadedAt)}{file.documentDate ? ` · ${formatDocDate(file.documentDate)}` : ""}</p>
                {file.frist && (() => {
                  const s = fristStatus(file.frist);
                  const c = s ? FRIST_STYLE[s] : null;
                  return c ? (
                    <span className="inline-flex items-center gap-0.5 text-[10px] px-1.5 py-0.5 rounded-full border font-medium"
                      style={{ background: c.bg, color: c.text, borderColor: c.border }}>
                      <AlarmClock size={8} />
                      {s === "overdue" ? "Überfällig" : s === "soon" ? "Bald fällig" : formatDocDate(file.frist)}
                    </span>
                  ) : null;
                })()}
              </div>
              {file.tags?.length > 0 && !isMobile && <div className="mt-1"><TagChips tags={file.tags} max={4} /></div>}
            </div>
            {cat && (
              <span className="text-[11px] px-2 py-0.5 rounded-full font-medium flex-shrink-0 hidden sm:inline-block"
                style={{ background: cat.color + "20", color: cat.color }}>{cat.name}</span>
            )}
            <div className={`flex items-center gap-0.5 flex-shrink-0 ${isMobile ? "opacity-100" : "opacity-0 group-hover:opacity-100"} transition-opacity`}>
              <button onClick={e => { e.stopPropagation(); onStar(file.id); }} className="p-2 rounded-lg hover:bg-black/5">
                <Star size={14} fill={file.starred ? "#FF9F0A" : "none"} style={{ color: file.starred ? "#FF9F0A" : TEXT2 }} />
              </button>
              {!isMobile && (
                <button onClick={e => { e.stopPropagation(); onDownload(file); }} className="p-2 rounded-lg hover:bg-black/5" style={{ color: TEXT2 }}>
                  <Download size={14} />
                </button>
              )}
              {inTrash ? (
                <>
                  <button onClick={e => { e.stopPropagation(); onRestore(file.id); }} className="p-2 rounded-lg hover:bg-emerald-50" style={{ color: "#059669" }} title="Wiederherstellen">
                    <RotateCcw size={14} />
                  </button>
                  <button onClick={e => { e.stopPropagation(); onPurge(file.id); }} className="p-2 rounded-lg hover:bg-red-50" style={{ color: "#DC2626" }} title="Endgültig löschen">
                    <Trash2 size={14} />
                  </button>
                </>
              ) : (
                <button onClick={e => { e.stopPropagation(); onSoftDelete(file.id); }} className="p-2 rounded-lg hover:bg-red-50 hover:text-red-500" style={{ color: TEXT2 }}>
                  <Trash2 size={14} />
                </button>
              )}
            </div>
          </motion.div>
        );
      })}
    </div>
  );
}

function EmptyState({ sidebar, search, onUpload }: { sidebar: string; search: string; onUpload: () => void }) {
  return (
    <div className="flex flex-col items-center justify-center h-full gap-4 text-center pb-12">
      <div className="w-20 h-20 rounded-3xl flex items-center justify-center"
        style={{ background: "rgba(255,255,255,0.6)", backdropFilter: "blur(20px)" }}>
        {search ? <Search size={32} style={{ color: TEXT2 }} />
          : sidebar === "trash" ? <Trash2 size={32} style={{ color: TEXT2 }} />
          : <Sparkles size={32} style={{ color: ACCENT }} />}
      </div>
      <div>
        <p className="font-semibold mb-1" style={{ color: TEXT }}>
          {search ? "Keine Ergebnisse"
            : sidebar === "trash" ? "Papierkorb ist leer"
            : "Noch keine Dokumente"}
        </p>
        <p className="text-sm max-w-xs" style={{ color: TEXT2 }}>
          {search ? `Keine Dokumente für „${search}" gefunden.`
            : sidebar === "starred" ? "Markiere Dokumente als Favoriten, um sie hier zu sehen."
            : sidebar === "trash"   ? "Gelöschte Dokumente erscheinen hier und können wiederhergestellt werden."
            : "Lade Dokumente hoch oder ziehe sie in dieses Fenster."}
        </p>
      </div>
      {!search && sidebar === "all" && (
        <button onClick={onUpload}
          className="flex items-center gap-2 px-5 py-2.5 rounded-xl text-sm font-medium text-white transition-opacity hover:opacity-90"
          style={{ background: ACCENT }}>
          <Upload size={14} /> Dateien hochladen
        </button>
      )}
    </div>
  );
}
