diff --git a/web/src/app/_ui/job-row.tsx b/web/src/app/_ui/job-row.tsx
index 4a8d5d6..63e7fb3 100644
--- a/web/src/app/_ui/job-row.tsx
+++ b/web/src/app/_ui/job-row.tsx
@@ -14,6 +14,7 @@ export function JobRow({
album,
kind,
label,
+ marker,
meta,
note,
bar,
@@ -22,6 +23,7 @@ export function JobRow({
album: string;
kind: Kind;
label: string;
+ marker?: string;
meta?: ReactNode;
note?: ReactNode;
bar?: ReactNode;
@@ -31,6 +33,7 @@ export function JobRow({
+ {marker ? {marker} : null}
{album} · {artist}
{meta ?
{meta}
: null}
diff --git a/web/src/app/design.css b/web/src/app/design.css
index 0bc14a7..fcdd2b1 100644
--- a/web/src/app/design.css
+++ b/web/src/app/design.css
@@ -388,6 +388,13 @@ nav.contents .sep { flex: 1; }
.tab .n { color: var(--rule-2); margin-left: 6px; }
.tab.has-errors .n { color: var(--alert); }
+/* "Next up" badge on the first queued row (the item the worker claims next) */
+.nextup {
+ display: inline-block; vertical-align: middle; margin-right: 9px; transform: translateY(-2px);
+ font-family: var(--mono); font-size: 0.56rem; letter-spacing: 0.13em; text-transform: uppercase;
+ color: var(--accent-ink); background: var(--accent); padding: 2px 7px; border-radius: 4px;
+}
+
/* ── Kebab menu (⋮ popup for press actions) ───────────── */
.dept.has-actions { align-items: center; }
.dept.has-actions .fill { transform: none; }
diff --git a/web/src/app/queue.tsx b/web/src/app/queue.tsx
index 3c060c0..b5066dd 100644
--- a/web/src/app/queue.tsx
+++ b/web/src/app/queue.tsx
@@ -189,6 +189,14 @@ export function Queue() {
const j = jobOf(r);
buckets[categoryOf(j.state, j.currentStage)].push(r);
}
+ // The API returns rows newest-first, but the worker claims the OLDEST queued job next
+ // (claim_next: ORDER BY createdAt ASC). Order the queue tab to match, so the item that runs
+ // next sits at the top instead of the bottom.
+ const ts = (r: Row) => (r.createdAt ? new Date(r.createdAt).getTime() : 0);
+ buckets.queue.sort((a, b) => ts(a) - ts(b));
+ // "Next up" = the first queued item the worker will actually claim (oldest, not individually
+ // paused) — mark it so the ordering reads clearly.
+ const nextUpId = buckets.queue.find((r) => !jobOf(r).paused)?.id ?? null;
const queuedCount = buckets.queue.length;
const downloading = buckets.active.length > 0;
// Smart default: land on the tab that has something worth looking at.
@@ -289,6 +297,7 @@ export function Queue() {
album={r.album}
kind={d.kind}
label={d.label}
+ marker={r.id === nextUpId ? "Next up" : undefined}
meta={meta}
note={note}
bar={bar}