diff --git a/pygittools/tasks.py b/pygittools/tasks.py
index dc39a83..330a596 100644
--- a/pygittools/tasks.py
+++ b/pygittools/tasks.py
@@ -19,6 +19,7 @@ EMPTY_TREE_OID_HEX = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
 BOARD_REF_PREFIX: str = "refs/tags/boards/"
 TASK_REF_PREFIX: str = "refs/tags/tasks/"
 COMMENT_REF_PREFIX: str = "refs/tags/comments/"
+ARCHIVE_BOARD_NAME: str = "Archive"
 
 
 def _ensure_empty_tree(repo: Repository) -> None:
@@ -385,3 +386,77 @@ def delete_task_local(repo: Repository, task_ref: str) -> None:
 
 	if task_ref in repo.references:
 		repo.references.delete(task_ref)
+
+
+def _remove_task_from_board(repo: Repository, board_ref: str, task_name: str) -> bool:
+	board = get_board(repo, board_ref)
+	if board is None:
+		return False
+	original = list(getattr(board, "tasks", []) or [])
+	filtered: list[Oid | str] = []
+	removed = False
+	for entry in original:
+		try:
+			oid = Oid(hex=entry) if isinstance(entry, str) else entry
+			board_task = get_task_by_oid(repo, oid)
+		except (ValueError, KeyError, TypeError):
+			filtered.append(entry)
+			continue
+		if board_task is None or board_task.name != task_name:
+			filtered.append(entry)
+		else:
+			removed = True
+	if not removed:
+		return False
+	board.tasks = filtered
+	board.update_message()
+	board.write(repo)
+	return True
+
+
+def archive_task_local(
+	repo: Repository,
+	task_ref: str,
+	source_board_name: str,
+	*,
+	archive_board_name: str = ARCHIVE_BOARD_NAME,
+) -> str:
+	"""Move a task from a board to the archive board, creating the archive board if needed."""
+	if task_ref not in repo.references:
+		raise KeyError(f"Task not found: {task_ref}")
+	task = get_task(repo, task_ref)
+	if task is None:
+		raise KeyError(f"Task not found: {task_ref}")
+	tag_obj = repo.revparse_single(task_ref)
+	if not isinstance(tag_obj, Tag):
+		raise KeyError(f"Task not found: {task_ref}")
+
+	source_ref = f"{BOARD_REF_PREFIX}{source_board_name}"
+	if source_ref not in repo.references:
+		raise KeyError(f"Board not found: {source_board_name}")
+
+	_remove_task_from_board(repo, source_ref, tag_obj.name)
+
+	oid_hex = str(repo.references[task_ref].resolve().target)
+	archive_ref = f"{BOARD_REF_PREFIX}{archive_board_name}"
+	if archive_ref in repo.references:
+		archive_board = get_board(repo, archive_ref)
+		assert archive_board is not None
+		tasks = list(getattr(archive_board, "tasks", []) or [])
+		if oid_hex not in tasks:
+			tasks.append(oid_hex)
+		archive_board.tasks = tasks
+		archive_board.update_message()
+		archive_board.write(repo)
+		return archive_ref
+
+	archive_board = Board(
+		Oid(hex=EMPTY_TREE_OID_HEX),
+		archive_ref,
+		tagger="",
+		description="Archived tasks",
+	)
+	archive_board.tasks = [oid_hex]
+	archive_board.update_message()
+	archive_board.write(repo)
+	return archive_ref
diff --git a/pygittools/tasks_test.py b/pygittools/tasks_test.py
index 569c876..eb502b0 100644
--- a/pygittools/tasks_test.py
+++ b/pygittools/tasks_test.py
@@ -5,12 +5,14 @@ import pytest
 from pygit2 import Oid, Repository, init_repository
 
 from pygittools.tasks import (
+	ARCHIVE_BOARD_NAME,
 	BOARD_REF_PREFIX,
 	EMPTY_TREE_OID_HEX,
 	TASK_REF_PREFIX,
 	Board,
 	Comment,
 	Task,
+	archive_task_local,
 	delete_task_local,
 	get_board,
 	get_comment,
@@ -238,3 +240,53 @@ def test_delete_task_local_removes_board_and_comment_refs(repo: Repository) -> N
 	loaded_board = get_board(repo, BOARD_MAIN)
 	assert loaded_board is not None
 	assert loaded_board.tasks == []
+
+
+def test_archive_task_local_moves_to_archive_board(repo: Repository) -> None:
+	board = Board(_empty_tree(), BOARD_MAIN, TAGGER, description="Main")
+	task = Task(
+		_empty_tree(),
+		_task_ref("to-archive"),
+		TAGGER,
+		title="To Archive",
+		description="move me",
+		status=Task.Status.DONE,
+	)
+	task_oid = task.write(repo)
+	board.tasks = [str(task_oid)]
+	board.update_message()
+	board.write(repo)
+
+	archive_ref = archive_task_local(repo, _task_ref("to-archive"), "main")
+
+	assert _task_ref("to-archive") in repo.references
+	main_board = get_board(repo, BOARD_MAIN)
+	assert main_board is not None
+	assert main_board.tasks == []
+	archive_board = get_board(repo, archive_ref)
+	assert archive_board is not None
+	assert archive_board.tasks == [str(task_oid)]
+	assert archive_ref == f"{BOARD_REF_PREFIX}{ARCHIVE_BOARD_NAME}"
+
+
+def test_archive_task_local_creates_archive_board_when_missing(repo: Repository) -> None:
+	board = Board(_empty_tree(), BOARD_MAIN, TAGGER, description="Main")
+	task = Task(
+		_empty_tree(),
+		_task_ref("solo"),
+		TAGGER,
+		title="Solo",
+		status=Task.Status.TODO,
+	)
+	task_oid = task.write(repo)
+	board.tasks = [str(task_oid)]
+	board.update_message()
+	board.write(repo)
+
+	archive_ref = archive_task_local(repo, _task_ref("solo"), "main")
+
+	assert archive_ref in repo.references
+	archive_board = get_board(repo, archive_ref)
+	assert archive_board is not None
+	assert getattr(archive_board, "description", "") == "Archived tasks"
+	assert archive_board.tasks == [str(task_oid)]
diff --git a/pygitweb/static/board.js b/pygitweb/static/board.js
index 42db49e..41a5e12 100644
--- a/pygitweb/static/board.js
+++ b/pygitweb/static/board.js
@@ -136,6 +136,7 @@
 
   preloadIcons();
   function initBoard() {
+    initBoardSelector();
     assignCloudIconToAllCards();
     refreshBoardRefManifest().then(function () {
       loadTasksForBoard();
@@ -190,6 +191,52 @@
     return { project: project || '', board: board || 'Tasks' };
   }
 
+  function boardViewUrl(project, boardName) {
+    var path = '/project/' + project + '/board/';
+    var params = new URLSearchParams();
+    if (boardName && boardName !== 'Tasks') params.set('b', boardName);
+    var qs = params.toString();
+    return qs ? path + '?' + qs : path;
+  }
+
+  function initBoardSelector() {
+    var select = document.getElementById('board-board-select');
+    if (!select) return;
+    var ctx = getBoardContext();
+    if (!ctx.project) return;
+
+    fetch('/board/list?' + new URLSearchParams({ project: ctx.project }).toString())
+      .then(function (res) { return res.ok ? res.json() : null; })
+      .then(function (boards) {
+        if (!Array.isArray(boards) || !boards.length) return;
+        var names = boards.map(function (b) { return b.name; });
+        select.innerHTML = '';
+        boards.forEach(function (b) {
+          var opt = document.createElement('option');
+          opt.value = b.name;
+          var label = b.name;
+          if (typeof b.task_count === 'number') label += ' (' + b.task_count + ')';
+          opt.textContent = label;
+          if (b.name === ctx.board) opt.selected = true;
+          select.appendChild(opt);
+        });
+        if (names.indexOf(ctx.board) < 0) {
+          var current = document.createElement('option');
+          current.value = ctx.board;
+          current.textContent = ctx.board;
+          current.selected = true;
+          select.insertBefore(current, select.firstChild);
+        }
+      })
+      .catch(function () {});
+
+    select.addEventListener('change', function () {
+      var nextBoard = select.value;
+      if (!nextBoard || nextBoard === ctx.board) return;
+      location.assign(boardViewUrl(ctx.project, nextBoard));
+    });
+  }
+
   function createCardFromTask(task) {
     if (!template || !template.content) return null;
     var clone = template.content.cloneNode(true);
@@ -312,6 +359,7 @@
   }
 
   function addDefaultCard(ev) {
+    if (ev.target.closest && ev.target.closest('.board-column-task-count')) return;
     if (!template || !template.content) return;
     var column = ev.currentTarget.closest('.board-column');
     var cardsContainer = column && column.querySelector('.board-column-cards');
@@ -329,9 +377,9 @@
     syncCardTitleLineTooltip(card);
     assignCloudIconToCard(card);
     cardsContainer.appendChild(clone);
-    var badge = column.querySelector('.board-column-add-card');
-    if (badge) {
-      badge.textContent = cardsContainer.querySelectorAll('.board-card').length;
+    var countBadge = column.querySelector('.board-column-task-count');
+    if (countBadge) {
+      countBadge.textContent = cardsContainer.querySelectorAll('.board-card').length;
     }
     var status = column.dataset.status || 'TODO';
     setCardIcon(card, 'cloud-up');
@@ -575,9 +623,9 @@
 
   function updateColumnBadge(column) {
     var cardsContainer = column && column.querySelector('.board-column-cards');
-    var badge = column && column.querySelector('.board-column-add-card');
-    if (badge && cardsContainer) {
-      badge.textContent = cardsContainer.querySelectorAll('.board-card').length;
+    var countBadge = column && column.querySelector('.board-column-task-count');
+    if (countBadge && cardsContainer) {
+      countBadge.textContent = cardsContainer.querySelectorAll('.board-card').length;
     }
   }
 
@@ -797,6 +845,56 @@
     }
   }
 
+  function setupPopoutActions(popoutCard, closePopout, cardsContainer) {
+    var deleteBtn = popoutCard.querySelector('.board-card-delete-btn');
+    var archiveBtn = popoutCard.querySelector('.board-card-archive-btn');
+    var taskRef = popoutCard.dataset.taskRef;
+    if (!taskRef) return;
+    var ctx = getBoardContext();
+    if (!ctx.project) return;
+
+    function removeCardFromBoard() {
+      if (closePopout) closePopout();
+      popoutCard.remove();
+      var column = cardsContainer && cardsContainer.closest('.board-column');
+      if (column) updateColumnBadge(column);
+    }
+
+    if (deleteBtn) {
+      deleteBtn.addEventListener('click', function (e) {
+        e.preventDefault();
+        e.stopPropagation();
+        if (!window.confirm('Delete this task permanently? This cannot be undone.')) return;
+        fetch('/tasks/delete?' + new URLSearchParams({
+          project: ctx.project,
+          board: ctx.board,
+          task: taskRef
+        }).toString(), { method: 'POST', credentials: 'same-origin' })
+          .then(function (res) {
+            if (res.ok) removeCardFromBoard();
+          })
+          .catch(function () {});
+      });
+    }
+
+    if (archiveBtn) {
+      archiveBtn.addEventListener('click', function (e) {
+        e.preventDefault();
+        e.stopPropagation();
+        if (!window.confirm('Archive this task? It will be moved to the Archive board.')) return;
+        fetch('/tasks/archive?' + new URLSearchParams({
+          project: ctx.project,
+          board: ctx.board,
+          task: taskRef
+        }).toString(), { method: 'POST', credentials: 'same-origin' })
+          .then(function (res) {
+            if (res.ok) removeCardFromBoard();
+          })
+          .catch(function () {});
+      });
+    }
+  }
+
   function showCardPopout(card) {
     var cardsContainer = card.parentNode;
     var nextSibling = card.nextSibling;
@@ -838,6 +936,7 @@
     function closeOnBackdrop(e) {
       if (e.target === overlay) close();
     }
+    setupPopoutActions(card, close, cardsContainer);
     overlay.addEventListener('click', closeOnBackdrop);
     document.body.appendChild(overlay);
     updateColumnBadge(cardsContainer.closest('.board-column'));
diff --git a/pygitweb/static/main.css b/pygitweb/static/main.css
index f995e32..ed0b1ee 100644
--- a/pygitweb/static/main.css
+++ b/pygitweb/static/main.css
@@ -978,6 +978,47 @@ pre {
 	min-height: 3rem;
 }
 
+.board-column-header-control {
+	display: inline-flex;
+	align-items: stretch;
+	padding: 0;
+	border: 0;
+	border-radius: var(--bs-border-radius);
+	overflow: hidden;
+	font-size: 0.75rem;
+	font-weight: 600;
+	line-height: 1;
+	color: var(--pgw-text);
+	cursor: pointer;
+}
+
+.board-column-add-label,
+.board-column-task-count {
+	display: inline-flex;
+	align-items: center;
+	padding: 0.35rem 0.5rem;
+}
+
+.board-column-add-label {
+	background-color: var(--pgw-bg);
+	color: var(--pgw-text-muted);
+}
+
+.board-column-task-count {
+	background-color: var(--bs-secondary);
+	color: #fff;
+	cursor: default;
+}
+
+.board-column-header-control:hover .board-column-add-label {
+	background-color: var(--pgw-bg-muted);
+}
+
+.board-column-header-control:focus-visible {
+	outline: 2px solid var(--bs-primary);
+	outline-offset: 2px;
+}
+
 /*
  * Board task error banner: Tabler .alert layout + PGW tokens so it tracks
  * prefers-color-scheme (and matches .card / .table overrides that use --pgw-*).
diff --git a/pygitweb/tasks.py b/pygitweb/tasks.py
index 9ce10cd..7da4150 100644
--- a/pygitweb/tasks.py
+++ b/pygitweb/tasks.py
@@ -15,11 +15,14 @@ from fastapi import APIRouter, Depends, HTTPException, Query, Request
 from fastapi.responses import JSONResponse
 
 from pygittools.tasks import (
+	ARCHIVE_BOARD_NAME,
 	BOARD_REF_PREFIX,
 	TASK_REF_PREFIX,
 	Board,
 	Comment,
 	Task,
+	archive_task_local,
+	delete_task_local,
 	get_board,
 	get_comment,
 	get_task,
@@ -407,25 +410,44 @@ def task_delete(
 	board: str = Query(..., description="Board name"),
 	task_ref: str = Query(..., alias="task", description="Task ref"),
 ) -> JSONResponse:
-	"""Delete a task (remove ref and remove from board.tasks)."""
+	"""Delete a task (remove ref, comments, and board entries)."""
 	repo = pygit2.Repository(os.path.join(settings.PROJECTROOT, project))
 	ref = task_ref if task_ref.startswith("refs/") else _task_ref(task_ref)
 	if ref not in repo.references:
 		raise HTTPException(status_code=404, detail="Task not found")
-	oid_hex = str(repo.references[ref].resolve().target)
-	repo.references.delete(ref)
-	board_ref = _board_ref(board)
-	b = get_board(repo, board_ref)
-	if b and getattr(b, "tasks", None):
-		try:
-			b.tasks = [x for x in b.tasks if x != oid_hex]
-			b.update_message()
-			b.write(repo)
-		except Exception:
-			pass
+	delete_task_local(repo, ref)
 	return JSONResponse(content={"message": "Task deleted"})
 
 
+@task_router.post(
+	"/archive",
+	response_class=JSONResponse,
+	dependencies=[Depends(require_permission(Permission.TASKS, project_from_query=True))],
+)
+def task_archive(
+	project: ValidatedReadableQueryProject,
+	board: str = Query(..., description="Source board name"),
+	task_ref: str = Query(..., alias="task", description="Task ref"),
+	archive_board: str = Query(ARCHIVE_BOARD_NAME, description="Archive board name"),
+) -> JSONResponse:
+	"""Move a task from a board to the archive board (creates archive board if needed)."""
+	if not archive_board or "/" in archive_board or ".." in archive_board:
+		raise HTTPException(status_code=400, detail="Invalid archive board name")
+	repo = pygit2.Repository(os.path.join(settings.PROJECTROOT, project))
+	ref = task_ref if task_ref.startswith("refs/") else _task_ref(task_ref)
+	try:
+		archive_ref = archive_task_local(repo, ref, board, archive_board_name=archive_board)
+	except KeyError as exc:
+		raise HTTPException(status_code=404, detail=str(exc)) from exc
+	return JSONResponse(
+		content={
+			"message": "Task archived",
+			"archive_board": archive_board,
+			"archive_ref": archive_ref,
+		}
+	)
+
+
 # ---------- Comment Routes ----------
 
 comment_router = APIRouter(tags=["comments"])
diff --git a/pygitweb/tasks_routes_test.py b/pygitweb/tasks_routes_test.py
index b37209d..d6577a7 100644
--- a/pygitweb/tasks_routes_test.py
+++ b/pygitweb/tasks_routes_test.py
@@ -405,6 +405,57 @@ class TestTaskRoutes:
 			repo = pygit2.Repository(str(Path(settings.PROJECTROOT) / str(mutable_env["project"])))
 			assert task_ref not in repo.references
 
+		def test_task_archive_response_format(self, mutable_env: dict[str, str | TestClient]) -> None:
+			client = mutable_env["client"]
+			assert isinstance(client, TestClient)
+			create_resp = client.post(
+				"/tasks/create",
+				params={
+					"project": mutable_env["project"],
+					"board": mutable_env["board"],
+					"title": "To archive",
+				},
+			)
+			task_ref: str = create_resp.json()["ref"]
+			archive_resp = client.post(
+				"/tasks/archive",
+				params={
+					"project": mutable_env["project"],
+					"board": mutable_env["board"],
+					"task": task_ref,
+				},
+			)
+			assert archive_resp.status_code == 200
+			archived = archive_resp.json()
+			assert archived["message"] == "Task archived"
+			assert archived["archive_board"] == "Archive"
+			assert archived["archive_ref"] == f"{BOARD_REF_PREFIX}Archive"
+
+			repo = pygit2.Repository(str(Path(settings.PROJECTROOT) / str(mutable_env["project"])))
+			assert task_ref in repo.references
+			main_board = get_board(repo, f"{BOARD_REF_PREFIX}{mutable_env['board']}")
+			assert main_board is not None
+			main_oids = [str(o) for o in main_board.tasks]
+			archived_oid = str(repo.references[task_ref].resolve().target)
+			assert archived_oid not in main_oids
+			archive_board = get_board(repo, f"{BOARD_REF_PREFIX}Archive")
+			assert archive_board is not None
+			assert len(archive_board.tasks) == 1
+
+			list_resp = client.get(
+				"/tasks/list",
+				params={"project": mutable_env["project"], "board": mutable_env["board"]},
+			)
+			refs_on_main = [t["ref"] for t in list_resp.json()]
+			assert task_ref not in refs_on_main
+
+			archive_list_resp = client.get(
+				"/tasks/list",
+				params={"project": mutable_env["project"], "board": "Archive"},
+			)
+			refs_on_archive = [t["ref"] for t in archive_list_resp.json()]
+			assert task_ref in refs_on_archive
+
 		def test_board_create_and_delete_response_format(self, mutable_env: dict[str, str | TestClient]) -> None:
 			client = mutable_env["client"]
 			assert isinstance(client, TestClient)
diff --git a/pygitweb/templates/board.html b/pygitweb/templates/board.html
index edcf6dc..31adcf3 100644
--- a/pygitweb/templates/board.html
+++ b/pygitweb/templates/board.html
@@ -4,10 +4,14 @@
   {% include "board_card.html" without context %}
 </template>
 
-<h1 class="page-title d-flex align-items-center gap-2">
+<h1 class="page-title d-flex align-items-center gap-2 flex-wrap">
   <a href="{{ project_url }}" class="text-reset">{{ project }}</a>
   <span class="text-muted">/</span>
-  <span>{{ board_name }} board</span>
+  <label class="visually-hidden" for="board-board-select">Board</label>
+  <select id="board-board-select" class="form-select form-select-sm board-board-select" aria-label="Board" style="width: auto; min-width: 10rem;">
+    <option value="{{ board_name }}" selected>{{ board_name }}</option>
+  </select>
+  <span class="text-muted">board</span>
 </h1>
 
 <div class="board-columns d-flex gap-3 overflow-auto pb-2" style="min-height: 70vh;" data-project="{{ project }}" data-board="{{ board_name }}">
@@ -17,7 +21,10 @@
        style="min-width: 280px; width: 280px;">
     <div class="d-flex align-items-center justify-content-between mb-2">
       <h3 class="h6 mb-0 text-muted">{{ col.label }}</h3>
-      <button type="button" class="badge bg-secondary border-0 board-column-add-card" aria-label="Add card">{{ col.tasks | length }}</button>
+      <button type="button" class="board-column-header-control board-column-add-card" aria-label="Add task">
+        <span class="board-column-add-label">(+ Add Task)</span>
+        <span class="board-column-task-count">{{ col.tasks | length }}</span>
+      </button>
     </div>
     <div class="board-column-cards d-flex flex-column">
       {% for task in col.tasks %}
diff --git a/pygitweb/templates/board_card.html b/pygitweb/templates/board_card.html
index 29d5201..bd0e6b0 100644
--- a/pygitweb/templates/board_card.html
+++ b/pygitweb/templates/board_card.html
@@ -64,6 +64,10 @@
           </div>
         </details>
       </div>
+      <div class="board-card-actions d-flex gap-2 mb-2">
+        <button type="button" class="btn btn-sm btn-outline-secondary board-card-archive-btn">Archive</button>
+        <button type="button" class="btn btn-sm btn-outline-danger board-card-delete-btn">Delete</button>
+      </div>
       <div class="mb-0 board-card-comments-section">
         <strong class="text-muted">Comments</strong>
         <div class="board-card-comments-list mt-2"></div>
