
    /* ── dragged into place ───────────────────────────────────────────
       Press and hold a row and it lifts, the rows below make room, and
       letting go lands it (../reorder.js). A press released without moving
       still opens the row's sheet, so nothing that worked yesterday stops
       working. The sheet's Move rows stay too: they are the accessible
       path, and the one that is still honest under a filter — a filtered
       list's visible order is NOT the stored order, so the drag itself is
       refused while the search has anything in it, the way the desktop
       refuses it ("reordering a partial view would be a lie about the
       saved sequence"), and the note under the field says so. */
    const canDrag = $derived(!q.trim() && items.length > 1);
    let dragFrom = null;
    function dragLift() { pls.dragging = true; dragFrom = items; }
    function dragHold(i) { const it = visible[i]; if (it) sheet(it); }
    /* a drop clears the flag when the write settles, not when the finger
       lifts: releasing it earlier would let the deferred re-read land the
       OLD order between the two */
    function dragEnd(dropped) { if (!dropped) pls.dragging = false; }
    function dragDrop(from, to) {
        /* a refetch that landed mid-drag replaced the rows under us: the
           indices the gesture collected address a list that is gone */
        if (items !== dragFrom || q.trim()) { pls.dragging = false; return; }
        const snap = items.slice();
        const next = snap.slice();
        const [row] = next.splice(from, 1);
        if (!row) { pls.dragging = false; return; }
        next.splice(to, 0, row);
        /* optimistic: the row is where you put it before the backend has
           been asked, and comes back if the answer is no */
        items = next;
        moveItem(pl, snap, row, to)
            .catch((e) => { items = snap; player.error = String(e); })
            .finally(() => { pls.dragging = false; });
    }
    /* `enabled` is a function, not a value: an action's `update` is
       deprecated in Svelte 5, so an options object that has to change
       identity to be heard is not something to lean on. This one is asked
       at the press, every press. */
    const rowDrag = { enabled: () => canDrag, rowSel: ".trk", skipSel: ".r", scrollSel: ".screen", onLift: dragLift, onDrop: dragDrop, onHold: dragHold, onEnd: dragEnd };
