From 5324d8fc7c6b0e281d2bdb8e4cb943b381a69453 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 3 Jul 2026 18:11:46 +0800 Subject: [PATCH] polish(ui): add --spacing-tap token + standardise ui-primitives recipe layer - globals.css: add --spacing-tap (2.75rem / 44px) -> generates min-h-tap / h-tap / w-tap / min-w-tap / size-tap; a single source of truth for the WCAG tap-target minimum instead of copy-pasted min-h-11 / h-11 magic numbers. - ui-primitives.tsx: adopt the tap tokens across the control/field/nav/chat recipes; unify disabled opacity (55/45 -> 50); replace arbitrary [56/64px] with min-h-14 / min-h-16; rounded-t-[var(--radius-xl)] -> rounded-t-xl. - Height-preserving & behaviour-neutral: min-h-11 -> min-h-tap (both 44px). The min-h-12 (48px) rows that satisfy the ui-smoke sub-pixel tap-target check are deliberately left as-is, NOT downgraded to the token. - docs/redesign/09-page-polish-plan.md: phased plan for the wider polish pass. Phase 1 of the production-page polish program. Verified: Tailwind v4 emits .min-h-tap { min-height: var(--spacing-tap) } = 44px; tsc / eslint / prettier all clean. Co-Authored-By: Claude Opus 4.8 --- docs/redesign/09-page-polish-plan.md | 193 +++++++++++++++++++++++++++ src/app/globals.css | 8 ++ src/components/ui-primitives.tsx | 34 ++--- 3 files changed, 218 insertions(+), 17 deletions(-) create mode 100644 docs/redesign/09-page-polish-plan.md diff --git a/docs/redesign/09-page-polish-plan.md b/docs/redesign/09-page-polish-plan.md new file mode 100644 index 0000000000..4776c292f6 --- /dev/null +++ b/docs/redesign/09-page-polish-plan.md @@ -0,0 +1,193 @@ +# Production-Page Polish & Perfection Plan — Clinical KB (July 2026) + +## Context + +The premium redesign (docs `01`–`06`) established a tiered design-token system, a +shared `ui/` + `ui-primitives` recipe layer, and mobile bottom-sheet patterns. The +token-adoption audit (`07`, score **78/100**) and the design-review prompts (`08`) +then measured _how consistently the app actually consumes those tokens_ and defined +what "polished" means for this product: a clinical knowledge base used by clinicians +under time pressure, where **safety, clarity, speed, and consistency** beat novelty. + +This plan turns that into an execution program to **perfect, upgrade, optimise, and +polish every production page**. It is deliberately _foundation-first_: the app's +pages are composed from a few shared layers, so fixing those layers polishes many +pages at once before we touch pages individually. + +**Scope decisions (confirmed with the user):** + +- **Production pages only** — the 14 real user-facing routes. The 18 `/mockups` + prototype pages are **out of scope**. +- **Polish only — no behaviour change.** Token adoption, accessibility, state + coverage, responsive/mobile, type scale, microcopy. No logic, data-flow, or + routing changes; no structural decomposition of the `ClinicalDashboard` / + `DocumentViewer` monoliths (that stays PAUSED per project memory). + +**Isolation:** all work happens in the dedicated worktree +`C:\Dev\Apps\_wt-page-polish` on branch **`claude/page-polish`** (branched off the +token-infra HEAD `46b433b7a`, which is only on `feature/tools-page-mockups`, not +`main`). The main checkout is a shared workspace with ~17 concurrently-modified +files — we never edit there. Commits stay path-scoped; rebase before any PR. + +## Surfaces in scope (14 production routes) + +| Route | Top component | Shared layer it rides on | +| -------------------------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------- | +| `/` | `ClinicalDashboard.tsx` (~9.6k LOC) | `global-search-shell`, `master-search-header`, `ClinicalSidebar`, `ui-primitives` | +| `/applications` | `applications-launcher-page.tsx` (1,491) | `global-search-shell`, `ui-primitives` | +| `/documents/[id]` | `DocumentViewer.tsx` (~3.6k) | `ui-primitives` | +| `/differentials` | `differentials-home-page.tsx` (15) | **`mode-home-template`** | +| `/differentials/diagnoses` | `differential-stream-page.tsx` (111) | shell + `ui-primitives` | +| `/differentials/diagnoses/[slug]` | `differential-detail-page.tsx` (525) | `ui-primitives` | +| `/differentials/presentations` | `differential-presentation-workflow-page.tsx` (635) | `ui-primitives` | +| `/services` | `services-home-page.tsx` (104) | **`mode-home-template`** | +| `/services/[slug]` | `service-detail-page.tsx` (780) | `ui-primitives` | +| `/forms` | `forms-home-page.tsx` (90) | **`mode-home-template`** | +| `/forms/[slug]` | `form-detail-page.tsx` (802) | `ui-primitives` | +| `/favourites` | `favourites-home-page.tsx` (33) | **`mode-home-template`** | +| `/medications` → `/medications/[slug]` | `medication-prescribing-workspace.tsx` | shell + `ui-primitives` | + +**Leverage layers (fix once → many pages improve):** +`src/components/ui-primitives.tsx` (recipe strings, every page) · +`src/components/mode-home-template.tsx` (services/forms/differentials/favourites +homes) · `src/components/clinical-dashboard/{global-search-shell, +master-search-header,ClinicalSidebar}.tsx` (chrome around every mode page) · +`src/app/globals.css` (tokens). + +## What "polished" means here — workstreams + +Derived from the `08` PR-pass checklist and the `07` audit debts: + +- **W1 — Token discipline.** Color/space/type/radius/elevation come from tokens, no + hardcoded one-offs. Audit debts: **M3** (~295 `text-[Npx]` → scale tokens), **L4** + (`bg-white`→`--surface`; `text-white`/`ring-white`→`*-contrast`), **L5** + (`border-blue-400` + arbitrary accent shadow → `--clinical-accent`/`--glow-*`), + **L6** (tap-target magic numbers → token), **L7** (`rounded-[var(--radius-lg)]` → + `rounded-lg`). Plus inline `style={{minHeight,minWidth}}` → utility classes. +- **W2 — State coverage.** Every interactive element has distinct default / hover / + focus / active / disabled / selected / loading / empty / error states; system + status always visible. Reuse `LoadingPanel` / `EmptyState` primitives. +- **W3 — Accessibility.** Touch targets ≥44px with spacing; keyboard-operable with + visible `focus-visible` ring; labeled icon-only controls; `prefers-reduced-motion` + and `forced-colors` respected; AA contrast in light **and** dark; 200% zoom / reflow. +- **W4 — Responsive & mobile.** Holds at mobile/tablet/desktop; mobile bottom-sheet, + safe-area, thumb-reach correct; long-text truncation/wrap/overflow; mobile-first + defaults (avoid desktop-first `hidden sm:block` where it strands mobile content). +- **W5 — Content & clinical safety.** Microcopy specific and action-oriented; + error/empty copy actionable; units explicit (mg vs mcg); no dangerous decimals + ("1.0", leading zero for "<1"); source/provenance and uncertainty legible; no + emphasis that could bias a clinical decision. +- **W6 — Consistency.** Reuse shared `ui/` components and recipes instead of + re-inventing; match patterns across pages; honour the unlayered-composer-chrome + gotcha (a call-site utility must not silently lose to a layered/unlayered class). + +## Phased execution + +### Phase 1 — Shared recipe foundation _(this session)_ + +Highest leverage: fixes the recipe layer that every page consumes. + +- `globals.css`: add a **`--spacing-tap: 2.75rem`** (44px) named token → generates + `min-h-tap` / `h-tap` / `w-tap` / `min-w-tap` / `size-tap` utilities (audit **L6**). +- `ui-primitives.tsx`: + - Standardise disabled opacity — `disabled:opacity-55` (`controlBase`) and + `disabled:opacity-45` (`toolbarButton`) → **`disabled:opacity-50`** (the majority + value already on `floatingControl`/`chatSendButton`). + - Adopt tap tokens: `min-h-[44px]`→`min-h-tap`, `h-[44px] w-[44px]`→`h-tap w-tap`, + `min-h-11`/`min-w-11`→`min-h-tap`/`min-w-tap` in the control/field/nav/chat + recipes (semantic WCAG intent, single source of truth). + - Verbose radius → utility: `rounded-t-[var(--radius-xl)]`→`rounded-t-xl` + (`sheetSurface`, audit **L7**). +- **Verify it compiles** (Tailwind v4 named-spacing utility generation) with a + worktree typecheck + build before moving on. + +### Phase 2 — Shared shells & home template + +Cascades to the four mode-home pages and the chrome on every mode page. + +- `mode-home-template.tsx`: PR-pass polish (states, tap targets via new tokens, + type scale, focus rings, reduced-motion, responsive) — improves + services/forms/differentials/favourites homes together. +- `global-search-shell.tsx`, `master-search-header.tsx`, `ClinicalSidebar.tsx`: + same pass on the persistent chrome. + +### Phase 3 — Type-scale token adoption (audit M3) + +Adopt the size-only tokens across production files, per-site (not a blind codemod): +`text-[11px]`→`text-2xs`, `text-[13px]`→`text-sm-minus`, `text-[15px]`→ +`text-base-minus`, `text-[10px]`→`text-3xs`, `text-[8px]`→`text-4xs`; one-offs +`text-[17/18/22px]`→`text-lg`/`text-xl`. `text-[9px]` has no exact token → per-site +decision (snaps to `text-4xs`). Batch by page/area; visual-check each batch. + +### Phase 4 — Per-page PR-pass polish + +Run the `08` PR-pass checklist against each production surface and fix findings. +Order (small/independent → large/hot, to bank wins and de-risk): + +1. `favourites-home`, `services-home`, `forms-home`, `differentials-home` (mostly + covered by Phase 2 — confirm). +2. `differential-stream`, `differential-detail`, `differential-presentation-workflow`. +3. `service-detail`, `form-detail`, `medication-prescribing-workspace`. +4. `applications-launcher-page` (button heights, disabled/loading states, mobile). +5. `ClinicalDashboard` (`/`) and `DocumentViewer` — largest, hottest; surgical + token/a11y/state polish only, no restructuring. + +### Phase 5 — Accessibility + resilience sweep & verification + +- Cross-cutting sweep: `motion-reduce:` fallbacks on remaining animated elements; + `focus-visible` pair on every interactive element; `forced-colors` spot-check; + 0/1/many + very-long-text resilience; loading/empty/error on async surfaces. +- Full verification (see below) in light, dark, reduced-motion, forced-colors, and + at 200% zoom. Then commit path-scoped, rebase, and hand off. + +## Critical files + +- **Foundation:** `src/app/globals.css`, `src/components/ui-primitives.tsx`, + `src/components/ui/sheet.tsx`. +- **Shared shells/template:** `src/components/mode-home-template.tsx`, + `src/components/clinical-dashboard/global-search-shell.tsx`, + `src/components/clinical-dashboard/master-search-header.tsx`, + `src/components/clinical-dashboard/ClinicalSidebar.tsx`. +- **Representative pages:** `src/components/applications-launcher-page.tsx`, + `src/components/services/service-detail-page.tsx`, + `src/components/forms/form-detail-page.tsx`, + `src/components/differentials/differential-detail-page.tsx`, + `src/components/ClinicalDashboard.tsx`, `src/components/DocumentViewer.tsx`. + +Reuse existing utilities — do **not** re-invent: `cn()`, `LoadingPanel`, +`EmptyState`, `PanelHeading`, the tone recipes (`toneSuccess`/`toneDanger`/…), and +the control/field/chat recipes in `ui-primitives.tsx`; `Sheet` in `ui/sheet.tsx`; +`ModeHomeTemplate` for home pages. + +## Verification + +- **Per phase / cheap gate:** `npx tsc --noEmit`, `npm run lint`, `npm run +format:check` (a required CI check — see project memory), then `npm run +verify:cheap`. +- **UI gate:** `npm run verify:ui` (Chromium Playwright) for any change touching + rendering/routing/styling. +- **Visual spot-checks:** for each polished page, verify in **light + dark + + `prefers-reduced-motion` + `forced-colors`** and at **200% zoom**. Preview via a + dev server _in this worktree_ (needs its own `npm ci` — done). Do **not** run + `npm run build` in the main checkout while its `:4298` dev server is live (it + corrupts `.next` and 500s the live preview — project memory). +- **Release confidence (end):** `npm run verify:release` before hand-off. + +## Risks & guardrails + +- **Concurrent edits:** `ui-primitives.tsx`, `ClinicalDashboard.tsx`, and the chrome + files are being polished in the main checkout too. Keep commits path-scoped, expect + trivial merge overlaps (same intent), and rebase before PR. +- **Layered vs unlayered CSS:** header chrome is `@layer components` (utilities win); + composer chrome (`answer-footer-search-*`, `*-composer-edge`) is intentionally + **unlayered** (the class wins). Check a class body before adding a utility to an + element that carries one — see `globals.css` audit notes + `capture-chrome-parity.ts`. +- **Type-scale swaps are size-only now** (`--text-2xs` no longer bakes tracking), so + exact-match `text-[Npx]`→token is a pure swap — but still visual-check `text-[9px]` + and any element whose leading/tracking was implicitly relying on an arbitrary size. +- **No behaviour change:** if a "polish" edit would alter logic, data, or routing, + stop and defer it out of this program. + +## Status + +- **Phase 1: in progress this session.** Remaining phases queued above for review. diff --git a/src/app/globals.css b/src/app/globals.css index 043ff29eae..09512995c8 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -21,6 +21,14 @@ --radius-xl: 0.75rem; --radius-2xl: 1rem; + /* Minimum interactive target (WCAG 2.5.5 / 2.5.8). Named spacing token so the + 44px tap-target minimum is a single source of truth instead of copy-pasted + `min-h-11` / `h-[44px]` magic numbers. Generates min-h-tap, h-tap, w-tap, + min-w-tap, size-tap. Semantic intent: "this is a touch target", not "44px". + NB: controls that flake the ui-smoke sub-pixel tap-target check when scrolled + deep in sheets stay on min-h-12 (48px) — do not downgrade those to the token. */ + --spacing-tap: 2.75rem; + /* Type scale — sub-`xs` micro steps plus the 13–22px "between" steps the dense clinical UI reaches for. These are intentionally SIZE-ONLY: unlike Tailwind's default text-* tokens they carry no `--line-height` / diff --git a/src/components/ui-primitives.tsx b/src/components/ui-primitives.tsx index 1ab622cb06..a6f448f38b 100644 --- a/src/components/ui-primitives.tsx +++ b/src/components/ui-primitives.tsx @@ -27,24 +27,24 @@ export const panel = "rounded-lg border border-[color:var(--border-lux)] bg-[color:var(--surface-lux)] shadow-[var(--shadow-soft)] ring-1 ring-[color:var(--border-strong)]/20 dark:ring-[color:var(--border-strong)]/10"; export const panelSubtle = quietPanel; export const controlBase = - "inline-flex min-h-11 items-center justify-center gap-2 rounded-lg text-sm font-semibold transition active:translate-y-px focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-55 disabled:hover:shadow-none"; + "inline-flex min-h-tap items-center justify-center gap-2 rounded-lg text-sm font-semibold transition active:translate-y-px focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:shadow-none"; export const primaryControl = `${controlBase} bg-[color:var(--command)] px-5 text-[color:var(--command-contrast)] shadow-[var(--shadow-tight)] hover:bg-[color:var(--command-hover)] hover:shadow-[var(--shadow-hover)]`; export const floatingControl = - "inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[color:var(--border-lux)] bg-[color:var(--surface-raised)] px-3 text-sm font-semibold text-[color:var(--text)] shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:bg-[color:var(--surface-subtle)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:shadow-none"; + "inline-flex min-h-tap items-center justify-center gap-2 rounded-lg border border-[color:var(--border-lux)] bg-[color:var(--surface-raised)] px-3 text-sm font-semibold text-[color:var(--text)] shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:bg-[color:var(--surface-subtle)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:shadow-none"; export const toolbarButton = - "grid h-11 w-11 shrink-0 place-items-center rounded-lg border border-[color:var(--border-lux)] bg-[color:var(--surface-raised)] text-[color:var(--text)] shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:bg-[color:var(--surface-subtle)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-45 disabled:hover:shadow-none"; + "grid h-tap w-tap shrink-0 place-items-center rounded-lg border border-[color:var(--border-lux)] bg-[color:var(--surface-raised)] text-[color:var(--text)] shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:bg-[color:var(--surface-subtle)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:shadow-none"; export const eyebrowText = "text-2xs font-semibold uppercase leading-4 tracking-[0.06em] text-[color:var(--text-soft)]"; export const fieldLabel = `mb-1.5 block ${eyebrowText}`; export const fieldControl = - "h-11 w-full rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-raised)] text-sm text-[color:var(--text)] shadow-[var(--shadow-inset)] outline-none transition placeholder:text-[color:var(--text-soft)] focus:border-[color:var(--focus)] focus:ring-4 focus:ring-[color:var(--focus)]/25"; + "h-tap w-full rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-raised)] text-sm text-[color:var(--text)] shadow-[var(--shadow-inset)] outline-none transition placeholder:text-[color:var(--text-soft)] focus:border-[color:var(--focus)] focus:ring-4 focus:ring-[color:var(--focus)]/25"; export const fieldControlWithIcon = `${fieldControl} pl-9 pr-3`; export const fieldControlPlain = `${fieldControl} px-3`; export const fieldIcon = "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[color:var(--text-soft)]"; export const shellChip = - "inline-flex min-h-11 items-center gap-2 rounded-lg border px-3 text-xs font-semibold shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)]"; + "inline-flex min-h-tap items-center gap-2 rounded-lg border px-3 text-xs font-semibold shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)]"; export const navPill = - "inline-flex min-h-11 items-center justify-center gap-2 rounded-lg border border-[color:var(--border-lux)] bg-[color:var(--surface-raised)] px-3 text-xs font-semibold text-[color:var(--text-muted)] shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:text-[color:var(--text)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50"; + "inline-flex min-h-tap items-center justify-center gap-2 rounded-lg border border-[color:var(--border-lux)] bg-[color:var(--surface-raised)] px-3 text-xs font-semibold text-[color:var(--text-muted)] shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:text-[color:var(--text)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50"; export const metadataPill = "inline-flex min-h-7 items-center rounded-md border border-[color:var(--border-lux)] bg-[color:var(--surface-raised)] px-2 text-xs font-semibold text-[color:var(--text-muted)] shadow-[var(--shadow-inset)]"; export const subtleStatusPill = @@ -57,7 +57,7 @@ export const iconTilePremium = export const compactMetadataRow = "mt-2 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs font-semibold tabular-nums text-[color:var(--text-muted)]"; export const sheetSurface = - "rounded-t-[var(--radius-xl)] border border-[color:var(--border-lux)] bg-[color:var(--surface-lux)] shadow-[var(--shadow-lux)] ring-1 ring-[color:var(--border-strong)]/20 backdrop-blur-xl dark:ring-[color:var(--border-strong)]/10 sm:rounded-lg"; + "rounded-t-xl border border-[color:var(--border-lux)] bg-[color:var(--surface-lux)] shadow-[var(--shadow-lux)] ring-1 ring-[color:var(--border-strong)]/20 backdrop-blur-xl dark:ring-[color:var(--border-strong)]/10 sm:rounded-lg"; export const sheetHandle = "mx-auto block h-1 w-10 rounded-full bg-[color:var(--border-strong)]/70 sm:hidden"; // Comfortable reading measure for long-form prose (answers, source passages, document text). export const proseMeasure = "max-w-[68ch]"; @@ -70,33 +70,33 @@ export const commandInput = export const chatAnswerText = "max-w-[68ch] text-base-minus font-medium leading-[1.56] text-[color:var(--text-heading)] sm:text-base sm:leading-[1.62]"; export const chatActionRow = - "flex min-h-11 flex-wrap items-center gap-1.5 text-xs font-semibold text-[color:var(--text-heading)] sm:min-h-8"; + "flex min-h-tap flex-wrap items-center gap-1.5 text-xs font-semibold text-[color:var(--text-heading)] sm:min-h-8"; export const chatMicroAction = - "inline-flex min-h-11 min-w-11 items-center justify-center gap-1.5 rounded-md px-2 text-xs font-semibold text-[color:var(--text-muted)] transition hover:bg-[color:var(--clinical-accent-soft)] hover:text-[color:var(--clinical-accent)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50"; + "inline-flex min-h-tap min-w-tap items-center justify-center gap-1.5 rounded-md px-2 text-xs font-semibold text-[color:var(--text-muted)] transition hover:bg-[color:var(--clinical-accent-soft)] hover:text-[color:var(--clinical-accent)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50"; export const sourceCapsule = - "source-capsule-hover focus-ring-premium inline-flex min-h-11 items-center gap-1.5 rounded-full border border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] px-3 text-xs font-semibold text-[color:var(--clinical-accent)] transition hover:border-[color:var(--clinical-accent)]"; + "source-capsule-hover focus-ring-premium inline-flex min-h-tap items-center gap-1.5 rounded-full border border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] px-3 text-xs font-semibold text-[color:var(--clinical-accent)] transition hover:border-[color:var(--clinical-accent)]"; export const evidenceRow = "flex min-h-12 w-full items-center justify-between gap-3 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface)] px-3 py-2 text-left shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:bg-[color:var(--surface-subtle)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]"; export const clinicalNotesRow = "flex min-h-12 w-full items-center justify-between gap-3 rounded-lg border border-[color:var(--clinical-chat-sand-border)] bg-[color:var(--clinical-chat-sand)] px-3 py-2 text-left shadow-[var(--shadow-inset)] transition hover:border-[color:var(--clinical-chat-sand-border-strong)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]"; export const chatComposerShell = - "flex min-h-[56px] items-center gap-2 rounded-full border border-[color:var(--border-strong)] bg-[color:var(--surface)] px-2 shadow-[0_1px_2px_rgb(16_24_40_/_5%),0_8px_22px_rgb(16_24_40_/_8%)] focus-within:border-[color:var(--clinical-accent)]"; + "flex min-h-14 items-center gap-2 rounded-full border border-[color:var(--border-strong)] bg-[color:var(--surface)] px-2 shadow-[0_1px_2px_rgb(16_24_40_/_5%),0_8px_22px_rgb(16_24_40_/_8%)] focus-within:border-[color:var(--clinical-accent)]"; export const chatComposerInput = - "min-h-11 min-w-0 flex-1 bg-transparent px-2 text-base font-medium text-[color:var(--text)] outline-none placeholder:text-[color:var(--text-soft)]"; + "min-h-tap min-w-0 flex-1 bg-transparent px-2 text-base font-medium text-[color:var(--text)] outline-none placeholder:text-[color:var(--text-soft)]"; export const chatComposerIconButton = - "grid h-11 w-11 shrink-0 place-items-center rounded-full text-[color:var(--text-muted)] transition hover:bg-[color:var(--surface-subtle)] hover:text-[color:var(--text)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50"; + "grid h-tap w-tap shrink-0 place-items-center rounded-full text-[color:var(--text-muted)] transition hover:bg-[color:var(--surface-subtle)] hover:text-[color:var(--text)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50"; export const chatSendButton = - "grid h-11 w-11 shrink-0 place-items-center rounded-full bg-[color:var(--clinical-accent)] text-[color:var(--clinical-accent-contrast)] shadow-[var(--shadow-tight)] transition hover:bg-[color:var(--clinical-accent-hover)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50"; + "grid h-tap w-tap shrink-0 place-items-center rounded-full bg-[color:var(--clinical-accent)] text-[color:var(--clinical-accent-contrast)] shadow-[var(--shadow-tight)] transition hover:bg-[color:var(--clinical-accent-hover)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50"; export const tableCard = "overflow-hidden rounded-lg border border-[color:var(--border)]/80 bg-[color:var(--surface)] shadow-[0_6px_16px_rgb(15_27_45_/_4%)]"; export const tableCardHeader = "border-b border-[color:var(--border)] bg-[color:var(--clinical-chat-table-header)] px-3 py-2.5 text-sm font-semibold text-[color:var(--text-heading)]"; export const tableMicroActionRow = - "flex min-h-11 flex-wrap items-center gap-1 border-t border-[color:var(--border)] px-2 py-1.5 text-xs font-semibold text-[color:var(--text-muted)] sm:min-h-9"; + "flex min-h-tap flex-wrap items-center gap-1 border-t border-[color:var(--border)] px-2 py-1.5 text-xs font-semibold text-[color:var(--text-muted)] sm:min-h-9"; export const sidebarItem = - "flex min-h-11 min-w-0 w-full items-center gap-2 overflow-hidden rounded-lg px-2.5 text-sm font-semibold text-[color:var(--text-muted)] transition hover:bg-[color:var(--surface-subtle)] hover:text-[color:var(--text)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50"; + "flex min-h-tap min-w-0 w-full items-center gap-2 overflow-hidden rounded-lg px-2.5 text-sm font-semibold text-[color:var(--text-muted)] transition hover:bg-[color:var(--surface-subtle)] hover:text-[color:var(--text)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)] disabled:cursor-not-allowed disabled:opacity-50"; export const sidebarToolTile = - "grid min-h-[64px] place-items-center gap-1 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface)] px-2 py-2 text-center text-xs font-semibold text-[color:var(--text)] shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:bg-[color:var(--surface-subtle)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]"; + "grid min-h-16 place-items-center gap-1 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface)] px-2 py-2 text-center text-xs font-semibold text-[color:var(--text)] shadow-[var(--shadow-inset)] transition hover:border-[color:var(--border-strong)] hover:bg-[color:var(--surface-subtle)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]"; export const statusDotBase = "inline-block h-2 w-2 shrink-0 rounded-full"; export const statusDotReady = `${statusDotBase} bg-[color:var(--success)]`; export const statusDotReview = `${statusDotBase} bg-[color:var(--warning)]`;