Drag-and-drop UX rewrite:
- The old "highlight target row + margin-grow animation" approach was
driven by per-row dragenter/dragleave events. Those fire in a noisy
enter/leave/enter cascade as the pointer crosses sub-elements and as
the row itself grows under the pointer, which is why the gap was
pulsing open/closed.
- New approach: a single container-level dragover handler. On dragstart
the source row is briefly cloned into a translucent "placeholder"
element (dashed outline, 45% opacity, pointer-events:none) inserted in
the source's slot; the original is then hidden (display:none) right
after dragstart so the browser can still capture it as the drag image.
As the cursor moves over the container we compute which sibling's
midpoint the pointer just crossed and insertBefore the placeholder
accordingly. The list length stays constant the whole time, so there
is no growing/shrinking gap to fight with — what the user sees is the
dragged item itself shown semi-transparently at the exact drop slot.
On drop, splice the array using the placeholder's index among the
non-source children, then re-render.
- The bindContainerDnd helper handles both lists; image grid uses
vertical Y math (same midpoint rule as the track list since cards
flow row-by-row in the auto-fill grid). attachDraggable now only sets
up dragstart/dragend/contextmenu per row; no more dragenter/dragleave.
Image grid:
- Image cards now have a caption below the thumbnail. When the same
URL appears in the music list, the music entry's title/artist are
borrowed via captionForImage(url); otherwise "(제목 없음)" muted text.
Layout changed from a square aspect-ratio card to a flex column:
.imgWrap holds the square thumbnail, .cardCaption sits underneath
with single-line title + smaller muted artist line.
CSS cleanup:
- Drop the old .dropAbove margin-grow rules and .dragOver border rule
on .trackRow/.imageCard. Replaced with .dragPlaceholder +
.hiddenWhileDragging.
- .imageCard no longer uses aspect-ratio on itself; aspect lives on
.imgWrap so caption can extend the card vertically.