diff --git a/DECISION-SHEET.md b/DECISION-SHEET.md
index cf94363351..94c5629fe2 100644
--- a/DECISION-SHEET.md
+++ b/DECISION-SHEET.md
@@ -38,6 +38,14 @@ Every open question from TOKEN-AUDIT.md §8 + batch logs and COMPONENT-INVENTORY
| C9 | Toast system (no shadcn primitive installed) | Keep custom toast; document as permanent choice (working tone/variant system; sonner migration = churn without user-visible gain) | DEFERRED to Run 4 — decide when toast palette colors get retokenized; sonner-behind-a-pushToast-facade is the alternative to evaluate |
| C10 | FeatureGate wrapper pattern (3 near-identical gates) | Nice-to-have shared primitive; backlog, not a run | APPROVED — backlog nice-to-have, not a run |
+## Gallery feedback round 1 (preset-tune session, Jul 6) — executed
+
+User rulings from the tune-session gallery review; all intentionally visible, snapshots NOT re-baselined (fresh before/after triplets regenerated in tests/storybook-visual/test-results/ against the old baseline):
+
+1. **Dark destructive red reverted** — `.dark --destructive` back to master's original `oklch(0.637 0.237 25.331)` (preset's softer `oklch(0.704 0.191 22.216)` rejected); light mode untouched.
+2. **Budget/quota BAR FILLS reuse status colors** — moving-fill elements only: healthy → `bg-(--status-task-done)`, warning → `bg-(--status-task-todo)`, exceeded/hard-stop → `bg-(--status-task-blocked)` in BudgetPolicyCard.tsx, QuotaBar.tsx (feeds ProviderQuotaCard/BillerSpendCard pages), Costs.tsx, CodexSubscriptionPanel.tsx (escalation tiers only). Inspected and deliberately LEFT: Org.tsx status dot (not a bar), BudgetPolicyCard chip washes/notice borders (not fills), CodexSubscriptionPanel healthy `bg-primary/70` + null `bg-zinc-700` (healthy tier uses brand primary by design — flagged as ambiguous, not emerald).
+3. **RUNNING = status blue, not cyan/teal** — IssueChatThread running chip now composes `brandChipBadge.blue` (layout classes unchanged); RunTranscriptView running label uses new `runningLabelText` export (`text-[#1D4ED8] dark:text-[#2563EB]`, hexes kept in lib/status-colors.ts for gate cleanliness); `statusBadge.running` + `agentStatusDot.running` maps and AgentDetail `runStatusIcons.running` re-pointed cyan→blue. Deliberately LEFT + flagged: `externalObjectStatusIcon/Badge.running` (same-map collision — `open` is already blue there; documented UX-spec tone system), and the cyan "Live" branding family (LiveRunWidget theme, AgentDetail live-card border + Live pulse dots, DesignGuide Live sample) — "Live" is a distinct motif from RUNNING chips; note AgentDetail's mobile Live pill is already blue, so a dedicated Live-color decision is recommended.
+
## Verification status (this review)
- `pnpm check:token-gates` — re-run independently: 3/3 CLEAN (468 files, 31 allowlist entries).
diff --git a/ui/src/components/BudgetPolicyCard.tsx b/ui/src/components/BudgetPolicyCard.tsx
index 3b894cac69..b24692c7f9 100644
--- a/ui/src/components/BudgetPolicyCard.tsx
+++ b/ui/src/components/BudgetPolicyCard.tsx
@@ -104,10 +104,10 @@ export function BudgetPolicyCard({
className={cn(
"h-full rounded-full transition-(--tp-width-background-color) duration-200",
summary.status === "hard_stop"
- ? "bg-red-400"
+ ? "bg-(--status-task-blocked)"
: summary.status === "warning"
- ? "bg-amber-300"
- : "bg-emerald-300",
+ ? "bg-(--status-task-todo)"
+ : "bg-(--status-task-done)",
)}
style={{ width: `${progress}%` }}
/>
diff --git a/ui/src/components/CodexSubscriptionPanel.tsx b/ui/src/components/CodexSubscriptionPanel.tsx
index 9751455e06..5f26314024 100644
--- a/ui/src/components/CodexSubscriptionPanel.tsx
+++ b/ui/src/components/CodexSubscriptionPanel.tsx
@@ -42,8 +42,8 @@ function detailText(window: QuotaWindow): string | null {
function fillClass(usedPercent: number | null): string {
if (usedPercent == null) return "bg-zinc-700";
- if (usedPercent >= 90) return "bg-red-400";
- if (usedPercent >= 70) return "bg-amber-400";
+ if (usedPercent >= 90) return "bg-(--status-task-blocked)";
+ if (usedPercent >= 70) return "bg-(--status-task-todo)";
return "bg-primary/70";
}
diff --git a/ui/src/components/IssueChatThread.tsx b/ui/src/components/IssueChatThread.tsx
index d4ac88df9e..4cc180f563 100644
--- a/ui/src/components/IssueChatThread.tsx
+++ b/ui/src/components/IssueChatThread.tsx
@@ -162,6 +162,7 @@ import {
} from "../lib/transcriptPresentation";
import { buildAgentMentionHref } from "@paperclipai/shared";
import { cn, formatDateTime, formatShortDate } from "../lib/utils";
+import { brandChipBadge } from "../lib/status-colors";
import { nextWorkMode, titleForPendingWorkMode, workModeMetaFor, workModeMetaList } from "../lib/work-mode-meta";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
@@ -1896,7 +1897,14 @@ function IssueChatAssistantMessage({
) : null}
{isRunning ? (
-
+ // Gallery feedback r1: running chip uses the canonical brand blue
+ // (brandChipBadge.blue), not cyan; layout/size classes unchanged.
+
Running
diff --git a/ui/src/components/QuotaBar.tsx b/ui/src/components/QuotaBar.tsx
index 62c16e9523..0fcefde135 100644
--- a/ui/src/components/QuotaBar.tsx
+++ b/ui/src/components/QuotaBar.tsx
@@ -12,9 +12,9 @@ interface QuotaBarProps {
}
function fillColor(pct: number): string {
- if (pct > 90) return "bg-red-400";
- if (pct > 70) return "bg-yellow-400";
- return "bg-green-400";
+ if (pct > 90) return "bg-(--status-task-blocked)";
+ if (pct > 70) return "bg-(--status-task-todo)";
+ return "bg-(--status-task-done)";
}
export function QuotaBar({
diff --git a/ui/src/components/transcript/RunTranscriptView.tsx b/ui/src/components/transcript/RunTranscriptView.tsx
index a8b3453aaa..102fd23e72 100644
--- a/ui/src/components/transcript/RunTranscriptView.tsx
+++ b/ui/src/components/transcript/RunTranscriptView.tsx
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
import type { TranscriptEntry } from "../../adapters";
import { MarkdownBody, type MarkdownExternalReferenceMap } from "../MarkdownBody";
import { cn, formatTokens } from "../../lib/utils";
+import { runningLabelText } from "../../lib/status-colors";
import {
Check,
ChevronDown,
@@ -1057,7 +1058,8 @@ function TranscriptToolGroup({
{humanizeLabel(item.name)}
diff --git a/ui/src/index.css b/ui/src/index.css
index d573f4cb1e..ffc36fe529 100644
--- a/ui/src/index.css
+++ b/ui/src/index.css
@@ -197,7 +197,7 @@
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
- --destructive: oklch(0.704 0.191 22.216);
+ --destructive: oklch(0.637 0.237 25.331); /* Gallery feedback round 1: user preferred master's original dark destructive red over the preset's softer 0.704 0.191 22.216. */
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
diff --git a/ui/src/lib/status-colors.ts b/ui/src/lib/status-colors.ts
index 5192c8fe54..c7133dfdaf 100644
--- a/ui/src/lib/status-colors.ts
+++ b/ui/src/lib/status-colors.ts
@@ -49,7 +49,7 @@ export const issueStatusTextDefault = "text-muted-foreground";
export const statusBadge: Record = {
// Agent statuses
active: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300",
- running: "bg-cyan-100 text-cyan-700 dark:bg-cyan-900/50 dark:text-cyan-300",
+ running: "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300", // Gallery feedback r1: running = status blue (matches in_progress liveness), not cyan.
scheduled_retry: "bg-sky-100 text-sky-700 dark:bg-sky-900/50 dark:text-sky-300",
paused: "bg-orange-100 text-orange-700 dark:bg-orange-900/50 dark:text-orange-300",
idle: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-300",
@@ -146,6 +146,14 @@ export const brandChipBadge: Record = {
red: "bg-[#FEE2E2] text-[#991B1B] border-[#DC2626] dark:bg-[#dc26262e] dark:text-[#DC2626] dark:border-[#dc262673]",
};
+/**
+ * Brand blue TEXT pair (the text hues of `brandChipBadge.blue`) for non-chip
+ * "Running" labels — Gallery feedback round 1: running-state copy uses the
+ * canonical status blue, not cyan/teal. Kept here so components stay free of
+ * hex literals (token-gate scope).
+ */
+export const runningLabelText = "text-[#1D4ED8] dark:text-[#2563EB]";
+
/**
* Issue/task status → brand colour name (PAP-75). `in_progress` is blue
* (liveness), `todo` amber (queued), `in_review` violet (awaiting review),
@@ -219,7 +227,7 @@ export const taskStatusIconVarDefault = "--status-task-icon-backlog";
// ---------------------------------------------------------------------------
export const agentStatusDot: Record = {
- running: "bg-cyan-400 animate-pulse",
+ running: "bg-blue-400 animate-pulse", // Gallery feedback r1: running dot = blue, not cyan.
active: "bg-green-400",
paused: "bg-yellow-400",
idle: "bg-yellow-400",
diff --git a/ui/src/pages/AgentDetail.tsx b/ui/src/pages/AgentDetail.tsx
index e62684b707..c196c99b3e 100644
--- a/ui/src/pages/AgentDetail.tsx
+++ b/ui/src/pages/AgentDetail.tsx
@@ -116,7 +116,7 @@ import {
const runStatusIcons: Record = {
succeeded: { icon: CheckCircle2, color: "text-green-600 dark:text-green-400" },
failed: { icon: XCircle, color: "text-red-600 dark:text-red-400" },
- running: { icon: Loader2, color: "text-cyan-600 dark:text-cyan-400" },
+ running: { icon: Loader2, color: "text-blue-600 dark:text-blue-400" }, // Gallery feedback r1: running = status blue, not cyan.
queued: { icon: Clock, color: "text-yellow-600 dark:text-yellow-400" },
scheduled_retry: { icon: Clock, color: "text-sky-600 dark:text-sky-400" },
timed_out: { icon: Timer, color: "text-orange-600 dark:text-orange-400" },
diff --git a/ui/src/pages/Costs.tsx b/ui/src/pages/Costs.tsx
index 90881ba3c6..b51a3f59ee 100644
--- a/ui/src/pages/Costs.tsx
+++ b/ui/src/pages/Costs.tsx
@@ -689,10 +689,10 @@ export function Costs() {
className={cn(
"h-full transition-(--tp-width-background-color) duration-150",
spendData.summary.utilizationPercent > 90
- ? "bg-red-400"
+ ? "bg-(--status-task-blocked)"
: spendData.summary.utilizationPercent > 70
- ? "bg-yellow-400"
- : "bg-emerald-400",
+ ? "bg-(--status-task-todo)"
+ : "bg-(--status-task-done)",
)}
style={{ width: `${Math.min(100, spendData.summary.utilizationPercent)}%` }}
/>