import { useState, useEffect, useRef, useCallback } from 'react';
import { api, S } from '../lib.js';

// ── Konstanten ────────────────────────────────────────────────────────────────
const COLORS = ['#FFFFFF','#FF6B9D','#4ECDC4','#FFE66D','#60A5FA','#A78BFA','#FB923C','#4ADE80','#F87171','#000000'];
const SIZES  = [2, 4, 8, 14, 22];
const FONT_SIZES = [12, 16, 20, 28, 40, 56];
const TOOLS  = [
  { id:'select',  label:'↖', tip:'Auswählen / Verschieben' },
  { id:'pen',     label:'✏', tip:'Stift' },
  { id:'line',    label:'╱', tip:'Linie' },
  { id:'rect',    label:'▭', tip:'Rechteck' },
  { id:'ellipse', label:'◯', tip:'Ellipse' },
  { id:'text',    label:'T', tip:'Text' },
  { id:'eraser',  label:'🧹', tip:'Radierer' },
];
const CURSOR_COLORS = ['#FF6B9D','#4ECDC4','#FFE66D','#60A5FA','#A78BFA','#FB923C'];
const BG = '#0D0D0F';
const getId = () => Math.random().toString(36).slice(2);
const HANDLE_R = 6; // Resize-Handle Radius in Screen-Px

// ── Bounding-Box-Berechnung ───────────────────────────────────────────────────
function getBounds(el) {
  switch (el.type) {
    case 'pen': case 'eraser': {
      if (!el.points?.length) return null;
      const xs = el.points.map(p => p.x), ys = el.points.map(p => p.y);
      const pad = (el.size||4) * (el.type==='eraser'?3:1);
      return { x: Math.min(...xs)-pad, y: Math.min(...ys)-pad,
               w: Math.max(...xs)-Math.min(...xs)+pad*2, h: Math.max(...ys)-Math.min(...ys)+pad*2 };
    }
    case 'line': {
      const pad = el.size||4;
      return { x: Math.min(el.x1,el.x2)-pad, y: Math.min(el.y1,el.y2)-pad,
               w: Math.abs(el.x2-el.x1)+pad*2, h: Math.abs(el.y2-el.y1)+pad*2 };
    }
    case 'rect':
      return { x: Math.min(el.x,el.x+el.w), y: Math.min(el.y,el.y+el.h),
               w: Math.abs(el.w), h: Math.abs(el.h) };
    case 'ellipse':
      return { x: el.cx-Math.abs(el.rx), y: el.cy-Math.abs(el.ry),
               w: Math.abs(el.rx)*2, h: Math.abs(el.ry)*2 };
    case 'text': {
      const fs = el.fontSize || 20;
      const w  = (el.text?.length || 4) * fs * 0.6;
      return { x: el.x, y: el.y - fs, w, h: fs * 1.3 };
    }
    default: return null;
  }
}

// Punkt-in-Bounds Hit-Test
function hitTest(el, cx, cy) {
  const b = getBounds(el);
  if (!b) return false;
  return cx >= b.x && cx <= b.x+b.w && cy >= b.y && cy <= b.y+b.h;
}

// ── Element verschieben ───────────────────────────────────────────────────────
function moveElement(el, dx, dy) {
  switch (el.type) {
    case 'pen': case 'eraser':
      return { ...el, points: el.points.map(p => ({ x: p.x+dx, y: p.y+dy })) };
    case 'line':
      return { ...el, x1: el.x1+dx, y1: el.y1+dy, x2: el.x2+dx, y2: el.y2+dy };
    case 'rect':
      return { ...el, x: el.x+dx, y: el.y+dy };
    case 'ellipse':
      return { ...el, cx: el.cx+dx, cy: el.cy+dy };
    case 'text':
      return { ...el, x: el.x+dx, y: el.y+dy };
    default: return el;
  }
}

// ── Element skalieren (Resize via Ecke rechts-unten) ─────────────────────────
function resizeElement(el, nx, ny) {
  const b = getBounds(el);
  if (!b) return el;
  const scaleX = b.w > 1 ? Math.max(0.05, (nx - b.x) / b.w) : 1;
  const scaleY = b.h > 1 ? Math.max(0.05, (ny - b.y) / b.h) : 1;
  switch (el.type) {
    case 'pen': case 'eraser':
      return { ...el, points: el.points.map(p => ({ x: b.x + (p.x-b.x)*scaleX, y: b.y + (p.y-b.y)*scaleY })) };
    case 'line':
      return { ...el, x1: b.x+(el.x1-b.x)*scaleX, y1: b.y+(el.y1-b.y)*scaleY,
               x2: b.x+(el.x2-b.x)*scaleX, y2: b.y+(el.y2-b.y)*scaleY };
    case 'rect':
      return { ...el, w: el.w * scaleX, h: el.h * scaleY };
    case 'ellipse':
      return { ...el, rx: el.rx * scaleX, ry: el.ry * scaleY };
    case 'text':
      return { ...el, fontSize: Math.max(8, Math.round((el.fontSize||20) * ((scaleX+scaleY)/2))) };
    default: return el;
  }
}

