diff --git a/pygitweb/static/board.js b/pygitweb/static/board.js
index 914cf0e..bd61fee 100644
--- a/pygitweb/static/board.js
+++ b/pygitweb/static/board.js
@@ -93,6 +93,17 @@
     return clone;
   }
 
+  function groupTasksByStatus(tasks) {
+    var byStatus = {};
+    if (!Array.isArray(tasks)) return byStatus;
+    tasks.forEach(function(task) {
+      var s = task.status || 'TODO';
+      if (!byStatus[s]) byStatus[s] = [];
+      byStatus[s].push(task);
+    });
+    return byStatus;
+  }
+
   function loadTasksForBoard() {
     var ctx = getBoardContext();
     if (!ctx.project) return;
@@ -102,12 +113,7 @@
       return res.json();
     }).then(function(tasks) {
       if (!Array.isArray(tasks)) return;
-      var byStatus = {};
-      tasks.forEach(function(task) {
-        var s = task.status || 'TODO';
-        if (!byStatus[s]) byStatus[s] = [];
-        byStatus[s].push(task);
-      });
+      var byStatus = groupTasksByStatus(tasks);
       var columns = document.querySelectorAll('.board-column.dropzone');
       columns.forEach(function(column) {
         var status = column.dataset.status || 'TODO';
@@ -123,6 +129,32 @@
     }).catch(function() {});
   }
 
+  function fetchBoardTasksJson() {
+    var ctx = getBoardContext();
+    if (!ctx.project) return Promise.resolve(null);
+    var listUrl = '/tasks/list?' + new URLSearchParams({ project: ctx.project, board: ctx.board }).toString();
+    return fetch(listUrl).then(function(res) {
+      return res.ok ? res.json() : null;
+    });
+  }
+
+  function reorderBoardDomToTaskList(tasks) {
+    if (!Array.isArray(tasks)) return;
+    var byStatus = groupTasksByStatus(tasks);
+    document.querySelectorAll('.board-column.dropzone').forEach(function(column) {
+      var status = column.dataset.status || 'TODO';
+      var cardsContainer = column.querySelector('.board-column-cards');
+      if (!cardsContainer) return;
+      var ordered = byStatus[status] || [];
+      ordered.forEach(function(task) {
+        if (!task.ref) return;
+        var card = findBoardCardByTaskRef(task.ref);
+        if (card) cardsContainer.appendChild(card);
+      });
+      updateColumnBadge(column);
+    });
+  }
+
   function addDefaultCard(ev) {
     if (!template || !template.content) return;
     var column = ev.currentTarget.closest('.board-column');
@@ -176,10 +208,49 @@
   });
 
   var boardColumns = document.querySelector('.board-columns');
-  var dropzones = document.querySelectorAll('.board-column.dropzone');
   var draggedCard = null;
   var didDrag = false;
 
+  function findBoardCardByTaskRef(taskRef) {
+    if (!taskRef) return null;
+    var cards = document.querySelectorAll('.board-card');
+    for (var i = 0; i < cards.length; i++) {
+      if (cards[i].dataset.taskRef === taskRef) return cards[i];
+    }
+    return null;
+  }
+
+  function clearColumnDropHighlight() {
+    document.querySelectorAll('.board-column.dropzone.border-primary').forEach(function(el) {
+      el.classList.remove('border-primary');
+    });
+  }
+
+  function dragTransferLooksLikeTask(e) {
+    if (draggedCard) return true;
+    var types = e.dataTransfer && e.dataTransfer.types;
+    if (!types || !types.length) return false;
+    for (var i = 0; i < types.length; i++) {
+      if (types[i] === 'text/plain' || types[i] === 'Text') return true;
+    }
+    return false;
+  }
+
+  function showBoardError(message) {
+    var anchor = boardColumns && boardColumns.parentNode;
+    if (!anchor) return;
+    var existing = document.querySelector('.board-task-error-hint');
+    if (existing) existing.remove();
+    var el = document.createElement('div');
+    el.className = 'alert board-task-error-hint py-2 px-3 mb-2';
+    el.setAttribute('role', 'alert');
+    el.textContent = message;
+    anchor.insertBefore(el, boardColumns);
+    window.setTimeout(function() {
+      if (el.parentNode) el.remove();
+    }, 8000);
+  }
+
   function updateColumnBadge(column) {
     var cardsContainer = column && column.querySelector('.board-column-cards');
     var badge = column && column.querySelector('.board-column-add-card');
@@ -194,14 +265,22 @@
     didDrag = true;
     draggedCard = card;
     e.dataTransfer.effectAllowed = 'move';
-    e.dataTransfer.setData('text/plain', card.dataset.taskRef || '');
+    try {
+      e.dataTransfer.setData('text/plain', card.dataset.taskRef || '');
+    } catch (err) {
+      /* ignore setData failures in restricted environments */
+    }
     card.classList.add('opacity-50');
   }
   function handleDragEnd(e) {
-    var card = e.target.closest && e.target.closest('.board-card');
+    var card = (draggedCard && draggedCard.classList.contains('opacity-50') ? draggedCard : null) ||
+      (e.target.closest && e.target.closest('.board-card'));
     if (card) card.classList.remove('opacity-50');
-    draggedCard = null;
-    setTimeout(function() { didDrag = false; }, 0);
+    clearColumnDropHighlight();
+    window.setTimeout(function() {
+      draggedCard = null;
+      didDrag = false;
+    }, 0);
   }
 
   /** Parse display date text to YYYY-MM-DD for input[type=date]. Returns '' if none. */
