diff --git a/docs/design-system/GATES.md b/docs/design-system/GATES.md index fc2da1219f..171bba6473 100644 --- a/docs/design-system/GATES.md +++ b/docs/design-system/GATES.md @@ -132,23 +132,23 @@ rendered size. The `*-minus` steps sit one notch under the Tailwind default they named for. This table is documentation, not a gate: do not delete type steps, and do not add a blocking ESLint rule for which existing step a surface chooses. -| Size | Utility | When to use | -| ----- | ----------------- | ------------------------------------------------------------------- | -| 10px | `text-3xs` | Micro floor: dense metadata, legends, count bubbles | -| 11px | `text-2xs` | Chip compact / secondary labels | -| 12px | `text-xs` | Tailwind default; Chip `standard` | -| 13px | `text-sm-minus` | One notch under `text-sm` (14px) — dense body/labels | -| 14px | `text-sm` | Tailwind default body/label | -| 15px | `text-base-minus` | One notch under `text-base` (16px) — comfortable body just under 16 | -| 16px | `text-base` | Tailwind default | -| 17px | `text-lg-minus` | One notch under `text-lg` (18px) | -| 18px | `text-lg` | Tailwind default | -| 20px | `text-xl` | Tailwind default | -| 22px | `text-2xl-minus` | One notch under `text-2xl` (24px) | -| 24px | `text-2xl` | Tailwind default | -| 26px | `text-3xl-minus` | One notch under `text-3xl` (30px) | -| 30px | `text-3xl` | Tailwind default | -| fluid | `text-hero` | Mode-home display heading (`clamp`) | +| Size | Utility | When to use | +| ----- | ----------------- | -------------------------------------------------------------------- | +| 12px | `text-3xs` | Legacy alias, now at the 12 px floor (Ward Flow review, 12 Oct 2026) | +| 12px | `text-2xs` | Legacy alias, now at the 12 px floor (Ward Flow review, 12 Oct 2026) | +| 12px | `text-xs` | Tailwind default; Chip `standard` | +| 13px | `text-sm-minus` | One notch under `text-sm` (14px) — dense body/labels | +| 14px | `text-sm` | Tailwind default body/label | +| 15px | `text-base-minus` | One notch under `text-base` (16px) — comfortable body just under 16 | +| 16px | `text-base` | Tailwind default | +| 17px | `text-lg-minus` | One notch under `text-lg` (18px) | +| 18px | `text-lg` | Tailwind default | +| 20px | `text-xl` | Tailwind default | +| 22px | `text-2xl-minus` | One notch under `text-2xl` (24px) | +| 24px | `text-2xl` | Tailwind default | +| 26px | `text-3xl-minus` | One notch under `text-3xl` (30px) | +| 30px | `text-3xl` | Tailwind default | +| fluid | `text-hero` | Mode-home display heading (`clamp`) | ⚠️ **The "1 318 call sites" figure this section used to quote was a repo-wide grep including `src/app/mockups/**`, which every one of these gates excludes** — 1 360 at `7aaf9349c`. diff --git a/docs/design-system/TOKENS.md b/docs/design-system/TOKENS.md index 1dcc8bf00f..0f6bb894be 100644 --- a/docs/design-system/TOKENS.md +++ b/docs/design-system/TOKENS.md @@ -41,27 +41,27 @@ This is the two-sources-of-truth failure C1 exists to kill. One winner per role: Declared in the `.ckb-v2` structural block **[verified: full read at `ef13a072a`]**. -| Group | Roles | Notes | -| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Space scale | `--space-0` … `--space-11` | 4px base. Markup never references the raw scale — semantic tokens only. | -| Semantic gaps | `--gap-tight` · `--gap-inline` · `--gap-stack` · `--gap-block` · `--gap-section` | The only inter-element spacing markup may use. | -| Semantic padding | `--pad-chip-x` · `--pad-control-x` · `--pad-cta-x` · `--pad-card` · `--pad-panel` · `--pad-strip` | Heading-inset convention rides on these (SPEC §4.6). | -| Page | `--page-gutter` · `--page-max` · `--measure` · `--header-h` | `--measure` is the prose measure — never wider. | -| Type steps | `--text-{xs,sm,body,md,lg,xl}` · `--text-hero` · `--text-hero--line-height` · `--text-hero-tr` | Seven steps. Shared `--leading-prose`; hero keeps `--text-hero--line-height` / `--text-hero-tr` (gated). Unused xs–xl `-lh`/`-tr` companions are orphans — do not re-require them. ⚠️ `--text-hero--line-height` is the one double-dash name — keep as-is; renaming is churn without a defect. | -| Chip sizes | `Chip` `compact` → `--text-2xs` (legacy compat step); `standard` (default) → `--text-xs` | Canonical (#220): dense surfaces opt into `size="compact"`; default adoption keeps `--text-xs`. `--text-2xs` stays a compatibility alias for Chip compact / legacy call sites — not a new v2 type step to invent against. Do not densify every Chip to chase the globals “small chips” note. | -| Type companions | `--leading-prose` · `--tracking-eyebrow` · `--nums` | `--nums` = numeric variant for all data. | -| Weights | `--font-weight-{body,label,heading,value}` | One job each; display uses heading weight (SPEC §4.6). | -| Radius | `--radius-{sm,md,lg,xl,2xl}` | One step per surface role. v2 and live `--radius-md` agreed in PR 5c and are now pinned to each other by the live token contract. | -| Icons | `--icon-{xs,sm,md,lg}` | Paired to adjacent type steps. | -| Density | `--tap-min` (alias of `--spacing-tap`, §1) · `--spacing-compact-meta` · `--chip-height` · `--row-comfortable` · `--row-compact` · `--cell-pad-{comfortable,compact}` | Tap target is not row height (gated on resolved values, not strings, now that `--tap-min` is an alias). Compact-meta is 40px metadata/disclosure only; `--row-compact` is 36px row height, never a tap substitute. | -| Accent rules | `--rule-w` · `--rule-accent` · `--rule-warning` | ⚠️ Reference `var(--clinical-accent)` / `var(--warning)` **without declaring them** — v2 depends on the live layer for both (§5). | -| Evidence gutter | `--gutter-col` · `--gutter-dot` · `--gutter-line-w` | One gutter column owns line and dot (SPEC §7). | -| Evidence spine | `--spine-w` · `--spine-current` · `--spine-stale` | Authored `59e4c3dfc` (§1); derived from rule/accent/warning roles. | -| Status mark | `--status-mark-size` · `--status-mark-stroke` | Authored `59e4c3dfc` (§1); codifies `StatusMark`'s inline geometry. | -| Quantity | `--quantity-unit-tracking` · `--quantity-unit-gap` | Winners per §1. | -| Dashed edge | `--border-dashed` | Drop targets / "nothing here yet" — distinct from `--border-strong`, which means emphasis. | -| Stacking | `--z-{base,raised,chrome,overlay,popover,modal,toast}` | Each rung names its `--eN` partner; toast above modal deliberately. | -| Motion | `--duration-{fast,base,slow}` · `--ease-standard` · `--ease-physical` | Durations zero under `prefers-reduced-motion` (gated). | +| Group | Roles | Notes | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Space scale | `--space-0` … `--space-11` | 4px base. Markup never references the raw scale — semantic tokens only. | +| Semantic gaps | `--gap-tight` · `--gap-inline` · `--gap-stack` · `--gap-block` · `--gap-section` | The only inter-element spacing markup may use. | +| Semantic padding | `--pad-chip-x` · `--pad-control-x` · `--pad-cta-x` · `--pad-card` · `--pad-panel` · `--pad-strip` | Heading-inset convention rides on these (SPEC §4.6). | +| Page | `--page-gutter` · `--page-max` · `--measure` · `--header-h` | `--measure` is the prose measure — never wider. | +| Type steps | `--text-{xs,sm,body,md,lg,xl}` · `--text-hero` · `--text-hero--line-height` · `--text-hero-tr` | Seven steps. Shared `--leading-prose`; hero keeps `--text-hero--line-height` / `--text-hero-tr` (gated). Unused xs–xl `-lh`/`-tr` companions are orphans — do not re-require them. ⚠️ `--text-hero--line-height` is the one double-dash name — keep as-is; renaming is churn without a defect. | +| Chip sizes | `Chip` `compact` → `--text-2xs` (legacy compat step); `standard` (default) → `--text-xs` | Canonical (#220): dense surfaces opt into `size="compact"`; default adoption keeps `--text-xs`. `--text-2xs` stays a compatibility alias for Chip compact / legacy call sites (since 12 Oct 2026 it and `--text-3xs` resolve to 12 px, the floor for every word on screen) — not a new v2 type step to invent against. Do not densify every Chip to chase the globals “small chips” note. | +| Type companions | `--leading-prose` · `--tracking-eyebrow` · `--nums` | `--nums` = numeric variant for all data. | +| Weights | `--font-weight-{body,label,heading,value}` | One job each; display uses heading weight (SPEC §4.6). | +| Radius | `--radius-{sm,md,lg,xl,2xl}` | One step per surface role. v2 and live `--radius-md` agreed in PR 5c and are now pinned to each other by the live token contract. | +| Icons | `--icon-{xs,sm,md,lg}` | Paired to adjacent type steps. | +| Density | `--tap-min` (alias of `--spacing-tap`, §1) · `--spacing-compact-meta` · `--chip-height` · `--row-comfortable` · `--row-compact` · `--cell-pad-{comfortable,compact}` | Tap target is not row height (gated on resolved values, not strings, now that `--tap-min` is an alias). Compact-meta is 40px metadata/disclosure only; `--row-compact` is 36px row height, never a tap substitute. | +| Accent rules | `--rule-w` · `--rule-accent` · `--rule-warning` | ⚠️ Reference `var(--clinical-accent)` / `var(--warning)` **without declaring them** — v2 depends on the live layer for both (§5). | +| Evidence gutter | `--gutter-col` · `--gutter-dot` · `--gutter-line-w` | One gutter column owns line and dot (SPEC §7). | +| Evidence spine | `--spine-w` · `--spine-current` · `--spine-stale` | Authored `59e4c3dfc` (§1); derived from rule/accent/warning roles. | +| Status mark | `--status-mark-size` · `--status-mark-stroke` | Authored `59e4c3dfc` (§1); codifies `StatusMark`'s inline geometry. | +| Quantity | `--quantity-unit-tracking` · `--quantity-unit-gap` | Winners per §1. | +| Dashed edge | `--border-dashed` | Drop targets / "nothing here yet" — distinct from `--border-strong`, which means emphasis. | +| Stacking | `--z-{base,raised,chrome,overlay,popover,modal,toast}` | Each rung names its `--eN` partner; toast above modal deliberately. | +| Motion | `--duration-{fast,base,slow}` · `--ease-standard` · `--ease-physical` | Durations zero under `prefers-reduced-motion` (gated). | ### Compact-meta vs tap (DS-P1-09 / DS-P2-24) diff --git a/scripts/check-type-scale.mjs b/scripts/check-type-scale.mjs index 1dc6fd63e2..85f124d941 100644 --- a/scripts/check-type-scale.mjs +++ b/scripts/check-type-scale.mjs @@ -4,7 +4,8 @@ // // The scale lives in the @theme block of src/app/globals.css: named steps // text-3xs … text-3xl-minus, on top of Tailwind's default xs/sm/base/lg/xl/2xl/3xl. -// text-3xs (10px) is the floor; the old 8px text-4xs step is retired. +// 12px is the floor: text-3xs and text-2xs are legacy names that now alias xs +// (owner decision 12 Oct 2026); the old 8px text-4xs step is retired. // Arbitrary text-[] values re-introduce off-scale sizes; this check // tracks that drift. Colour utilities (text-[color:var(--…)]) are the sanctioned // token-access form and are intentionally NOT flagged. diff --git a/src/app/api/account/preferences/route.ts b/src/app/api/account/preferences/route.ts index 459a57e72d..b5c633fba3 100644 --- a/src/app/api/account/preferences/route.ts +++ b/src/app/api/account/preferences/route.ts @@ -95,6 +95,7 @@ const preferencesPatchSchema = z showRecentOnHome: z.boolean(), showProtocolsOnHome: z.boolean(), compactCitations: z.boolean(), + brightLight: z.boolean(), // Omitted keys are preserved from the stored account row on PUT — never // defaulted here, or a pre-change tab can overwrite a stored opt-out. saveRecentSearches: z.boolean(), diff --git a/src/app/globals.css b/src/app/globals.css index 3c3908c356..b6f900ebd3 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -236,11 +236,11 @@ `eyebrowText` in ui-primitives.tsx for the uppercase eyebrow treatment (`--text-2xs` used to bake that tracking in; it is now the neutral 11px step and the tracking lives at the recipe/call site instead). - 3xs 10px micro floor: dense metadata, legend labels, count - bubbles. An 8px `4xs` step existed here and is retired - — unreadable at any density in a clinical product, so - nothing below 10px is a legal step. ≤9px snaps here. - 2xs 11px Chip `compact` / secondary labels (most common small size) + 3xs 12px legacy name, now an alias of xs. 12px is the floor for + every word on screen (owner decision, 12 Oct 2026, from + the Ward Flow review): the old 10px and 8px steps were + too small to read on a phone in a clinical product. + 2xs 12px legacy name, now an alias of xs (was 11px) (xs 12 · sm 14 · base 16 come from Tailwind's default scale — Chip `standard` uses xs / 12px; see docs/design-system/TOKENS.md) sm-minus 13px one notch under sm — dense body/labels bigger than xs @@ -248,8 +248,8 @@ (lg 18 · xl 20 · 2xl 24 come from Tailwind's default scale) lg-minus 17px one notch under lg — display titles a shade under 18 2xl-minus 22px one notch under 2xl — wide-screen nowrap headings */ - --text-3xs: 0.625rem; - --text-2xs: 0.6875rem; + --text-3xs: 0.75rem; /* alias of xs, see above */ + --text-2xs: 0.75rem; /* alias of xs, see above */ --text-sm-minus: 0.8125rem; --text-base-minus: 0.9375rem; --text-lg-minus: 1.0625rem; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index ed3dfb1c84..626bf3e34d 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -17,6 +17,7 @@ import { ToastProvider } from "@/components/ui/toast"; import { PUBLIC_APP_ROBOTS_METADATA } from "@/lib/crawler-policy"; import { BRAND_DESCRIPTION, BRAND_NAME } from "@/lib/brand"; import "./globals.css"; +import "./reading-aids.css"; /** * Origin of the Supabase project, or null when the public env is absent (demo mode). @@ -158,7 +159,7 @@ export default async function RootLayout({ // read it), which reads as a hydration mismatch on this attribute. suppressHydrationWarning dangerouslySetInnerHTML={{ - __html: `${THEME_BOOTSTRAP_SCRIPT}${GLASS_FALLBACK_SCRIPT}(function(){try{var p=JSON.parse(localStorage.getItem("clinical-kb-preferences")||"{}");if(p&&typeof p==="object"){if(p.density==="compact"||p.density==="spacious"){document.documentElement.setAttribute("data-density",p.density);}if(p.motion==="reduced"||p.motion==="full"){document.documentElement.setAttribute("data-motion",p.motion);}}}catch(e){/* storage blocked or stored preferences JSON corrupt - the default density/motion apply */}})();`, + __html: `${THEME_BOOTSTRAP_SCRIPT}${GLASS_FALLBACK_SCRIPT}(function(){try{var p=JSON.parse(localStorage.getItem("clinical-kb-preferences")||"{}");if(p&&typeof p==="object"){if(p.density==="compact"||p.density==="spacious"){document.documentElement.setAttribute("data-density",p.density);}if(p.motion==="reduced"||p.motion==="full"){document.documentElement.setAttribute("data-motion",p.motion);}if(p.brightLight===true){document.documentElement.setAttribute("data-bright","");}}}catch(e){/* storage blocked or stored preferences JSON corrupt - the default density/motion apply */}})();`, }} /> = { plain: "", }; -/** The mockup's shapes, drawn on a 10px grid: a diamond, a triangle, a dashed ring, a dot. */ +/** One shape per status (Ward Flow review, 12 Oct 2026), on a 10px grid: triangle for date passed, filled dot for start renewing, dashed ring for not recorded, tick for recorded. */ export function AdminStatusMark({ status }: { readonly status: AdminStatus }) { if (status === "date-passed") { return ( -
- {renewNext ? : null} + {renewNext ? ( + + ) : null} {needsYou ? : null}
diff --git a/src/components/admin/admin-work.module.css b/src/components/admin/admin-work.module.css index f9e5b398b3..725e55d393 100644 --- a/src/components/admin/admin-work.module.css +++ b/src/components/admin/admin-work.module.css @@ -159,6 +159,7 @@ display: inline-flex; align-items: center; gap: calc(var(--spacing) * 1.75); + font-family: var(--font-mono); font-size: var(--text-2xl-minus); font-weight: 600; letter-spacing: -0.02em; diff --git a/src/components/admin/renewals/checklist-summary.tsx b/src/components/admin/renewals/checklist-summary.tsx index 4a11a0ff36..fe1929fcc4 100644 --- a/src/components/admin/renewals/checklist-summary.tsx +++ b/src/components/admin/renewals/checklist-summary.tsx @@ -1,4 +1,4 @@ -import { Diamond, Triangle } from "lucide-react"; +import { Circle, Triangle } from "lucide-react"; import { adminStyles } from "@/components/admin/admin-kit"; import { AdminStatusShape, adminStatusColour } from "@/components/admin/admin-status-tag"; @@ -51,7 +51,7 @@ const percent = (offset: number) => `${((offset / 12) * 100).toFixed(2)}%`; /** * "Next 12 months" (Josh's locked mockup, work-mode redesign 6 Oct 2026): one * row per item due for action, its name and date at the left and a track at - * the right with a mark at its date (a diamond once passed, a triangle while + * the right with a mark at its date (a triangle once passed, a dot while * still ahead) in its status colour, a line at today in the mode colour, and * month initials under the tracks. * @@ -95,7 +95,7 @@ function ChecklistTimeline({
{items.map((row) => { const passed = row.expiresOn < today; - const Mark = passed ? Diamond : Triangle; + const Mark = passed ? Triangle : Circle; return (
@@ -112,8 +112,8 @@ function ChecklistTimeline({ className={adminStyles.tlTodayLine} data-today-line="true" /> - {/* Shape and colour together (M8): a triangle still ahead, a - diamond already passed, the same shapes the list's tags carry. */} + {/* Shape and colour together (M8): a triangle already passed, a + dot still ahead, the same shapes the list's tags carry. */} } + eyebrow={answer ? "Renewals" : item.kind === "new-job" ? "New job starts" : "Renew next"} + title={answer ?? item.title} + sub={{answer ? `Next: ${item.title} · ${dateLine}` : dateLine}} footer={
{hasWindow && item.windowStart && item.windowEnd ? ( diff --git a/src/components/clinical-dashboard/master-search-header.tsx b/src/components/clinical-dashboard/master-search-header.tsx index 84f6fb1a14..20e4d54716 100644 --- a/src/components/clinical-dashboard/master-search-header.tsx +++ b/src/components/clinical-dashboard/master-search-header.tsx @@ -2438,7 +2438,7 @@ export function MasterSearchHeader(props: MasterSearchHeaderProps) {
diff --git a/src/components/clinical-dashboard/settings-dialog.tsx b/src/components/clinical-dashboard/settings-dialog.tsx index 18a1ee4902..9fbbb5449c 100644 --- a/src/components/clinical-dashboard/settings-dialog.tsx +++ b/src/components/clinical-dashboard/settings-dialog.tsx @@ -128,6 +128,7 @@ const APP_PREFERENCE_RESET_KEYS: ReadonlyArray = [ "showRecentOnHome", "showProtocolsOnHome", "compactCitations", + "brightLight", ]; /** Every preference the full reset covers. Excludes the `saveRecentSearches` @@ -1197,6 +1198,18 @@ export function SettingsDialog({ options={APPEARANCE_OPTIONS} /> + { + setPreference("brightLight", next); + if (next) setThemePreference("light"); + }} + /> {/* The label stays "Interface density" — it is the accessible name pinned by the DOM and Playwright suites. What was missing is that this is also the app's text-size control: it @@ -1791,7 +1804,11 @@ function SettingsField({
{children ? ( -
{children}
+ // Below 360px a stacked control borrows most of the row's side padding: at the 12px type + // floor a three-way choice such as "Comprehensive" needs every pixel of its third. +
+ {children} +
) : valueText ? ( {valueText} diff --git a/src/components/clinical-dashboard/settings-sections.ts b/src/components/clinical-dashboard/settings-sections.ts index b585bd0c9b..5a904b8f5f 100644 --- a/src/components/clinical-dashboard/settings-sections.ts +++ b/src/components/clinical-dashboard/settings-sections.ts @@ -114,6 +114,12 @@ export const SETTINGS_SEARCH_ENTRIES: ReadonlyArray = [ label: "Appearance", keywords: "theme dark mode light mode night colour color system contrast", }, + { + id: "settings-row-bright-light", + section: "app-preferences", + label: "Bright light", + keywords: "glare sunlight sun outdoors high contrast bold darker lines readability ward", + }, { id: "settings-row-interface-density", section: "app-preferences", diff --git a/src/components/clinical-dashboard/universal-search-also-matches.tsx b/src/components/clinical-dashboard/universal-search-also-matches.tsx index 371dfc6d30..d5349d51ed 100644 --- a/src/components/clinical-dashboard/universal-search-also-matches.tsx +++ b/src/components/clinical-dashboard/universal-search-also-matches.tsx @@ -183,7 +183,18 @@ export function UniversalSearchAlsoMatches({ : matchCount > 0 ? `${withCatalogueDegradedNotice(`${matchCountLabel(matchCount)} also match this search`, catalogueDegraded)}.` : emptyMessage; - const headerMeta = searchPending ? "Searching…" : matchCount > 0 ? matchCountLabel(matchCount) : "No other matches"; + // Below 360px the 12px type floor leaves no room for "related", so the closed header reads + // "3 modes"; the full count is still announced by the status node above. + const headerMeta = searchPending ? ( + "Searching…" + ) : matchCount > 0 ? ( + <> + {matchCount} related + {matchCount === 1 ? "mode" : "modes"} + + ) : ( + "No other matches" + ); if (!submissionActive) return null; if (!viewportReady || trimmedQuery.length < 2) return null; @@ -225,7 +236,7 @@ export function UniversalSearchAlsoMatches({ // This header used to go inert from sm up because the panel was always // open there, which put a grid of cross-mode suggestions between the // composer and the results the search actually asked for. - "flex min-h-tap w-full items-center gap-2.5 rounded-xl px-2 text-left transition-colors", + "flex min-h-tap w-full items-center gap-1.5 rounded-xl px-2 text-left transition-colors min-[360px]:gap-2.5", "hover:bg-[color:var(--surface)]", focusRing, )} @@ -260,7 +271,10 @@ export function UniversalSearchAlsoMatches({ what lets the count sit at the far edge at every width without a second line, and it replaces the phone's old stacked subtitle plus a tinted count badge that said the same number twice. */} - + {/* Visual cue only — the button's name stays the title, so a screen reader is not read a count that the sr-only status node already announces properly. */} diff --git a/src/components/clinical-dashboard/use-app-preferences.ts b/src/components/clinical-dashboard/use-app-preferences.ts index 13ada096f6..0ff9d1d216 100644 --- a/src/components/clinical-dashboard/use-app-preferences.ts +++ b/src/components/clinical-dashboard/use-app-preferences.ts @@ -179,7 +179,7 @@ function persist(next: AppPreferences) { window.dispatchEvent(new Event(changeEvent)); } -/** Reflects density/motion onto so the choice takes real visual effect. */ +/** Reflects density, motion and Bright light onto so the choice takes real visual effect. */ export function applyPreferenceSideEffects(preferences: AppPreferences) { if (typeof document === "undefined") return; const root = document.documentElement; @@ -196,6 +196,7 @@ export function applyPreferenceSideEffects(preferences: AppPreferences) { } else { root.setAttribute("data-motion", preferences.motion); } + root.toggleAttribute("data-bright", preferences.brightLight); } /** diff --git a/src/components/cme/cpd-feature-kit.tsx b/src/components/cme/cpd-feature-kit.tsx index 1ac1ec783d..f0db2a68bc 100644 --- a/src/components/cme/cpd-feature-kit.tsx +++ b/src/components/cme/cpd-feature-kit.tsx @@ -126,7 +126,7 @@ export function Tag({ tone = "neutral", children }: { readonly tone?: TagTone; r /** "Not checked" honesty marker, in the warning tint. */ export function PendingTag({ children }: { readonly children: ReactNode }) { return ( - +