// ── Canvas rendern ────────────────────────────────────────────────────────────
function renderElements(ctx, elements, vp, selectedId, forExport) {
  const W = ctx.canvas.width, H = ctx.canvas.height;
  ctx.save();
  // Hintergrund
  ctx.fillStyle = BG;
  ctx.fillRect(0, 0, W, H);

  ctx.translate(vp.x, vp.y);
  ctx.scale(vp.zoom, vp.zoom);

  for (const el of elements) {
    ctx.save();
    ctx.globalAlpha = 1;
    ctx.strokeStyle = el.color || '#fff';
    ctx.fillStyle   = el.color || '#fff';
    ctx.lineWidth   = (el.size || 4) / vp.zoom;
    ctx.lineCap     = 'round';
    ctx.lineJoin    = 'round';

    switch (el.type) {
      case 'pen': {
        if (!el.points?.length) break;
        ctx.beginPath();
        ctx.moveTo(el.points[0].x, el.points[0].y);
        for (const p of el.points.slice(1)) ctx.lineTo(p.x, p.y);
        ctx.stroke();
        break;
      }
      case 'eraser': {
        if (!el.points?.length) break;
        ctx.strokeStyle = BG;
        ctx.lineWidth = (el.size||4) * 3 / vp.zoom;
        ctx.beginPath();
        ctx.moveTo(el.points[0].x, el.points[0].y);
        for (const p of el.points.slice(1)) ctx.lineTo(p.x, p.y);
        ctx.stroke();
        break;
      }
      case 'line': {
        ctx.beginPath(); ctx.moveTo(el.x1, el.y1); ctx.lineTo(el.x2, el.y2); ctx.stroke();
        break;
      }
      case 'rect':
        ctx.strokeRect(el.x, el.y, el.w, el.h);
        break;
      case 'ellipse':
        ctx.beginPath();
        ctx.ellipse(el.cx, el.cy, Math.abs(el.rx||1), Math.abs(el.ry||1), 0, 0, Math.PI*2);
        ctx.stroke();
        break;
      case 'text': {
        const fs = el.fontSize || 20;
        ctx.font = `${fs}px monospace`;
        ctx.fillText(el.text, el.x, el.y);
        break;
      }
    }
    ctx.restore();

    // Auswahl-Rahmen + Resize-Handle
    if (!forExport && selectedId === el.id) {
      const b = getBounds(el);
      if (b) {
        ctx.save();
        ctx.strokeStyle = '#4ECDC4';
        ctx.lineWidth   = 1.5 / vp.zoom;
        ctx.setLineDash([4/vp.zoom, 3/vp.zoom]);
        ctx.strokeRect(b.x-2/vp.zoom, b.y-2/vp.zoom, b.w+4/vp.zoom, b.h+4/vp.zoom);
        ctx.setLineDash([]);
        // Resize-Handle rechts-unten
        const hx = b.x+b.w, hy = b.y+b.h, hr = HANDLE_R/vp.zoom;
        ctx.fillStyle = '#4ECDC4';
        ctx.beginPath(); ctx.arc(hx, hy, hr, 0, Math.PI*2); ctx.fill();
        ctx.restore();
      }
    }
  }
  ctx.restore();
}

// Offscreen-Export (ohne Auswahl-Rahmen, original Viewport)
function exportToPng(elements, title) {
  if (!elements?.length) return; // Nichts zu exportieren
  let minX=Infinity,minY=Infinity,maxX=-Infinity,maxY=-Infinity;
  for (const el of elements) {
    const b = getBounds(el); if (!b) continue;
    minX=Math.min(minX,b.x); minY=Math.min(minY,b.y);
    maxX=Math.max(maxX,b.x+b.w); maxY=Math.max(maxY,b.y+b.h);
  }
  if (!isFinite(minX)) { minX=0; minY=0; maxX=800; maxY=600; }
  const pad=40, W=Math.max(800,maxX-minX+pad*2), H=Math.max(600,maxY-minY+pad*2);
  const offscreen = document.createElement('canvas');
  offscreen.width=W; offscreen.height=H;
  const ctx = offscreen.getContext('2d');
  renderElements(ctx, elements, { x: -minX+pad, y: -minY+pad, zoom:1 }, null, true);
  const a = document.createElement('a');
  a.href = offscreen.toDataURL('image/png');
  a.download = (title||'whiteboard').replace(/[^\w\s-]/g,'').trim()+'.png';
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}

// ── Share-Modal ───────────────────────────────────────────────────────────────
function ShareModal({ wbId, onClose, toast }) {
  const [allUsers, setAllUsers] = useState([]);
  const [perms,    setPerms]    = useState([]);
  const [saving,   setSaving]   = useState(false);
  useEffect(() => {
    Promise.all([api('/tools/whiteboard/users-list'), api(`/tools/whiteboard/${wbId}/data`)])
      .then(([u,d]) => { setAllUsers(u.users||[]); setPerms(d.permissions||[]); }).catch(()=>{});
  }, [wbId]);
  const roleOf = uid => perms.find(p=>p.user_id===uid)?.role||'none';
  const setRole = (uid, role) => setPerms(prev => {
    const f = prev.filter(p=>p.user_id!==uid);
    return role==='none' ? f : [...f,{user_id:uid,role}];
  });
  const save = async () => {
    setSaving(true);
    try {
      await api(`/tools/whiteboard/${wbId}/permissions`,{ method:'PUT',
        body:{ permissions: allUsers.map(u=>({ user_id:u.id, role: roleOf(u.id)==='none'?null:roleOf(u.id) })) }});
      toast('Berechtigungen gespeichert ✓'); onClose();
    } catch(e){ toast(e.message,'error'); } finally { setSaving(false); }
  };
  const mob = window.innerWidth < 768;
  return (
    <div onClick={e=>e.target===e.currentTarget&&onClose()} style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.75)',zIndex:2000,
      display:'flex',alignItems:mob?'flex-end':'center',justifyContent:'center',padding:mob?0:20}}>
      <div style={{background:'#1a1a1e',border:'1px solid rgba(255,255,255,0.12)',borderRadius:mob?'16px 16px 0 0':14,
        width:'100%',maxWidth:440,display:'flex',flexDirection:'column',maxHeight:mob?'80vh':'75vh',
        paddingBottom:mob?'calc(56px + env(safe-area-inset-bottom,0px))':0}}>
        <div style={{padding:'18px 20px 0',flexShrink:0}}>
          {mob&&<div style={{width:36,height:4,background:'rgba(255,255,255,0.15)',borderRadius:2,margin:'0 auto 14px'}}/>}
          <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:14}}>
            <div style={{...S.head,marginBottom:0}}>TEILEN</div>
            <button onClick={onClose} style={{background:'transparent',border:'none',color:'rgba(255,255,255,0.4)',cursor:'pointer',fontSize:18}}>✕</button>
          </div>
        </div>
        <div style={{overflowY:'auto',padding:'0 20px',flex:1}}>
          {allUsers.length===0&&<div style={{color:'rgba(255,255,255,0.3)',fontFamily:'monospace',fontSize:12}}>Keine anderen Benutzer.</div>}
          {allUsers.map(u=>(
            <div key={u.id} style={{display:'flex',alignItems:'center',gap:8,padding:'8px 0',borderBottom:'1px solid rgba(255,255,255,0.05)'}}>
              <span style={{flex:1,fontFamily:'monospace',fontSize:13,color:'#fff'}}>{u.username}</span>
              {['none','view','edit'].map(r=>(
                <button key={r} onClick={()=>setRole(u.id,r)} style={{...S.btn(r==='edit'?'#4ECDC4':r==='view'?'#60A5FA':'#888888',true),
                  fontSize:10,padding:'3px 8px',
                  background:roleOf(u.id)===r?(r==='edit'?'rgba(78,205,196,0.2)':r==='view'?'rgba(96,165,250,0.2)':'rgba(136,136,136,0.2)'):'transparent',
                  fontWeight:roleOf(u.id)===r?700:400}}>
                  {r==='none'?'✕ kein':r==='view'?'👁 lesen':'✏ bearbeiten'}
                </button>
              ))}
            </div>
          ))}
        </div>
        <div style={{padding:'14px 20px 20px',flexShrink:0,borderTop:'1px solid rgba(255,255,255,0.07)',display:'flex',gap:8,justifyContent:'flex-end'}}>
          <button onClick={onClose} style={S.btn('#888888',true)}>Abbrechen</button>
          <button onClick={save} disabled={saving} style={S.btn('#4ECDC4',true)}>{saving?'…':'Speichern'}</button>
        </div>
      </div>
    </div>
  );
}

