<!-- source-hash: 266e1c3c423b749f9d7def8ff717188e -->
A drag-and-drop Kanban board component that manages ticket movement across columns using `@dnd-kit/core`, with support for column collapsing, horizontal scrolling, and constrained drop rules.

## Key Components

| Export / Symbol | Description |
|---|---|
| `Board` | Main board component rendering all columns with full DnD orchestration |
| `BoardProps` | Props interface defining columns, event handlers, and customization options |
| `collisionDetection` | Custom collision strategy: prefers ticket hits, falls back to column, then closest corners |
| `handleDragStart` | Records drag origin (ticket + source column) and sets overlay state |
| `handleDragOver` | Optimistically moves tickets across columns respecting `dropDisabled` and `allowedFromColumns` |
| `handleDragEnd` | Commits the move by calling `onChange` with positional context (`afterTicketId`, `beforeTicketId`), or rolls back |
| `handleDragCancel` | Reverts local state to the last server-provided `columns` prop |
| `locate` | Helper to find a ticket's column, index, and reference by ID |
| `findIndexInColumn` | Helper returning a ticket's index within a specific column |

## Usage Example

```typescript
<Board
  columns={[
    {
      id: 'open',
      label: 'Open',
      tickets: [{ id: 'T-1', title: 'Fix login bug' }],
    },
    {
      id: 'in-progress',
      label: 'In Progress',
      tickets: [],
      allowedFromColumns: ['open'],
    },
    {
      id: 'done',
      label: 'Done',
      tickets: [],
      dropDisabled: true,
    },
  ]}
  onChange={({ ticketId, fromColumnId, toColumnId, afterTicketId }) => {
    api.moveTicket({ ticketId, toColumnId, afterTicketId })
  }}
  onLoadMore={(columnId) => fetchMore(columnId)}
  getTicketHref={(ticketId) => `/tickets/${ticketId}`}
  renderAssignSlot={(ticket) => <AssigneeAvatar ticket={ticket} />}
  collapseStorageKey="board-collapse-state"
/>
```

> **Drop rules:** Set `dropDisabled: true` on a column to block all inbound drops. Use `allowedFromColumns: ['open']` to restrict which source columns can feed into a target — dragging back to the origin column is always permitted regardless of these rules.