diff --git a/src/app/(search-app)/documents/search/page.tsx b/src/app/(search-app)/documents/search/page.tsx
index c893789951..da59415164 100644
--- a/src/app/(search-app)/documents/search/page.tsx
+++ b/src/app/(search-app)/documents/search/page.tsx
@@ -1,21 +1,12 @@
import type { Metadata } from "next";
import Link from "next/link";
import { buttonFaceClass } from "@/components/ui/button-face";
-import { cn } from "@/components/ui-primitives";
export const metadata: Metadata = {
title: "Document Search - PsychSift",
description: "Search indexed clinical documents and review matching evidence.",
};
-// Primary buttons wear the area colour, never the shared near-black command
-// fill (design pass, October 2026). Re-pointing the command tokens on the
-// button itself keeps Button's hover and pressed states and leaves every other
-// control on the page alone. In forced colours the accent becomes LinkText,
-// which ButtonText cannot sit on, so the native button pair comes back.
-const clinicalPrimaryAccent =
- "[--command:var(--clinical-accent)] [--command-hover:var(--clinical-accent-hover)] [--command-active:var(--clinical-accent-active)] [--command-contrast:var(--clinical-accent-contrast)] forced-colors:[--command:ButtonFace] forced-colors:[--command-hover:ButtonFace] forced-colors:[--command-active:ButtonFace]";
-
// The empty screen's line drawing: two stacked documents and a magnifier, in
// the area colour. It repeats what the heading says, so it is hidden from
// screen readers. Fills use tokens through classes so dark mode follows.
@@ -82,10 +73,7 @@ export default function DocumentsSearchRoute() {
matching page and passage.
-
+
Open Documents search
diff --git a/src/app/work-mode.css b/src/app/work-mode.css
index 4b41df967c..c641100884 100644
--- a/src/app/work-mode.css
+++ b/src/app/work-mode.css
@@ -119,6 +119,11 @@
--command: var(--work-primary);
--command-hover: var(--mode-identity-deep, var(--work-primary));
--command-contrast: var(--work-primary-text);
+ /* The shared primary Button (button-face.ts) reads these; outside a work frame it falls back to Clinical blue. */
+ --action: var(--command);
+ --action-hover: var(--command-hover);
+ --action-active: var(--mode-identity-deep, var(--work-primary));
+ --action-contrast: var(--command-contrast);
}
/* Glass fallback. Blur is the costliest paint in the frame: on an older or
diff --git a/src/components/my-day/my-day-dashboard.tsx b/src/components/my-day/my-day-dashboard.tsx
index d365390b64..7fcc7cc525 100644
--- a/src/components/my-day/my-day-dashboard.tsx
+++ b/src/components/my-day/my-day-dashboard.tsx
@@ -414,7 +414,6 @@ export function MyDayDashboard({
// ---------------------------------------------------------------- items
// The feed hides what Later moved by the snoozes kept on this device, so Needs you reads the same ones.
const needsYouDevice = needsYouList === items ? device : stored;
- const needsYou = selectNeedsYou(needsYouList, needsYouDevice.snoozes, today);
// The flag repeats only what Needs you lists, so it never shows an item the bell leaves out.
const flagItems = useMemo(() => {
const listed = new Set(needsYouList.map((item) => item.id));
@@ -423,6 +422,9 @@ export function MyDayDashboard({
(id) => isSnoozed(needsYouDevice.snoozes, id, today),
);
}, [items, needsYouList, needsYouDevice.snoozes, today]);
+ // The item the flag shows is not listed again straight below it; with the flag hidden, Needs you keeps it.
+ const flaggedId = !device.hidden.has("flag") ? flagItems[0]?.id : undefined;
+ const needsYou = selectNeedsYou(needsYouList, needsYouDevice.snoozes, today, undefined, flaggedId);
const tomorrow = addDaysToDate(today, 1);
// The next rostered day, offered by Later only when it is not tomorrow (and only from a roster that loaded).
const workingDay = useMemo(
@@ -716,6 +718,7 @@ export function MyDayDashboard({
shown={needsYou.shown}
waiting={needsYou.waiting}
total={needsYou.total}
+ flagged={flaggedId !== undefined}
checked={checked}
checkedAt={checkedAt}
incomplete={incomplete}
diff --git a/src/components/my-day/my-day-today-cards.tsx b/src/components/my-day/my-day-today-cards.tsx
index 9cc49b0623..90c9589c44 100644
--- a/src/components/my-day/my-day-today-cards.tsx
+++ b/src/components/my-day/my-day-today-cards.tsx
@@ -1503,6 +1503,7 @@ export function NeedsYouCard({
shown,
waiting,
total,
+ flagged = false,
checked,
checkedAt,
incomplete = false,
@@ -1521,6 +1522,8 @@ export function NeedsYouCard({
readonly shown: readonly MyDayNeedsYouItem[];
readonly waiting: number;
readonly total: number;
+ /** The flag card above shows one waiting item, so it is counted here but not listed again. */
+ readonly flagged?: boolean;
readonly checked: readonly string[];
/** When the sources were last checked, "14:05". */
readonly checkedAt?: string | null;
@@ -1577,7 +1580,7 @@ export function NeedsYouCard({
className="m-0 px-3.5 py-3 text-xs font-semibold text-[color:var(--text-muted)]"
data-testid="my-day-needs-you-snoozed"
>
- {`${incomplete ? "Nothing else found in the sources that loaded." : "Nothing else needs you today."} ${total - waiting} hidden until tomorrow.`}
+ {`${incomplete ? "Nothing else found in the sources that loaded." : "Nothing else needs you today."}${total > waiting ? ` ${total - waiting} hidden until tomorrow.` : ""}`}
);
} else {
@@ -1590,12 +1593,10 @@ export function NeedsYouCard({
);
}
const atLeast = incomplete ? "at least " : "";
+ // The count describes the rows below, so the item already in the flag is not counted twice.
+ const listed = flagged ? Math.max(waiting - 1, 0) : waiting;
const count =
- waiting === 0
- ? undefined
- : waiting > shown.length
- ? `${atLeast}${shown.length} of ${waiting}`
- : `${atLeast}${waiting}`;
+ listed === 0 ? undefined : listed > shown.length ? `${atLeast}${shown.length} of ${listed}` : `${atLeast}${listed}`;
const stampTone = checked.length === 0 ? "off" : incomplete ? "warn" : "ok";
const stamp =
checked.length === 0
diff --git a/src/components/specifiers/specifier-builder-page.tsx b/src/components/specifiers/specifier-builder-page.tsx
index 5cbcb3f5a2..e17615342c 100644
--- a/src/components/specifiers/specifier-builder-page.tsx
+++ b/src/components/specifiers/specifier-builder-page.tsx
@@ -52,14 +52,6 @@ import {
} from "@/lib/specifiers";
import { copyButton, errorCopy, specifierBuilderCopy } from "@/lib/ui-copy";
-// Primary buttons wear the area colour, never the shared near-black command
-// fill (design pass, October 2026). Re-pointing the command tokens on the
-// button itself keeps Button's hover and pressed states and leaves every other
-// control on the page alone. In forced colours the accent becomes LinkText,
-// which ButtonText cannot sit on, so the native button pair comes back.
-const clinicalPrimaryAccent =
- "[--command:var(--clinical-accent)] [--command-hover:var(--clinical-accent-hover)] [--command-active:var(--clinical-accent-active)] [--command-contrast:var(--clinical-accent-contrast)] forced-colors:[--command:ButtonFace] forced-colors:[--command-hover:ButtonFace] forced-colors:[--command-active:ButtonFace]";
-
const specifierFamilyOrder: Record = {
"episode-features": 0,
"course-onset": 1,
@@ -883,7 +875,6 @@ export function SpecifierBuilderPage({ initialSpecifiers = [] }: { initialSpecif
-