// ── Hauptkomponente ───────────────────────────────────────────────────────────
export default function Whiteboard({ toast, mobile }) {
  // View
  const [view,      setView]      = useState('list');
  const [boards,    setBoards]    = useState([]);
  const [loading,   setLoading]   = useState(true);
  const [current,   setCurrent]   = useState(null);
  const [creating,  setCreating]  = useState(false);
  const [newTitle,  setNewTitle]  = useState('');
  const [editingId, setEditingId] = useState(null);
  const [editTitle, setEditTitle] = useState('');

  // Canvas
  const [elements,  setElements]  = useState([]);
  const [undoStack, setUndoStack] = useState([]);
  const [tool,      setTool]      = useState('pen');
  const [color,     setColor]     = useState('#FFFFFF');
  const [size,      setSize]      = useState(1);
  const [fontSize,  setFontSize]  = useState(2); // Index in FONT_SIZES
  const [viewport,  setViewport]  = useState({x:0,y:0,zoom:1});
  const [saving,    setSaving]    = useState(false);
  const [shareModal,setShareModal]= useState(false);
  const [textInput, setTextInput] = useState(null);
  const [textPreview, setTextPreview] = useState(null); // Live-Vorschau während Tippen
  const [selectedId,setSelectedId]= useState(null);

  // Kollaboration
  const [collab, setCollab] = useState([]);
  const wsRef        = useRef(null);
  const cursorTimers = useRef({});

  // Canvas Refs
  const canvasRef   = useRef(null);
  const drawing     = useRef(false);
  const currentEl   = useRef(null);
  const startPos    = useRef({x:0,y:0});
  const isPanning   = useRef(false);
  const isDragging  = useRef(false); // Element verschieben
  const isResizing  = useRef(false); // Element resize
  const panStart    = useRef({x:0,y:0});
  const dragStart   = useRef({x:0,y:0}); // Startpos für Drag
  const vpRef       = useRef({x:0,y:0,zoom:1});
  const elementsRef = useRef([]);
  const selectedRef = useRef(null);
  const textPreviewRef = useRef(null);

  // ── Hilfsfunktionen ──────────────────────────────────────────────────────
  const syncVp = (vp) => { vpRef.current=vp; setViewport({...vp}); };

  const toCanvas = (e) => {
    const rect = canvasRef.current.getBoundingClientRect();
    const sx = (e.clientX ?? e.touches?.[0]?.clientX ?? 0) - rect.left;
    const sy = (e.clientY ?? e.touches?.[0]?.clientY ?? 0) - rect.top;
    return { x:(sx-vpRef.current.x)/vpRef.current.zoom, y:(sy-vpRef.current.y)/vpRef.current.zoom, sx, sy };
  };

  // Ist der Pointer auf dem Resize-Handle des selektierten Elements?
  const onResizeHandle = (cx, cy) => {
    if (!selectedRef.current) return false;
    const el = elementsRef.current.find(e=>e.id===selectedRef.current);
    if (!el) return false;
    const b = getBounds(el); if (!b) return false;
    const hxS = (b.x+b.w)*vpRef.current.zoom + vpRef.current.x;
    const hyS = (b.y+b.h)*vpRef.current.zoom + vpRef.current.y;
    const rect = canvasRef.current.getBoundingClientRect();
    const mxS = cx + rect.left - rect.left;
    const myS = cy + rect.top  - rect.top;
    // cx/cy sind schon in Screen-Koordinaten (sx,sy aus toCanvas)
    return Math.hypot(cx - (b.x+b.w)*vpRef.current.zoom - vpRef.current.x,
                      cy - (b.y+b.h)*vpRef.current.zoom - vpRef.current.y) <= HANDLE_R + 4;
  };

  // ── Element-Operationen ──────────────────────────────────────────────────
  const broadcast = (els) => {
    wsRef.current?.readyState===1 && wsRef.current.send(JSON.stringify({type:'elements',elements:els}));
  };

  const pushElement = (el) => {
    const next = [...elementsRef.current, el];
    elementsRef.current = next;
    setUndoStack(prev => [...prev, elementsRef.current.slice(0,-1)]);
    setElements([...next]);
    broadcast(next);
  };

  const updateLastElement = (el) => {
    const next = [...elementsRef.current.slice(0,-1), el];
    elementsRef.current = next;
    setElements([...next]);
  };

  const finalizeLastElement = (el) => {
    const next = [...elementsRef.current.slice(0,-1), el];
    elementsRef.current = next;
    setElements([...next]);
    broadcast(next);
  };

  const updateElement = (id, updater) => {
    const next = elementsRef.current.map(e => e.id===id ? updater(e) : e);
    elementsRef.current = next;
    setElements([...next]);
    broadcast(next);
    setUndoStack(prev => [...prev, elementsRef.current]);
  };

  const undo = () => {
    if (!undoStack.length) return;
    const prev = undoStack[undoStack.length-1];
    setUndoStack(s => s.slice(0,-1));
    elementsRef.current = prev;
    setElements([...prev]);
    broadcast(prev);
    setSelectedId(null); selectedRef.current=null;
  };

  // ── Laden / Speichern ────────────────────────────────────────────────────
  const loadBoards = useCallback(() => {
    setLoading(true);
    api('/tools/whiteboard').then(d=>setBoards(d.whiteboards||[])).catch(()=>toast('Fehler','error')).finally(()=>setLoading(false));
  }, []);

  const renameBoard = async (id, title) => {
    if (!title?.trim()) return;
    try {
      await api(`/tools/whiteboard/${id}/title`,{method:'PATCH',body:{title:title.trim()}});
      setBoards(prev=>prev.map(b=>b.id===id?{...b,title:title.trim()}:b));
      if (current?.id===id) setCurrent(prev=>({...prev,title:title.trim()}));
    } catch(e){ toast(e.message,'error'); } finally { setEditingId(null); }
  };

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

  const openBoard = async (board) => {
    try {
      const d = await api(`/tools/whiteboard/${board.id}/data`);
      const els = JSON.parse(typeof d.elements==='string'?d.elements:JSON.stringify(d.elements||[]));
      const vp  = JSON.parse(typeof d.viewport==='string'?d.viewport:JSON.stringify(d.viewport||{x:0,y:0,zoom:1}));
      elementsRef.current=els; vpRef.current=vp;
      setElements(els); setViewport(vp); setUndoStack([]);
      setCurrent({id:board.id,title:d.title,role:d.role});
      setView('canvas'); connectWs(board.id);
    } catch(e){ toast(e.message,'error'); }
  };

  const closeBoard = () => {
    disconnectWs(); setView('list'); setCurrent(null);
    setElements([]); setUndoStack([]); setCollab([]);
    setSelectedId(null); selectedRef.current=null;
    loadBoards();
  };

  const save = async () => {
    if (saving) return; setSaving(true);
    try {
      await api(`/tools/whiteboard/${current.id}/save`,{body:{elements:elementsRef.current,viewport:vpRef.current}});
      toast('Gespeichert ✓');
    } catch(e){ toast(e.message,'error'); } finally { setSaving(false); }
  };

  // ── WebSocket ─────────────────────────────────────────────────────────────
  const connectWs = useCallback((wbId) => {
    const token = localStorage.getItem('sk_token');
    const proto = location.protocol==='https:'?'wss':'ws';
    const ws = new WebSocket(`${proto}://${location.host}/ws/whiteboard?token=${token}&id=${wbId}`);
    wsRef.current = ws;
    const cc = {}; let ci=0;
    ws.onmessage = (e) => {
      const msg = JSON.parse(e.data);
      switch(msg.type) {
        case 'user_join':
          if(!cc[msg.userId]) cc[msg.userId]=CURSOR_COLORS[ci++%CURSOR_COLORS.length];
          setCollab(prev=>[...prev.filter(c=>c.userId!==msg.userId),{userId:msg.userId,username:msg.username,color:cc[msg.userId],x:0,y:0}]); break;
        case 'user_leave':
          setCollab(prev=>prev.filter(c=>c.userId!==msg.userId)); break;
        case 'active_users':
          msg.users.forEach(u=>{ if(!cc[u.userId]) cc[u.userId]=CURSOR_COLORS[ci++%CURSOR_COLORS.length]; });
          setCollab(msg.users.map(u=>({...u,color:cc[u.userId]||CURSOR_COLORS[0],x:0,y:0}))); break;
        case 'cursor':
          if(!cc[msg.userId]) cc[msg.userId]=CURSOR_COLORS[ci++%CURSOR_COLORS.length];
          setCollab(prev=>prev.map(c=>c.userId===msg.userId?{...c,x:msg.x,y:msg.y}:c));
          clearTimeout(cursorTimers.current[msg.userId]);
          cursorTimers.current[msg.userId]=setTimeout(()=>setCollab(prev=>prev.map(c=>c.userId===msg.userId?{...c,x:-9999,y:-9999}:c)),3000); break;
        case 'elements':
          elementsRef.current=msg.elements; setElements([...msg.elements]); break;
      }
    };
    ws.onclose=()=>setCollab([]); ws.onerror=()=>{};
  },[]);

  const disconnectWs = useCallback(()=>{ wsRef.current?.close(); wsRef.current=null; setCollab([]); },[]);

  // ── Canvas-Rendering ──────────────────────────────────────────────────────
  const render = useCallback(() => {
    const canvas = canvasRef.current; if(!canvas) return;
    const ctx = canvas.getContext('2d');
    renderElements(ctx, elementsRef.current, vpRef.current, selectedRef.current, false);
    // Cursor anderer User
    for (const c of collab) {
      if (c.x===-9999) continue;
      const sx = c.x*vpRef.current.zoom+vpRef.current.x;
      const sy = c.y*vpRef.current.zoom+vpRef.current.y;
      ctx.save(); ctx.fillStyle=c.color; ctx.font='10px monospace';
      ctx.fillText('▶ '+c.username, sx+6, sy-4); ctx.restore();
    }
    // Text-Vorschau während Tippen
    if (textPreviewRef.current) {
      const { text, x, y, fontSize, color } = textPreviewRef.current;
      const vp = vpRef.current;
      const sx = x * vp.zoom + vp.x;
      const sy = y * vp.zoom + vp.y;
      ctx.save();
      ctx.font = `${fontSize * vp.zoom}px monospace`;
      ctx.fillStyle = color;
      ctx.globalAlpha = 0.85;
      ctx.fillText(text || '|', sx, sy);
      // Cursor-Blinken simulieren: kleiner Strich hinter dem Text
      if (text) {
        const tw = ctx.measureText(text).width;
        ctx.fillRect(sx + tw + 1, sy - fontSize * vp.zoom * 0.8, 2, fontSize * vp.zoom);
      }
      ctx.restore();
    }
  }, [collab, textPreview]);

  useEffect(()=>{ render(); }, [elements, viewport, selectedId, collab, render]);

  // Canvas-Resize
  useEffect(()=>{
    if(view!=='canvas') return;
    const resize=()=>{ const c=canvasRef.current; if(!c) return; c.width=c.offsetWidth; c.height=c.offsetHeight; render(); };
    resize(); window.addEventListener('resize',resize);
    return ()=>window.removeEventListener('resize',resize);
  },[view, render]);

  // ── Pointer-Events ────────────────────────────────────────────────────────
  const onPointerDown = (e) => {
    if (current?.role==='view') return;
    e.preventDefault();
    const { x, y, sx, sy } = toCanvas(e);

    // Panning: Mittelklick oder Leerzeichen-Drag (über CSS cursor:grab bei select-tool)
    if (e.button===1) {
      isPanning.current=true;
      panStart.current={ x: sx-vpRef.current.x, y: sy-vpRef.current.y }; return;
    }

    // Select-Tool
    if (tool==='select') {
      // Resize-Handle?
      if (onResizeHandle(sx, sy)) {
        isResizing.current=true;
        dragStart.current={x,y}; return;
      }
      // Element treffen?
      const hit = [...elementsRef.current].reverse().find(el=>hitTest(el,x,y));
      if (hit) {
        selectedRef.current=hit.id; setSelectedId(hit.id);
        isDragging.current=true;
        dragStart.current={x,y}; return;
      }
      // Leerer Klick → Auswahl aufheben
      selectedRef.current=null; setSelectedId(null); return;
    }

    // Text
    if (tool==='text') {
      textPreviewRef.current = { text:'', x, y, fontSize:FONT_SIZES[fontSize], color };
      render();
      setTextInput({ sx, sy, x, y }); return;
    }

    drawing.current=true; startPos.current={x,y};

    if (tool==='pen'||tool==='eraser') {
      const el={id:getId(),type:tool,color,size:SIZES[size],points:[{x,y}]};
      currentEl.current=el; pushElement(el);
    } else if (tool==='line') {
      const el={id:getId(),type:'line',color,size:SIZES[size],x1:x,y1:y,x2:x,y2:y};
      currentEl.current=el; pushElement(el);
    } else if (tool==='rect') {
      const el={id:getId(),type:'rect',color,size:SIZES[size],x,y,w:0,h:0};
      currentEl.current=el; pushElement(el);
    } else if (tool==='ellipse') {
      const el={id:getId(),type:'ellipse',color,size:SIZES[size],cx:x,cy:y,rx:0,ry:0};
      currentEl.current=el; pushElement(el);
    }
  };

  const onPointerMove = (e) => {
    const { x, y, sx, sy } = toCanvas(e);
    wsRef.current?.readyState===1 && wsRef.current.send(JSON.stringify({type:'cursor',x,y}));

    if (isPanning.current) {
      syncVp({ ...vpRef.current, x: sx-panStart.current.x, y: sy-panStart.current.y }); return;
    }

    if (isDragging.current && selectedRef.current) {
      const dx=x-dragStart.current.x, dy=y-dragStart.current.y;
      dragStart.current={x,y};
      const next=elementsRef.current.map(el=>el.id===selectedRef.current?moveElement(el,dx,dy):el);
      elementsRef.current=next; setElements([...next]); return;
    }

    if (isResizing.current && selectedRef.current) {
      const next=elementsRef.current.map(el=>el.id===selectedRef.current?resizeElement(el,x,y):el);
      elementsRef.current=next; setElements([...next]); return;
    }

    if (!drawing.current||!currentEl.current) return;
    const el=currentEl.current;

    if (tool==='pen'||tool==='eraser') {
      const updated={...el,points:[...el.points,{x,y}]};
      currentEl.current=updated; updateLastElement(updated);
    } else if (tool==='line') {
      const updated={...el,x2:x,y2:y}; currentEl.current=updated; updateLastElement(updated);
    } else if (tool==='rect') {
      const updated={...el,w:x-el.x,h:y-el.y}; currentEl.current=updated; updateLastElement(updated);
    } else if (tool==='ellipse') {
      const updated={...el,rx:x-el.cx,ry:y-el.cy}; currentEl.current=updated; updateLastElement(updated);
    }
  };

  const onPointerUp = () => {
    isPanning.current=false;
    if (isDragging.current||isResizing.current) {
      broadcast(elementsRef.current);
      setUndoStack(prev=>[...prev, elementsRef.current]);
    }
    isDragging.current=false; isResizing.current=false;
    if (!drawing.current) return;
    drawing.current=false;
    if (currentEl.current) { finalizeLastElement(currentEl.current); currentEl.current=null; }
  };

  const onWheel = (e) => {
    e.preventDefault();
    const delta=e.deltaY>0?0.9:1.1;
    const rect=canvasRef.current.getBoundingClientRect();
    const mx=e.clientX-rect.left, my=e.clientY-rect.top;
    const nz=Math.max(0.1,Math.min(8, vpRef.current.zoom*delta));
    syncVp({ x: mx-(mx-vpRef.current.x)*(nz/vpRef.current.zoom),
             y: my-(my-vpRef.current.y)*(nz/vpRef.current.zoom), zoom:nz });
  };

  const zoom = (factor) => {
    const nz=Math.max(0.1,Math.min(8,vpRef.current.zoom*factor));
    const canvas=canvasRef.current; if(!canvas) return;
    const mx=canvas.width/2, my=canvas.height/2;
    syncVp({ x: mx-(mx-vpRef.current.x)*(nz/vpRef.current.zoom),
             y: my-(my-vpRef.current.y)*(nz/vpRef.current.zoom), zoom:nz });
  };

  // Tastenkürzel
  useEffect(()=>{
    if(view!=='canvas') return;
    const onKey=(e)=>{
      if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA') return;
      if((e.ctrlKey||e.metaKey)&&e.key==='z'){ e.preventDefault(); undo(); }
      if((e.ctrlKey||e.metaKey)&&e.key==='s'){ e.preventDefault(); save(); }
      if((e.ctrlKey||e.metaKey)&&(e.key==='='||e.key==='+')){ e.preventDefault(); zoom(1.2); }
      if((e.ctrlKey||e.metaKey)&&e.key==='-'){ e.preventDefault(); zoom(0.8); }
      if((e.ctrlKey||e.metaKey)&&e.key==='0'){ e.preventDefault(); syncVp({x:0,y:0,zoom:1}); }
      if(e.key==='Escape'){ setSelectedId(null); selectedRef.current=null; }
      if((e.key==='Delete'||e.key==='Backspace')&&selectedRef.current&&e.target.tagName!=='INPUT'){
        e.preventDefault();
        const next=elementsRef.current.filter(el=>el.id!==selectedRef.current);
        setUndoStack(prev=>[...prev,elementsRef.current]);
        elementsRef.current=next; setElements([...next]); broadcast(next);
        setSelectedId(null); selectedRef.current=null;
      }
    };
    window.addEventListener('keydown',onKey);
    return ()=>window.removeEventListener('keydown',onKey);
  },[view, undoStack]);

  // ── Cursor-Style ──────────────────────────────────────────────────────────
  const getCursor = () => {
    if (tool==='select') return isDragging.current?'grabbing':isResizing.current?'nwse-resize':'default';
    if (tool==='eraser') return 'cell';
    if (tool==='text')   return 'text';
    return 'crosshair';
  };

  // ── Render: Liste ─────────────────────────────────────────────────────────
  if (view==='list') return (
    <div style={{padding:mobile?'14px 14px 90px':'36px 44px'}}>
      <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:24,flexWrap:'wrap'}}>
        <h2 style={{margin:0,fontSize:15,fontFamily:'monospace',color:'rgba(255,255,255,0.55)',letterSpacing:2,fontWeight:400}}>WHITEBOARD</h2>
        <div style={{flex:1}}/>
        {creating ? (
          <div style={{display:'flex',gap:6}}>
            <input autoFocus value={newTitle} onChange={e=>setNewTitle(e.target.value)}
              onKeyDown={async e=>{
                if(e.key==='Enter'&&newTitle.trim()){
                  try{ const wb=await api('/tools/whiteboard',{body:{title:newTitle.trim()}}); setCreating(false);setNewTitle(''); await openBoard(wb); }
                  catch(err){ toast(err.message,'error'); }
                }
                if(e.key==='Escape'){setCreating(false);setNewTitle('');}
              }}
              placeholder="Name des Whiteboards" style={{...S.inp,width:mobile?160:220,fontSize:12}}/>
            <button onClick={async()=>{
              if(!newTitle.trim()) return;
              try{ const wb=await api('/tools/whiteboard',{body:{title:newTitle.trim()}}); setCreating(false);setNewTitle(''); await openBoard(wb); }
              catch(err){ toast(err.message,'error'); }
            }} style={S.btn('#4ECDC4',true)}>Erstellen</button>
            <button onClick={()=>{setCreating(false);setNewTitle('');}} style={S.btn('#888888',true)}>✕</button>
          </div>
        ) : (
          <button onClick={()=>setCreating(true)} style={S.btn('#4ECDC4')}>+ Neu</button>
        )}
      </div>

      {loading&&<div style={{color:'rgba(255,255,255,0.3)',fontFamily:'monospace',fontSize:12}}>Lädt…</div>}
      {!loading&&boards.length===0&&(
        <div style={{...S.card,textAlign:'center',padding:40,color:'rgba(255,255,255,0.25)',fontFamily:'monospace',fontSize:12}}>
          Noch kein Whiteboard. Erstelle eines mit "+ Neu".
        </div>
      )}

      {boards.map(b=>(
        <div key={b.id} style={{...S.card,display:'flex',alignItems:'center',gap:12,marginBottom:8,cursor:'pointer'}}
          onClick={()=>{ if(editingId!==b.id) openBoard(b); }}>
          <div style={{fontSize:24}}>🖊</div>
          <div style={{flex:1,minWidth:0}}>
            {editingId===b.id?(
              <input autoFocus value={editTitle} onChange={e=>setEditTitle(e.target.value)}
                onKeyDown={e=>{if(e.key==='Enter') renameBoard(b.id,editTitle); if(e.key==='Escape') setEditingId(null);}}
                onBlur={()=>renameBoard(b.id,editTitle)}
                onClick={e=>e.stopPropagation()}
                style={{...S.inp,fontSize:12,fontWeight:700,padding:'3px 8px',width:'100%'}}/>
            ):(
              <div style={{color:'#fff',fontFamily:'monospace',fontSize:13,fontWeight:700}}
                onDoubleClick={e=>{e.stopPropagation();if(b.role==='owner'||b.role==='edit'){setEditingId(b.id);setEditTitle(b.title);}}}>
                {b.title}
              </div>
            )}
            <div style={{color:'rgba(255,255,255,0.3)',fontFamily:'monospace',fontSize:10,marginTop:2}}>
              {b.role==='owner'?'👑 Eigenes':b.role==='edit'?'✏ Bearbeiten':'👁 Lesen'}
              {b.owner_name&&b.role!=='owner'&&` · von ${b.owner_name}`}
              {' · '+new Date(b.updated_at).toLocaleDateString('de-DE',{day:'2-digit',month:'2-digit',year:'numeric'})}
            </div>
          </div>
          <div style={{display:'flex',gap:4,flexShrink:0}}>
            <button onClick={e=>{e.stopPropagation();
              api(`/tools/whiteboard/${b.id}/data`).then(d=>{
                const els=JSON.parse(typeof d.elements==='string'?d.elements:JSON.stringify(d.elements||[]));
                exportToPng(els,b.title);
              }).catch(()=>toast('Export fehlgeschlagen','error'));
            }} title="Als PNG exportieren"
              style={{background:'transparent',border:'none',color:'rgba(255,255,255,0.25)',cursor:'pointer',fontSize:14,padding:'4px 6px'}}>⬇</button>
            {(b.role==='owner'||b.role==='edit')&&(
              <button onClick={e=>{e.stopPropagation();setEditingId(b.id);setEditTitle(b.title);}} title="Umbenennen"
                style={{background:'transparent',border:'none',color:'rgba(255,255,255,0.2)',cursor:'pointer',fontSize:14,padding:'4px 6px'}}>✎</button>
            )}
            {b.role==='owner'&&(
              <button onClick={e=>{e.stopPropagation();if(confirm(`"${b.title}" löschen?`))
                api(`/tools/whiteboard/${b.id}`,{method:'DELETE'}).then(loadBoards).catch(err=>toast(err.message,'error'));
              }} style={{background:'transparent',border:'none',color:'rgba(255,255,255,0.2)',cursor:'pointer',fontSize:16,padding:'4px 6px'}}>✕</button>
            )}
          </div>
        </div>
      ))}
    </div>
  );

  // ── Render: Canvas ────────────────────────────────────────────────────────
  const canEdit = current?.role !== 'view';

  return (
    <div style={{display:'flex',flexDirection:'column',height:'100%',background:BG,position:'relative'}}>

      {shareModal&&current?.role==='owner'&&(
        <ShareModal wbId={current.id} onClose={()=>setShareModal(false)} toast={toast}/>
      )}

      {textInput&&(
        mobile ? (
          <div style={{position:'fixed',bottom:0,left:0,right:0,zIndex:500,
            background:'#1a1a1e',borderTop:'1px solid rgba(255,255,255,0.15)',
            padding:'12px 16px',paddingBottom:'calc(12px + env(safe-area-inset-bottom,0px))'}}>
            <div style={{fontSize:10,fontFamily:'monospace',color:'rgba(255,255,255,0.35)',marginBottom:6}}>
              TEXT EINGEBEN · Enter = bestätigen · Esc = abbrechen
            </div>
            <div style={{display:'flex',gap:8}}>
              <input autoFocus
                onChange={e=>{
                  textPreviewRef.current={text:e.target.value,x:textInput.x,y:textInput.y,fontSize:FONT_SIZES[fontSize],color};
                  render();
                }}
                onKeyDown={e=>{
                  if(e.key==='Enter'){
                    const v=e.target.value; textPreviewRef.current=null; setTextInput(null);
                    if(v?.trim()) pushElement({id:getId(),type:'text',color,fontSize:FONT_SIZES[fontSize],text:v,x:textInput.x,y:textInput.y});
                  }
                  if(e.key==='Escape'){ textPreviewRef.current=null; setTextInput(null); render(); }
                }}
                placeholder="Text eingeben…"
                style={{...S.inp,flex:1,fontSize:14,color}}/>
              <button onMouseDown={e=>{
                e.preventDefault();
                const inp=e.currentTarget.previousSibling;
                const v=inp?.value||''; textPreviewRef.current=null; setTextInput(null);
                if(v?.trim()) pushElement({id:getId(),type:'text',color,fontSize:FONT_SIZES[fontSize],text:v,x:textInput.x,y:textInput.y});
              }} style={S.btn('#4ECDC4',true)}>✓</button>
              <button onMouseDown={e=>{e.preventDefault();textPreviewRef.current=null;setTextInput(null);render();}} style={S.btn('#888888',true)}>✕</button>
            </div>
          </div>
        ) : (
          <div style={{position:'absolute',left:textInput.sx,top:textInput.sy,zIndex:100,pointerEvents:'auto'}}>
            <input autoFocus
              onChange={e=>{
                textPreviewRef.current={text:e.target.value,x:textInput.x,y:textInput.y,fontSize:FONT_SIZES[fontSize],color};
                render();
              }}
              onKeyDown={e=>{
                if(e.key==='Enter'){
                  const v=e.target.value; textPreviewRef.current=null; setTextInput(null);
                  if(v?.trim()) pushElement({id:getId(),type:'text',color,fontSize:FONT_SIZES[fontSize],text:v,x:textInput.x,y:textInput.y});
                }
                if(e.key==='Escape'){ textPreviewRef.current=null; setTextInput(null); render(); }
              }}
              onBlur={e=>{
                const v=e.target.value; textPreviewRef.current=null; setTextInput(null);
                if(v?.trim()) pushElement({id:getId(),type:'text',color,fontSize:FONT_SIZES[fontSize],text:v,x:textInput.x,y:textInput.y});
              }}
              style={{background:'rgba(0,0,0,0.7)',border:'none',
                outline:`1px dashed ${color}`,borderRadius:4,padding:'2px 6px',
                color,fontSize:FONT_SIZES[fontSize],fontFamily:'monospace',minWidth:120}}/>
          </div>
        )
      )}

      {/* ── Topbar ───────────────────────────────────────────────────────── */}
      <div style={{display:'flex',alignItems:'center',gap:8,padding:'8px 12px',
        background:'rgba(0,0,0,0.6)',borderBottom:'1px solid rgba(255,255,255,0.08)',flexShrink:0,flexWrap:'wrap'}}>
        <button onClick={closeBoard} style={{background:'transparent',border:'none',color:'rgba(255,255,255,0.5)',cursor:'pointer',fontSize:18,padding:'2px 6px',lineHeight:1}}>←</button>
        {editingId===current?.id?(
          <input autoFocus value={editTitle} onChange={e=>setEditTitle(e.target.value)}
            onKeyDown={e=>{if(e.key==='Enter') renameBoard(current.id,editTitle); if(e.key==='Escape') setEditingId(null);}}
            onBlur={()=>renameBoard(current.id,editTitle)}
            style={{...S.inp,fontSize:13,fontWeight:700,flex:1,padding:'3px 8px'}}/>
        ):(
          <span onDoubleClick={()=>{if(current?.role!=='view'){setEditingId(current.id);setEditTitle(current.title);}}}
            title={current?.role!=='view'?'Doppelklick zum Umbenennen':''}
            style={{color:'#fff',fontFamily:'monospace',fontSize:13,fontWeight:700,flex:1,minWidth:0,
              overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap',cursor:current?.role!=='view'?'text':'default'}}>
            {current?.title}
            {current?.role==='view'&&<span style={{color:'rgba(255,255,255,0.3)',fontSize:10,marginLeft:8}}>👁 nur lesen</span>}
          </span>
        )}
        {collab.length>0&&(
          <div style={{display:'flex',gap:4,alignItems:'center'}}>
            {collab.map(c=>(
              <span key={c.userId} style={{background:c.color,color:'#000',fontFamily:'monospace',fontSize:9,borderRadius:10,padding:'2px 7px',fontWeight:700}}>
                {c.username[0].toUpperCase()}
              </span>
            ))}
          </div>
        )}
        <button onClick={()=>exportToPng(elementsRef.current,current?.title)}
          style={{...S.btn('#60A5FA',true),fontSize:11}}>⬇ PNG</button>
        {canEdit&&<button onClick={save} disabled={saving} style={{...S.btn('#4ECDC4',true),fontSize:11}}>{saving?'…':'💾 Speichern'}</button>}
        {current?.role==='owner'&&(
          <button onClick={()=>setShareModal(true)} style={{...S.btn('#A78BFA',true),fontSize:11}}>👥 Teilen</button>
        )}
      </div>

      {/* ── Toolbar ──────────────────────────────────────────────────────── */}
      {canEdit&&(
        <div style={{display:'flex',alignItems:'center',gap:5,padding:'5px 10px',
          background:'rgba(0,0,0,0.45)',borderBottom:'1px solid rgba(255,255,255,0.06)',
          flexShrink:0,flexWrap:'wrap'}}>

          {/* Tools */}
          <div style={{display:'flex',gap:3}}>
            {TOOLS.map(t=>(
              <button key={t.id} onClick={()=>{
                setTool(t.id); setSelectedId(null); selectedRef.current=null;
                textPreviewRef.current=null; setTextInput(null); render();
              }} title={t.tip}
                style={{background:tool===t.id?'rgba(78,205,196,0.2)':'rgba(255,255,255,0.05)',
                  border:`1px solid ${tool===t.id?'#4ECDC4':'rgba(255,255,255,0.1)'}`,
                  borderRadius:7,padding:'5px 9px',color:tool===t.id?'#4ECDC4':'rgba(255,255,255,0.7)',
                  cursor:'pointer',fontSize:14,lineHeight:1,fontFamily:'monospace'}}>
                {t.label}
              </button>
            ))}
          </div>

          <div style={{width:1,height:22,background:'rgba(255,255,255,0.1)'}}/>

          {/* Farben */}
          <div style={{display:'flex',gap:3}}>
            {COLORS.map(c=>(
              <button key={c} onClick={()=>setColor(c)}
                style={{width:18,height:18,borderRadius:'50%',background:c,
                  border:color===c?'2px solid #fff':'1px solid rgba(255,255,255,0.2)',cursor:'pointer',padding:0,flexShrink:0}}/>
            ))}
          </div>

          <div style={{width:1,height:22,background:'rgba(255,255,255,0.1)'}}/>

          {/* Strichstärke */}
          <div style={{display:'flex',gap:3,alignItems:'center'}}>
            {SIZES.map((s,i)=>(
              <button key={i} onClick={()=>setSize(i)}
                style={{width:18,height:18,borderRadius:'50%',
                  background:size===i?'rgba(78,205,196,0.2)':'rgba(255,255,255,0.05)',
                  border:`1px solid ${size===i?'#4ECDC4':'rgba(255,255,255,0.15)'}`,
                  cursor:'pointer',padding:0,display:'flex',alignItems:'center',justifyContent:'center'}}>
                <div style={{width:s+2,height:s+2,borderRadius:'50%',background:size===i?'#4ECDC4':'#888'}}/>
              </button>
            ))}
          </div>

          {/* Textgröße — nur bei Text-Tool */}
          {tool==='text'&&<>
            <div style={{width:1,height:22,background:'rgba(255,255,255,0.1)'}}/>
            <span style={{color:'rgba(255,255,255,0.35)',fontFamily:'monospace',fontSize:9}}>TEXT</span>
            <div style={{display:'flex',gap:3,alignItems:'center'}}>
              {FONT_SIZES.map((fs,i)=>(
                <button key={i} onClick={()=>setFontSize(i)}
                  style={{padding:'2px 6px',borderRadius:6,fontFamily:'monospace',fontSize:Math.min(14,fs/2)+1,
                    background:fontSize===i?'rgba(78,205,196,0.2)':'rgba(255,255,255,0.05)',
                    border:`1px solid ${fontSize===i?'#4ECDC4':'rgba(255,255,255,0.15)'}`,
                    color:fontSize===i?'#4ECDC4':'rgba(255,255,255,0.6)',cursor:'pointer'}}>
                  {fs}
                </button>
              ))}
            </div>
          </>}

          <div style={{width:1,height:22,background:'rgba(255,255,255,0.1)'}}/>

          {/* Undo + Löschen */}
          <button onClick={undo} disabled={!undoStack.length} title="Rückgängig (Ctrl+Z)"
            style={{background:'rgba(255,255,255,0.05)',border:'1px solid rgba(255,255,255,0.1)',
              borderRadius:7,padding:'5px 9px',color:undoStack.length?'rgba(255,255,255,0.7)':'rgba(255,255,255,0.2)',
              cursor:undoStack.length?'pointer':'default',fontSize:12,fontFamily:'monospace'}}>↩</button>

          {selectedId&&(
            <button onClick={()=>{
              const next=elementsRef.current.filter(el=>el.id!==selectedId);
              setUndoStack(prev=>[...prev,elementsRef.current]);
              elementsRef.current=next; setElements([...next]); broadcast(next);
              setSelectedId(null); selectedRef.current=null;
            }} style={{background:'rgba(255,107,157,0.08)',border:'1px solid rgba(255,107,157,0.25)',
              borderRadius:7,padding:'5px 9px',color:'#ff6b9d',cursor:'pointer',fontSize:12,fontFamily:'monospace'}}>
              ✕ Löschen
            </button>
          )}

          <button onClick={()=>{if(confirm('Alles löschen?')){
            elementsRef.current=[];setElements([]);setUndoStack([]);broadcast([]);
            setSelectedId(null);selectedRef.current=null;
          }}} style={{background:'rgba(255,107,157,0.05)',border:'1px solid rgba(255,107,157,0.15)',
            borderRadius:7,padding:'5px 9px',color:'rgba(255,107,157,0.5)',cursor:'pointer',fontSize:12,fontFamily:'monospace'}}>
            🗑
          </button>

          <div style={{width:1,height:22,background:'rgba(255,255,255,0.1)'}}/>

          {/* Zoom */}
          <button onClick={()=>zoom(1.2)} style={{background:'rgba(255,255,255,0.05)',border:'1px solid rgba(255,255,255,0.1)',borderRadius:7,padding:'5px 9px',color:'rgba(255,255,255,0.7)',cursor:'pointer',fontSize:14,fontFamily:'monospace'}}>＋</button>
          <span style={{color:'rgba(255,255,255,0.35)',fontFamily:'monospace',fontSize:10,minWidth:32,textAlign:'center'}}>
            {Math.round(viewport.zoom*100)}%
          </span>
          <button onClick={()=>zoom(0.8)} style={{background:'rgba(255,255,255,0.05)',border:'1px solid rgba(255,255,255,0.1)',borderRadius:7,padding:'5px 9px',color:'rgba(255,255,255,0.7)',cursor:'pointer',fontSize:14,fontFamily:'monospace'}}>－</button>
          <button onClick={()=>syncVp({x:0,y:0,zoom:1})}
            style={{background:'transparent',border:'none',color:'rgba(255,255,255,0.25)',cursor:'pointer',fontFamily:'monospace',fontSize:9}}>
            Reset
          </button>
        </div>
      )}

      {/* ── Canvas ───────────────────────────────────────────────────────── */}
      <canvas ref={canvasRef}
        onPointerDown={onPointerDown} onPointerMove={onPointerMove}
        onPointerUp={onPointerUp} onPointerLeave={onPointerUp} onWheel={onWheel}
        style={{flex:1,width:'100%',touchAction:'none',cursor:getCursor()}}
      />

      {/* Hinweis: Select-Tool Aktionen */}
      {tool==='select'&&selectedId&&!mobile&&(
        <div style={{position:'absolute',bottom:12,left:'50%',transform:'translateX(-50%)',
          background:'rgba(0,0,0,0.7)',border:'1px solid rgba(255,255,255,0.1)',
          borderRadius:8,padding:'5px 12px',fontFamily:'monospace',fontSize:10,color:'rgba(255,255,255,0.4)',
          pointerEvents:'none',whiteSpace:'nowrap'}}>
          Ziehen: verschieben · Ecke (◉) ziehen: skalieren · Entf: löschen
        </div>
      )}
    </div>
  );
}
