Skip to content
7 changes: 6 additions & 1 deletion data/repo-awareness-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": "repo-awareness-snapshot-v3",
"captured_revision": {
"committed_at": "2026-10-15"
"committed_at": "2026-10-16"
},
"routes": {
"modes": [
Expand Down Expand Up @@ -2500,6 +2500,11 @@
"section": "agents",
"catalogued": true
},
{
"path": "docs/decisions/2026-10-16-admin-shows-the-date-line.md",
"section": "decisions",
"catalogued": true
},
{
"path": "docs/product/clinical-trust-direction.md",
"section": "product",
Expand Down
10 changes: 10 additions & 0 deletions docs/decisions/2026-10-16-admin-shows-the-date-line.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Decision: Admin shows the date line over its title, like the other Work pages

- **Status:** decided 2026-10-16
- **Source:** `src/components/admin/admin-today-page.tsx`, find "The owner brought the date line back on 16 Oct 2026"

The owner approved a before and after picture of Admin with the date over its title, matching My Day,
CPD, Roster and Teaching. This replaces the earlier wish, from the 13 Oct 2026 design audit, to leave
the date line off Admin.

If this summary and the source ever differ, the source wins.
8 changes: 5 additions & 3 deletions src/components/admin/admin-today-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ import { useWorkTimeZone } from "@/components/work-time/use-work-time-zone";
*
* Phone is one column in that order. From a laptop width it splits into what
* to act on (left) and what is ahead (right); left then right IS the phone
* order. The band names the page "Admin" (audit D15, 13 Oct 2026): no greeting,
* and no date line, which the owner asked to drop from Admin.
* order. The band names the page "Admin" (audit D15, 13 Oct 2026) with no
* greeting. The owner brought the date line back on 16 Oct 2026, after seeing it
* beside the other Work pages, so Admin shows the date over its title like them.
*
* Fail-closed: while loading, signed out or after a failed load nothing from
* the reader's records is drawn, never a saved copy (the device cache holds
Expand Down Expand Up @@ -110,7 +111,8 @@ export function AdminTodayPage({ now: nowProp }: { now?: Date } = {}) {
const load = adminLoadState(state);
const bandShown = useModeBandShown();
const dateEcho = formatZonedLongDay(today);
useModeBandHeading({ title: "Admin" });
// The date line over the title, as every other Work page has it (My Day, CPD, Roster, Teaching).
useModeBandHeading({ eyebrow: dateEcho, title: "Admin" });
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Resolved

const own = useMemo(() => selectAdminOwnEntries(state), [state]);
const shared = useMemo(() => selectAdminSharedEntries(state), [state]);
Expand Down
3 changes: 2 additions & 1 deletion src/components/admin/admin-work.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -512,9 +512,10 @@
fill: currentColor;
}

/* Full hero ink, not faded: the labels are small, so they keep their contrast
on the slate (design follow-up 15 Oct 2026). */
.onHero .windowLabels {
color: inherit;
opacity: 0.86;
}

.onHero .windowLabels b {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export function AnswerSuggestionChips({
layout = "wrap",
className,
icon: Icon,
chipShape = "pill",
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Resolved
}: {
suggestions: string[];
onPick: (suggestion: string) => void;
Expand All @@ -129,6 +130,12 @@ export function AnswerSuggestionChips({
// Optional leading glyph rendered inside every chip — used to signal a chip's
// kind (e.g. a history icon on recent-search chips) without changing the label.
icon?: LucideIcon;
// "control" squares the chip to the Clinical control rung (`rounded-lg`, as
// `controlBase`): rounded pills are a Work-mode shape only (owner decision,
// 15 Oct 2026). The chip's base rule in globals.css is unlayered, so the
// utility needs the important modifier to win. Default stays "pill" so the
// answer-thread follow-ups and any Work caller keep their shape.
chipShape?: "pill" | "control";
}) {
const scrolls = layout === "scroll";
const { ref: railRef, node: railNode, edges } = useRailScrollEdges();
Expand Down Expand Up @@ -166,7 +173,7 @@ export function AnswerSuggestionChips({
type="button"
disabled={disabled}
onClick={() => onPick(suggestion)}
className={cn("answer-suggestion-chip", focusRing)}
className={cn("answer-suggestion-chip", chipShape === "control" && "!rounded-lg", focusRing)}
>
{Icon ? (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { useSavedRegistryFavourites } from "@/components/clinical-dashboard/use-
import { cn } from "@/components/ui-primitives";
import { appModeDefinition, filterCrossModesForSession, type AppModeId } from "@/lib/app-modes";
import { appModeIcons } from "@/lib/app-mode-icons";
import { modeMenuSideForMode } from "@/lib/phone-mode-groups";
import {
commandDropdownCanDisplay,
commandDropdownMinimumWidthMediaQuery,
Expand Down Expand Up @@ -305,7 +306,15 @@ function SmartIntentCue({ active, modeLabel }: { active: boolean; modeLabel: str
);
}

function SmartPromptRow({ examples, onPickExample }: { examples: string[]; onPickExample: (example: string) => void }) {
function SmartPromptRow({
modeId,
examples,
onPickExample,
}: {
modeId: AppModeId;
examples: string[];
onPickExample: (example: string) => void;
}) {
return (
<AnswerSuggestionChips
suggestions={examples}
Expand All @@ -314,6 +323,8 @@ function SmartPromptRow({ examples, onPickExample }: { examples: string[]; onPic
testId="smart-search-prompt-row"
layout="scroll"
className="smart-search-prompt-row"
// Clinical keeps its square-ish control corners; pills are Work's shape.
chipShape={modeMenuSideForMode(modeId) === "clinical" ? "control" : "pill"}
/>
);
}
Expand Down Expand Up @@ -1264,6 +1275,7 @@ export function UniversalSearchCommandSurface({
</div>
{showHomeSuggestions ? (
<SmartPromptRow
modeId={modeId}
examples={config.examples}
onPickExample={(example) => {
onQueryChange(example);
Expand Down
7 changes: 6 additions & 1 deletion src/components/cme/cme-work.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ html[data-mode-identity="cme"] {
--cme-hero-track: color-mix(in srgb, white 14%, transparent);
--cme-hero-line: color-mix(in srgb, white 22%, transparent);
--cme-hero-panel: color-mix(in srgb, white 10%, transparent);
--cme-hero-soft-text: color-mix(in srgb, white 82%, transparent);
/* The hero's quieter words (the kicker, "of 50 h", the legend, the pace line):
the hero's own ink, barely lifted, so they stay readable on the copper
(design follow-up 15 Oct 2026: at 82% white they read as faint). */
--cme-hero-soft-text: color-mix(in srgb, var(--mode-identity-contrast) 94%, transparent);
}

html.dark [data-mode-identity="cme"],
Expand All @@ -39,6 +42,8 @@ html.dark[data-mode-identity="cme"] {
--cme-cat-4: color-mix(in oklab, var(--mode-identity) 24%, var(--surface-raised));
--cme-hero-cat-2: color-mix(in oklab, var(--mode-identity) 70%, white);
--cme-hero-cat-3: var(--mode-identity);
/* The dark hero is inked with the heading colour, not white. */
--cme-hero-soft-text: color-mix(in srgb, var(--text-heading) 92%, transparent);
}

/* --------------------------------------------------------------- page body */
Expand Down
9 changes: 8 additions & 1 deletion src/components/favourites/favourites-shelf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,14 @@ function TileFace({ item }: { item: FavouriteItem }) {
) : null}
</span>
<span className="block w-full min-w-0">
<span className="line-clamp-2 break-words hyphens-auto text-xs font-bold leading-tight text-[color:var(--work-ink)]">
{/* Words wrap only at spaces (no break-words, no hyphens: Chromium splits unknown drug
names mid-word, "Acamprosa te"). Two lines, then an ellipsis; a single word too long
for the tile ends in an ellipsis rather than being cut in two (15 Oct 2026). At large
text the clamp lifts, so there a long word may break rather than spill past the tile. */}
<span
data-testid="favourites-shelf-tile-name"
className="line-clamp-2 overflow-hidden text-ellipsis [overflow-wrap:normal] [word-break:normal] [html[data-large-text]_&]:[overflow-wrap:break-word] text-xs font-bold leading-tight text-[color:var(--work-ink)]"
>
{label}
</span>
<span
Expand Down
1 change: 1 addition & 0 deletions src/components/formulation/formulation-home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ function FormulationResults({ query }: { query: string }) {
label="Try another pattern"
labelPlacement="above"
layout="scroll"
chipShape="control"
testId="formulation-pattern-suggestions"
suggestions={formulationSearchPresets.map((preset) => preset.label)}
onPick={(label) => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/medicines/medicines-home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,9 @@ function FindArea() {
<Link
key={suggestion}
href={appModeHomeHref("prescribing", { query: suggestion, run: true })}
className={cn(focusRing, "inline-flex min-h-12 shrink-0 items-center rounded-xl no-underline")}
className={cn(focusRing, "inline-flex min-h-12 shrink-0 items-center rounded-lg no-underline")}
>
<span className="whitespace-nowrap rounded-xl border border-[color:var(--dash-line-strong)] bg-[color:var(--dash-raised)] px-3 py-1.5 text-sm text-[color:var(--dash-ink)] forced-colors:border">
<span className="whitespace-nowrap rounded-lg border border-[color:var(--dash-line-strong)] bg-[color:var(--dash-raised)] px-3 py-1.5 text-sm text-[color:var(--dash-ink)] forced-colors:border">
{suggestion}
</span>
</Link>
Expand Down
2 changes: 1 addition & 1 deletion src/components/my-day/my-day-today-cards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ export function ShiftLegend({
<ul
role="list"
aria-label="Key"
className="m-0 flex min-w-0 flex-1 list-none flex-wrap gap-x-3 gap-y-1 p-0 text-2xs font-semibold text-[color:var(--text-muted)]"
className="m-0 flex min-w-0 flex-1 list-none flex-wrap gap-x-3 gap-y-1 p-0 text-xs font-semibold text-[color:var(--text-muted)]"
Comment thread
BigSimmo marked this conversation as resolved.
Resolved
>
{shown.map(([kind, label]) => (
<li key={kind} className="inline-flex items-center gap-1.25">
Expand Down
73 changes: 49 additions & 24 deletions src/components/on-call/kit/hospital-line.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,35 +22,60 @@ export function OnCallHospitalLine({
readonly handbook: HospitalHandbookState;
readonly testId?: string;
}) {
const [open, setOpen] = useState(false);
const name = handbook.siteName ?? handbook.serviceName;
if (!name) return null;
const canChange = handbook.hospitals.length > 1;
return (
<div className="flex min-w-0 flex-wrap items-center gap-x-3 px-1" data-testid={testId}>
<p className={cn(modeNameText, "min-w-0 break-words text-base-minus text-[color:var(--text-heading)]")}>{name}</p>
{canChange ? (
<>
<button
type="button"
aria-haspopup="dialog"
aria-label={`Change hospital, currently ${name}`}
onClick={() => setOpen(true)}
className={cn(
focusRing,
"inline-flex min-h-12 items-center rounded-md px-1 text-base-minus font-medium text-[color:var(--clinical-accent)]",
)}
data-testid={`${testId}-change`}
>
Change
</button>
<Sheet open={open} onClose={() => setOpen(false)} title="Choose hospital" testId={`${testId}-sheet`}>
<div data-mode-identity="on-call" className="min-w-0">
<OnCallHospitalChooser handbook={handbook} onChosen={() => setOpen(false)} />
</div>
</Sheet>
</>
) : null}
<OnCallHospitalChange handbook={handbook} testId={testId} />
</div>
);
}

/**
* The "Change" control on its own, with its chooser sheet, for a surface that
* already names the hospital (Now's summary hero). Renders nothing unless the
* reader belongs to more than one hospital. `variant="hero"` draws it as the
* hero's quiet pill; the default is the hospital line's text button.
*/
export function OnCallHospitalChange({
handbook,
testId,
variant = "line",
}: {
readonly handbook: HospitalHandbookState;
/** Stem for the control's testids: `${testId}-change` and `${testId}-sheet`. */
readonly testId: string;
readonly variant?: "line" | "hero";
}) {
const [open, setOpen] = useState(false);
const name = handbook.siteName ?? handbook.serviceName;
if (!name || handbook.hospitals.length <= 1) return null;
return (
<>
<button
type="button"
aria-haspopup="dialog"
aria-label={`Change hospital, currently ${name}`}
onClick={() => setOpen(true)}
className={
variant === "hero"
? "work-hero__action"
: cn(
focusRing,
"inline-flex min-h-12 items-center rounded-md px-1 text-base-minus font-medium text-[color:var(--clinical-accent)]",
)
}
data-variant={variant === "hero" ? "quiet" : undefined}
data-testid={`${testId}-change`}
>
{variant === "hero" ? "Change hospital" : "Change"}
</button>
<Sheet open={open} onClose={() => setOpen(false)} title="Choose hospital" testId={`${testId}-sheet`}>
<div data-mode-identity="on-call" className="min-w-0">
<OnCallHospitalChooser handbook={handbook} onChosen={() => setOpen(false)} />
</div>
</Sheet>
</>
);
}
27 changes: 25 additions & 2 deletions src/components/on-call/now/on-call-hero.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import { Phone } from "lucide-react";
import type { ReactNode } from "react";

import { WorkHero } from "@/components/mode-kit/work";
import { onCallShiftClock } from "@/components/on-call/now/shift-clock";
Expand All @@ -25,6 +26,11 @@ import type { OnCallShiftContext } from "@/lib/on-call/shift-context";
* window (as "Right now" reads it), and the number is the hospital's recorded
* switchboard. With no switchboard recorded there is no pill, never an invented
* number. While the handbook loads the pill's place is held, so nothing moves.
*
* The hero is also where the page names the hospital (its eyebrow), so the
* "Change hospital" control sits here as a quiet second pill, not on a separate
* hospital line that would repeat the name. It is passed in as
* `hospitalChange`, and is absent for a reader with only one hospital.
*/
export function NowOnCallHero({
context,
Expand All @@ -37,6 +43,7 @@ export function NowOnCallHero({
rosterError = false,
rosterSignedOut = false,
now,
hospitalChange = null,
}: {
readonly context: OnCallShiftContext;
/** The hospital's switchboard row, or null when none is recorded. */
Expand All @@ -51,6 +58,8 @@ export function NowOnCallHero({
/** Signed out, the roster read returns nothing, which is not the same as no shift. */
readonly rosterSignedOut?: boolean;
readonly now: Date;
/** The quiet "Change hospital" pill, or null when there is nothing to change. */
readonly hospitalChange?: ReactNode;
}) {
const hospitalPhone = useOnCallHospitalPhone();
const clock = onCallShiftClock(context, now);
Expand All @@ -68,7 +77,14 @@ export function NowOnCallHero({
? `Switchboard ${dial.display}`
: null;

const eyebrow = [clock ? "On call now" : "Right now", hospitalName].filter(Boolean).join(" · ");
const lead = clock ? "On call now" : "Right now";
const eyebrow = hospitalName ? (
<>
{lead} · <span data-testid="on-call-now-hospital">{hospitalName}</span>
</>
) : (
lead
);
const title = clock
? `${onCallDurationWords(clock.minutesLeft)} left`
: cover && periodLabel
Expand Down Expand Up @@ -120,7 +136,14 @@ export function NowOnCallHero({
eyebrow={eyebrow}
title={<span className="nums">{title}</span>}
sub={sub ? <span className="nums">{sub}</span> : null}
footer={action ? <div className="work-hero__actions">{action}</div> : null}
footer={
action || hospitalChange ? (
<div className="work-hero__actions">
{action}
{hospitalChange}
</div>
) : null
}
/>
);
}
Loading
Loading