@@ -425,51 +504,89 @@
     showCardPopout(card);
   }
 
-  function handleDragOver(e) {
+  function handleBoardDragOver(e) {
+    if (!boardColumns || !boardColumns.contains(e.target)) return;
+    var col = e.target.closest && e.target.closest('.board-column.dropzone');
+    if (!col || !dragTransferLooksLikeTask(e)) return;
     e.preventDefault();
     e.dataTransfer.dropEffect = 'move';
-    e.currentTarget.classList.add('border-primary');
-  }
-  function handleDragLeave(e) {
-    e.currentTarget.classList.remove('border-primary');
+    clearColumnDropHighlight();
+    col.classList.add('border-primary');
   }
+
   function pushTaskStatusUpdate(card, newStatus) {
     var taskRef = card && card.dataset.taskRef;
-    if (!taskRef) return;
+    if (!taskRef) return Promise.resolve({ ok: false, status: 0 });
     var ctx = getBoardContext();
-    if (!ctx.project) return;
-    fetch('/tasks/update?' + new URLSearchParams({ project: ctx.project, board: ctx.board, task: taskRef }).toString(), {
+    if (!ctx.project) return Promise.resolve({ ok: false, status: 0 });
+    return fetch('/tasks/update?' + new URLSearchParams({ project: ctx.project, board: ctx.board, task: taskRef }).toString(), {
       method: 'POST',
       headers: { 'Content-Type': 'application/json' },
       body: JSON.stringify({ status: newStatus || 'TODO' })
-    }).catch(function() {});
+    }).then(function(res) {
+      return { ok: res.ok, status: res.status };
+    }).catch(function() {
+      return { ok: false, status: 0 };
+    });
   }
 
-  function handleDrop(e) {
+  function handleBoardDrop(e) {
+    var col = e.target.closest && e.target.closest('.board-column.dropzone');
+    if (!col || !boardColumns || !boardColumns.contains(e.target)) return;
     e.preventDefault();
-    e.currentTarget.classList.remove('border-primary');
-    if (!draggedCard) return;
-    var targetColumn = e.currentTarget.closest && e.currentTarget.closest('.board-column');
-    var targetCards = targetColumn && targetColumn.querySelector('.board-column-cards');
+    e.stopPropagation();
+    clearColumnDropHighlight();
+    var taskRef = '';
+    try {
+      taskRef = e.dataTransfer.getData('text/plain') || '';
+    } catch (err2) {
+      taskRef = '';
+    }
+    var card = draggedCard || findBoardCardByTaskRef(taskRef);
+    if (!card) return;
+    var targetColumn = col;
+    var targetCards = targetColumn.querySelector('.board-column-cards');
     if (!targetCards) return;
-    var sourceColumn = draggedCard.closest && draggedCard.closest('.board-column');
+    var sourceColumn = card.closest && card.closest('.board-column');
     if (sourceColumn === targetColumn) return;
     var newStatus = targetColumn.dataset.status || 'TODO';
-    draggedCard.dataset.taskStatus = newStatus;
-    targetCards.appendChild(draggedCard);
+    var prevStatus = card.dataset.taskStatus || 'TODO';
+    var sourceCards = sourceColumn && sourceColumn.querySelector('.board-column-cards');
+    var nextSibling = card.nextSibling;
+    card.dataset.taskStatus = newStatus;
+    targetCards.appendChild(card);
     updateColumnBadge(sourceColumn);
     updateColumnBadge(targetColumn);
-    pushTaskStatusUpdate(draggedCard, newStatus);
+    pushTaskStatusUpdate(card, newStatus).then(function(result) {
+      if (result.ok) {
+        fetchBoardTasksJson().then(function(tasks) {
+          if (tasks) reorderBoardDomToTaskList(tasks);
+        });
+        return;
+      }
+      card.dataset.taskStatus = prevStatus;
+      if (sourceCards) {
+        if (nextSibling && nextSibling.parentNode === sourceCards) {
+          sourceCards.insertBefore(card, nextSibling);
+        } else {
+          sourceCards.appendChild(card);
+        }
+      }
+      updateColumnBadge(sourceColumn);
+      updateColumnBadge(targetColumn);
+      var st = result.status;
+      var msg = (st === 401 || st === 403)
+        ? 'Not authorized to update tasks (sign in again if needed). The card was moved back.'
+        : 'Could not save the task move. The card was moved back.';
+      showBoardError(msg);
+    });
   }
 
   if (boardColumns) {
     boardColumns.addEventListener('dragstart', handleDragStart);
     boardColumns.addEventListener('dragend', handleDragEnd);
     boardColumns.addEventListener('click', handleCardClick);
+    boardColumns.addEventListener('dragover', handleBoardDragOver, true);
+    boardColumns.addEventListener('drop', handleBoardDrop, true);
   }
-  dropzones.forEach(function(el) {
-    el.addEventListener('dragover', handleDragOver);
-    el.addEventListener('dragleave', handleDragLeave);
-    el.addEventListener('drop', handleDrop);
-  });
 })();
