Decision (updated)

✅ Keep per-element drag (brand-name, tagline, est-year, each shape).

✅ Use one drag system only: the overlay/pointer system.

❌ Remove the legacy DOM mousedown/mousemove listeners on <text> (that’s the conflict).

Micro-diff plan (no UI changes)

A) Delete the conflicting effect
Remove the entire useEffect that adds:

el.addEventListener('mousedown', …) on <text>/<textPath>

document mousemove / mouseup / mouseleave
(That’s the block you pasted lines ~644–729.)

B) Keep your overlay drag — per element
For each draggable overlay box (the ones you already render above the SVG):

Give it a stable id key (use what you already have):

Text: brand-name, tagline, est-year

Shapes: use the existing data-shape-id UUID you already render

If the whole logo group is also draggable, it can keep logo

Attach the pointer handlers on that overlay div, not on the container:

onPointerDown={(e) => handleDragStart(e, dragId)}

onPointerMove={(e) => handleDragMove(e)}

onPointerUp={(e) => handleDragEnd(e)}

Where dragId is the overlay’s unique id (text id or shape uuid)

In your drag logic (no UI changes):

On pointerdown:

Find the preview wrapper via [data-logo-preview] and cache rect.

Capture:

dragRef.current.id = dragId

dragRef.current.startXPct = positions[dragId]?.xPct ?? 0

dragRef.current.startYPct = positions[dragId]?.yPct ?? 0

dragRef.current.originX = e.clientX

dragRef.current.originY = e.clientY

e.currentTarget.setPointerCapture?.(e.pointerId); e.preventDefault();

On pointermove:

dxPct = ((e.clientX - originX) / rect.width) * 100

dyPct = ((e.clientY - originY) / rect.height) * 100

next = { xPct: clamp(startXPct + dxPct), yPct: clamp(startYPct + dyPct) }

setPositions(prev => ({ ...prev, [id]: next })) inside requestAnimationFrame

On pointerup:

Clear dragRef.current, release capture, e.preventDefault()

Ensure the overlay box styles on [data-logo-preview]:

Wrapper: touch-action: none; user-select: none;

Draggable overlays: pointer-events: auto; cursor: grab;

Non-interactive layers: pointer-events: none;

C) Persist what you already have

Keep your existing positions state shape (map by id → {xPct,yPct}).

Keep rendering overlays with style={{ left: xPct+'%', top: yPct+'%' }} (or whatever you already use).

Do not change any formatter/text/rotation/export code in this step.