diff --git a/pygitweb/static/main.css b/pygitweb/static/main.css
index 9b2df11..715269e 100644
--- a/pygitweb/static/main.css
+++ b/pygitweb/static/main.css
@@ -38,6 +38,11 @@
 	--pgw-rem-bg: #fee2e2;
 	--pgw-info-fg: #52525b;
 
+	--pgw-warn-fg: #92400e;
+	--pgw-warn-bg: #fffbeb;
+	--pgw-warn-border: #fcd34d;
+	--pgw-warn-accent: #d97706;
+
 	--pgw-blob-selection-bg: rgba(193, 173, 0, 0.35);
 
 	--pgw-navbar-bg: #e2e8f0;
@@ -94,6 +99,11 @@
 		--pgw-rem-bg: #7f1d1d;
 		--pgw-info-fg: #a1a1aa;
 
+		--pgw-warn-fg: #fde68a;
+		--pgw-warn-bg: #422006;
+		--pgw-warn-border: #92400e;
+		--pgw-warn-accent: #f59e0b;
+
 		--pgw-blob-selection-bg: rgba(193, 173, 0, 0.4);
 
 		--pgw-navbar-bg: #2d2d4a;
@@ -754,6 +764,25 @@ tbody tr {
 	}
 }
 
+/* Empty columns still accept drags over the card stack area */
+.board-column-cards {
+	min-height: 3rem;
+}
+
+/*
+ * Board task error banner: Tabler .alert layout + PGW tokens so it tracks
+ * prefers-color-scheme (and matches .card / .table overrides that use --pgw-*).
+ */
+.board-task-error-hint.alert {
+	--bs-alert-color: var(--pgw-warn-fg);
+	--bs-alert-bg: var(--pgw-warn-bg);
+	--bs-alert-border-color: var(--pgw-warn-border);
+	background-color: var(--pgw-warn-bg);
+	color: var(--pgw-warn-fg);
+	border: 1px solid var(--pgw-warn-border);
+	border-left: 0.25rem solid var(--pgw-warn-accent);
+}
+
 /* Board: Sync status icons (mask so currentColor is respected when loaded) */
 .board-card-icon-wrap {
 	opacity: 0.7;
diff --git a/pygitweb/tasks.py b/pygitweb/tasks.py
index 0b9dd0e..cd1e61c 100644
--- a/pygitweb/tasks.py
+++ b/pygitweb/tasks.py
@@ -311,11 +311,12 @@ async def task_update(
 	t = get_task(repo, ref)
 	if not t:
 		raise HTTPException(status_code=404, detail="Task not found")
+	status_changed = bool("status" in body and hasattr(Task.Status, body["status"]))
 	if "title" in body:
 		t.title = body["title"]
 	if "description" in body:
 		t.description = body.get("description", "")
-	if "status" in body and hasattr(Task.Status, body["status"]):
+	if status_changed:
 		t.status = Task.Status(body["status"])
 	if "priority" in body and hasattr(Task.Priority, body["priority"]):
 		t.priority = Task.Priority(body["priority"])
@@ -336,7 +337,11 @@ async def task_update(
 	board_ref = _board_ref(board)
 	b = get_board(repo, board_ref)
 	if b and getattr(b, "tasks", None):
-		b.tasks = [str(new_oid) if o == old_oid else o for o in b.tasks]
+		new_oid_str = str(new_oid)
+		tasks_list = [new_oid_str if o == old_oid else o for o in b.tasks]
+		if status_changed and new_oid_str in tasks_list:
+			tasks_list = [o for o in tasks_list if o != new_oid_str] + [new_oid_str]
+		b.tasks = tasks_list
 		b.update_message()
 		b.write(repo)
 	return JSONResponse(content=_task_to_json(t))
diff --git a/pygitweb/tasks_routes_test.py b/pygitweb/tasks_routes_test.py
index 10d1e5f..a599045 100644
--- a/pygitweb/tasks_routes_test.py
+++ b/pygitweb/tasks_routes_test.py
@@ -10,7 +10,7 @@ from fastapi import FastAPI
 from fastapi.testclient import TestClient
 from pygit2 import Oid
 
-from pygittools.tasks import BOARD_REF_PREFIX, TASK_REF_PREFIX, Board, Comment, Task
+from pygittools.tasks import BOARD_REF_PREFIX, TASK_REF_PREFIX, Board, Comment, Task, get_board
 from pygitweb.config import settings
 from pygitweb.tasks import EMPTY_TREE_OID, board_router, comment_router, task_router
 
@@ -242,6 +242,51 @@ class TestTaskRoutes:
 			assert updated["assignee"] == "bob"
 			assert updated["due_date"] == "2025-02-03T04:05:06+00:00"
 
+		def test_task_status_update_moves_oid_to_end_of_board_list(
+			self, mutable_env: dict[str, str | TestClient]
+		) -> None:
+			client = mutable_env["client"]
+			assert isinstance(client, TestClient)
+			a_ref = client.post(
+				"/tasks/create",
+				params={
+					"project": mutable_env["project"],
+					"board": mutable_env["board"],
+					"title": "First",
+					"status": "TODO",
+				},
+			).json()["ref"]
+			b_ref = client.post(
+				"/tasks/create",
+				params={
+					"project": mutable_env["project"],
+					"board": mutable_env["board"],
+					"title": "Second",
+					"status": "TODO",
+				},
+			).json()["ref"]
+			list_before = client.get(
+				"/tasks/list",
+				params={"project": mutable_env["project"], "board": mutable_env["board"]},
+			).json()
+			refs_before = [t["ref"] for t in list_before if t["title"] in ("First", "Second")]
+			assert refs_before.index(a_ref) < refs_before.index(b_ref)
+			client.post(
+				"/tasks/update",
+				params={
+					"project": mutable_env["project"],
+					"board": mutable_env["board"],
+					"task": a_ref,
+				},
+				json={"status": "IN_PROGRESS"},
+			)
+			repo = pygit2.Repository(str(Path(settings.PROJECTROOT) / str(mutable_env["project"])))
+			loaded = get_board(repo, f"{BOARD_REF_PREFIX}{mutable_env['board']}")
+			assert loaded is not None
+			a_oid = str(repo.references[a_ref].resolve().target)
+			b_oid = str(repo.references[b_ref].resolve().target)
+			assert loaded.tasks.index(b_oid) < loaded.tasks.index(a_oid)
+
 		def test_comment_delete_response_format(self, mutable_env: dict[str, str | TestClient]) -> None:
 			client = mutable_env["client"]
 			assert isinstance(client, TestClient)
