Component sheet
Every token and component Orchestrator uses, in every state it needs. The base is the shared Medical library (assets/orc-ui/, vendored without changes); the Orchestrator layer (assets/orc/) adds chat blocks with the orc- prefix and the shared login-* sign-in pattern. Chat specimens sit inside an .orc-shell and sign-in specimens inside a .login-page (without the viewport frame), so they show the product focus ring, field border and unavailable look as in the app. All data on this page is synthetic.
<link rel="stylesheet" href="assets/orc-ui/components.css"> <!-- baseline: tokens, base, components layers -->
<link rel="stylesheet" href="assets/orc/tokens.css"> <!-- product tokens (var() aliases, no new hues) -->
<link rel="stylesheet" href="assets/orc/app.css"> <!-- every page: shell, chat blocks, utilities -->
<link rel="stylesheet" href="assets/orc/login.css"> <!-- sign-in screen only -->
Foundations · assets/orc-ui/tokens.css + assets/orc/tokens.css
Tokens
Colours come only from the baseline. Orchestrator adds layout, type, motion and z-index tokens (the baseline has none) and names its surfaces through --orc-* aliases such as --orc-message-user-bg: var(--color-surface-selected). Contrast is WCAG 2.x, computed against the background named in the note.
Colour — baseline variables Orchestrator uses
--color-page-bg#f4f7fa
Shell and rail background
--color-surface#ffffff
Header, thread, cards, composer
--color-surface-muted#f3f5f8
Chip, suggestion and unavailable button background
--color-surface-selected#eff6ff
User message bubble
--color-text-primary#172033
Text, titles · 16.27:1 on surface
--color-text-secondary#506486
Meta, hints, references, placeholders · 5.99:1 surface, 5.57:1 page
--color-text-tertiary#7e8ea9
3.32:1 — field border and touch grip only, never text
--color-text-placeholder#92a4c3
2.52:1 — baseline gap, replaced in the product scope
--color-border#d7dfea
Dividers, rail edge, composer hairline · decorative
--color-border-strong#c8ced8
Unavailable button border · 1.58:1 as the baseline input border
--color-primary-50#eff6ff
Info notice, hovers, assistant tile
--color-primary-100#dbeafe
User bubble border, avatar
--color-primary-200#bfdbfe
Suggestion hover, pending bubble, resizer hover line
--color-primary-500#3b82f6
Typing dots · 3.68:1 non-text
--color-primary-600#2563eb
Primary button, selected bar, resizer drag line · 5.17:1
--color-primary-700#1d4ed8
Brand tile, focus ring, assistant mark · 6.70:1
--color-primary-800#1e40af
Info notice text · 8.01:1 on primary-50
--color-danger-50#fef2f2
Error notice, delete confirm
--color-danger-100#fee2e2
Error notice and confirm border
--color-danger-500#ef4444
Failed bubble, invalid field · 3.76:1
--color-danger-600#dc2626
Destructive button, field error · 4.83:1
--color-danger-700#b91c1c
Failed turn, counter over limit · 6.47:1
--color-warning-50#fff9eb
Demo chip, warning notice
--color-warning-300#f9cf6c
Demo chip and notice border
--color-warning-700#894c06
Demo chip text, counter near limit · 6.45:1
Colour — product roles for focus, fields and states
--orc-field-border= text-tertiary · #7e8ea9
Composer and sign-in field edge · 3.32:1
--orc-field-placeholder= text-secondary · #506486
Placeholder in the product scope · 5.99:1
--orc-disabled-bg= surface-muted · #f3f5f8
Unavailable filled button and suggestion
--orc-disabled-border= border-strong · #c8ced8
Its edge
--orc-disabled-fg= text-secondary · #506486
Its text, ghost included · 5.48:1 on muted
--orc-assistant-tile-bg= primary-50 · #eff6ff
Assistant and typing tile
--orc-assistant-tile-fg= primary-700 · #1d4ed8
Mark on that tile · 6.16:1
--orc-reference-fg= text-secondary · #506486
“Reference: <id>” in chat and sign-in
--orc-resizer-hover= primary-200 · #bfdbfe
Resizer line on hover · a hint only
--orc-resizer-active= primary-600 · #2563eb
Resizer line while dragged or focused · 4.81:1
--orc-resizer-grip= text-secondary · #506486
Three-dot grip on hover · 5.57:1
--orc-resizer-grip-touch= text-tertiary · #7e8ea9
Pill grip on touch · 3.09:1
--orc-composer-divider= border · #d7dfea
Composer hairline while the thread is not at its end
--orc-brand-tile-* is for the header and sign-in logos only; answer and typing tiles use the quiet --orc-assistant-tile-*.
Contrast of every text pair in use
| Where | Foreground | Background | Ratio |
|---|---|---|---|
| Messages, titles, conversation titles | --color-text-primary | surface · page-bg · surface-selected | 16.27 · 15.13 · 14.95 |
| Time, meta, hints, references, typing text, rail footer, disclaimer, login notes, placeholders | --color-text-secondary | surface · page-bg · surface-muted · surface-selected | 5.99 · 5.57 · 5.48 · 5.50 |
| Unavailable buttons and suggestions | --orc-disabled-fg | surface-muted | 5.48 |
| Ghost buttons (Change email, Resend code) | --color-primary-600 | surface · primary-50 (hover) | 5.17 · 4.75 |
| Module label, suggestion hover, assistant tile mark, filter chip | --color-primary-700 | surface · primary-50 | 6.70 · 6.16 |
| Avatar initials | --color-primary-700 | primary-100 | 5.49 |
| Info notice title · message (82% mix) | --color-primary-800 | primary-50 | 8.01 · 5.23 |
| Error notice title · message (82% mix), delete confirm | --color-danger-700 | danger-50 | 5.91 · 4.51 |
| Warning notice title, Demo chip | --color-warning-700 | warning-50 | 6.45 |
| Warning notice message (82% mix) — Orchestrator puts warning copy in the title instead | --color-warning-700 82% | warning-50 | 4.35 |
| Field error, composer over-limit error | --color-danger-600 | surface | 4.83 |
| Failed turn line, counter over limit | --color-danger-700 | surface | 6.47 |
| Counter near limit | --color-warning-700 | surface | 6.77 |
| Primary button · brand tile · destructive button | --color-white | primary-600 · primary-700 · danger-600 | 5.17 · 6.70 · 4.83 |
| Toast title · message | --color-white · 78% white | text-primary | 16.27 · 10.32 |
| Non-text: selected bar, field focus border | --color-primary-600 | surface | 5.17 |
| Non-text: failed bubble border · typing dots | --color-danger-500 · --color-primary-500 | surface | 3.76 · 3.68 |
| Non-text: product focus ring (after a surface gap) | --orc-focus-ring | surface · page-bg · surface-muted | 6.70 · 6.23 · 6.14 |
| Non-text: composer and sign-in field border | --orc-field-border | surface | 3.32 |
| Non-text: resizer line while dragged or focused · touch grip | --orc-resizer-active · --orc-resizer-grip-touch | page-bg | 4.81 · 3.09 |
| Non-text baseline gaps, outside the product scope only | input border · focus ring halo | surface | 1.58 · 1.29 |
Type scale — --orc-text-*
Orchestrator
What could make your working day easier?
Chats
Check dose settings against the local reference levels before the protocol is approved.
Ask about worklists, reporting, scheduling or anything else in your clinical workflow.
One-time code
Deleted when you sign out
Clinical assistant
Spacing — --space-N = N × 0.25rem
--space-10.25rem--space-20.5rem--space-30.75rem--space-41rem--space-51.25rem--space-61.5rem--space-82rem--space-102.5rem--space-123remRadii, shadows, focus
--radius-sm0.375rem · rows, chips
--radius-md0.5rem · tiles, notices
--radius-lg0.75rem · cards, bubbles, composer
--shadow-smheader, selected row
--shadow-mdlogin card, drawer
--orc-focus-ringevery control in the product scope
--orc-focus-ring-insetthread scroller, rail rows
Layout, motion and layers
| Layout token | Value |
|---|---|
--orc-header-height | 5.25rem (mirrors .app-header; 4.5rem below 40rem) |
--orc-rail-width | 20rem default (two columns from 64rem); set inline on the shell when resized |
--orc-rail-width-min · --orc-rail-width-max | 16rem · 32rem; the conversation keeps at least 40rem |
--orc-drawer-width | 22.5rem (below 64rem; full width below 40rem) |
--orc-thread-measure | 46rem reading width of the thread |
--orc-composer-max | = thread measure |
--orc-composer-max-lines | 12 lines before the field scrolls |
--orc-bubble-max | 36rem (and at most 88% of the thread) |
--orc-gutter | 1.5rem; 1rem below 40rem |
--orc-login-width | 27rem sign-in panel |
--orc-touch-target | 2.75rem under any-pointer: coarse and in shells at or below 40rem |
| Motion / layer token | Value |
|---|---|
--orc-duration-instant | 80ms |
--orc-duration-fast | 120ms — hovers, rows |
--orc-duration-intent | 150ms — delay before the resizer shows its line and grip |
--orc-duration-base | 200ms — fields, login step, rail collapse |
--orc-duration-slow | 240ms — drawer |
--orc-duration-loop | 1200ms — typing, skeleton |
--orc-ease-out · --orc-ease-standard · --orc-ease-emphasized | ease-out · ease · cubic-bezier(0.2, 0, 0, 1) |
--orc-z-raised · header · backdrop · drawer · toast | 1 · 50 · 60 · 70 · 80 |
Under prefers-reduced-motion: reduce every transition and animation in assets/orc/ is off; the typing dots are hidden, skeletons stop pulsing and the rail collapses at once. | |
assets/orc-ui/icons.svg + assets/orc/icons.svg
.icon sprites
Same drawing spec as the baseline: 24 × 24 viewBox, stroke only, .icon sets stroke-width 1.8, round caps and joins and currentColor. The Orchestrator sprite adds only what the baseline lacks; message and sparkle appear on this sheet only. refresh, plus, close, lock, check (copied), alert, error, chevron-right and chevron-down (“New answer”) come from the baseline.
Orchestrator sprite · assets/orc/icons.svg
Baseline sprite used by Orchestrator · assets/orc-ui/icons.svg
Medical baseline · assets/orc-ui (unchanged)
Baseline components Orchestrator uses
Used as they are. Orchestrator composes them and tunes them only through their own custom properties (--button-height, --input-font-size, and in its scope --focus-ring, --input-border and --input-placeholder); unavailable buttons take the product look. Focus states are simulated on this static page.
.app-header with the Orchestrator brand, Demo chip, user and Sign out
.button variants
.button states
Busy = .button__spinner + aria-busy="true" + aria-disabled="true"; it keeps the baseline fade and the label changes to the busy copy. Unavailable (without aria-busy) takes the product look: see Focus and unavailable.
.chip and .avatar
.orc-message__avatar)
.form-field + .input
Enter a valid email address.
.inline-notice info · warning · error · compact
Your session ended. Sign in again to continue.
You're offline. Messages won't send until your connection is back.
We couldn't send the code. Try again in a moment.
Reference: 7f3a9c21You've reached 20 chats. Delete one to start another.
Warning copy goes in __title: the baseline __message mixes the colour to 82% and drops warning text to 4.35:1.
.toast
Signed out
The toast lives in .toast-region (fixed, bottom end) after sign-out. The baseline .empty-state is not used: its dashed border reads as a drop zone.
Orchestrator layer · assets/orc/app.css
.orc-shell
The signed-in screen: baseline .app-header, then the chat list (rail) beside the conversation. The shell fills the viewport (100dvh); only the list and the thread scroll. The shell is a size container, so its breakpoints follow its own width: from 64rem two columns, with a resizer (__resizer) on the rail's edge and “Hide chats” in the rail header; below 64rem the rail becomes a drawer opened by the header “Chats” button, with a backdrop; at or below 40rem the drawer is full width and header actions become icon-only. The frames below set --orc-shell-height: 100% to embed it.
Two columns (≥ 64rem) · rail at a stored width of 24rem, resizer on its edge · .orc-shell, __resizer
Chats
On a shared computer? Sign out when you're done.
Synthetic demo only — do not send real PHI.
CT protocol checklist
Synthetic demo only — do not send real PHI.
The rail is clamp(16rem, var(--orc-rail-width), min(32rem, 100cqi − 40rem)) wide; the thread keeps its 46rem measure. Hover, focus or tab to the rail's edge to see the resizer; dragging needs the prototype's script. Tab order: rail, resizer, thread, composer.
Two columns, list hidden · .orc-shell--rail-collapsed
Chats
On a shared computer? Sign out when you're done.
Synthetic demo only — do not send real PHI.
CT protocol checklist
Synthetic demo only — do not send real PHI.
The rail column closes to 0; the rail is inert and hidden, and so is the resizer. “Chats” (aria-expanded="false") appears in the header and expands the rail in place to its last width; the title strip names the open chat. “Hide chats”, Enter on the resizer, or a drag 4rem past the narrowest width collapse it; focus moves to “Chats”, and back to the selected row on expand. Below 64rem the modifier has no effect.
Resizer states (simulated, drawn with the resizer tokens) · rail on the left, conversation on the right
--orc-duration-intent — 2px --orc-resizer-hover, three-dot grip, col-resize--orc-resizer-active; .orc-shell--resizing on the shell--orc-focus-ring around the griprole="separator", aria-orientation="vertical", aria-controls the rail, “Resize chat list”, values in CSS px: “Chat list width: 320 px, default”, “…, narrowest” at 16rem, “…, widest” at the maximum. ←/→ 1rem, Shift 4rem, Home/End narrowest/widest, Enter collapses; a click without movement switches Default 20rem ↔ Wide 28rem; Esc during a drag restores the width. Forced colours: CanvasText at rest, Highlight on hover, focus and drag.
Below 64rem, drawer closed · new empty chat (48rem frame)
Below 64rem, drawer open · .orc-shell--drawer-open
Open drawer: role="dialog", aria-modal="true", focus moves to the close button and is kept inside; Escape, the backdrop and “Close chats” close it and return focus to “Chats”. Closed, it is visibility: hidden, so nothing inside can take focus. Below 64rem there is no resizer and no “Hide chats”.
Below 40rem, drawer open (full width, 24.375rem frame)
The phone layout of the closed shell (header shows only icon, Demo chip and icon actions) depends on the baseline header's viewport media query, so it is shown in screens.html at 390 px, and here when this page itself is narrower than 40rem.
Orchestrator layer · assets/orc/app.css
.orc-rail
The chat list: header (title, “Deleted when you sign out”, then “Hide chats” from 64rem or “Close chats” in the drawer), “New chat”, a persistent notice slot (__notice), the scrolling list and the shared-computer hint. Up to 20 chats; at the limit “New chat” is disabled and the warning notice explains why. With no chats the list slot holds one meta line (__empty), not a card.
Loading · .orc-skeleton in the list slot
Chats
On a shared computer? Sign out when you're done.
Synthetic demo only — do not send real PHI.
Empty (first run) · .orc-rail__empty
Chats
No chats yet. Chats you start appear here.
On a shared computer? Sign out when you're done.
Synthetic demo only — do not send real PHI.
A plain meta line in secondary text: no border, icon or card. The composer is ready; the first message starts a chat.
Limit reached (20 chats) · notice slot filled
Chats
You've reached 20 chats. Delete one to start another.
On a shared computer? Sign out when you're done.
Synthetic demo only — do not send real PHI.
Narrowest width (16rem) · long titles
Chats
On a shared computer? Sign out when you're done.
Synthetic demo only — do not send real PHI.
Title, time and delete must not overlap at 16rem; the full title is in each row's title.
Orchestrator layer · assets/orc/app.css
.orc-conversation
One row: __select (title in one line with ellipsis, the full title in its title, update time in tabular-nums) and an icon __delete. On fine pointers delete rests at 0.6 opacity in unselected rows and is full on hover, on focus within the row and in the selected row; always full on touch. Selected uses the .worklist-row language: surface, 0.25rem inset bar in primary-600 and --shadow-sm, plus aria-current="true". The row button takes the inset ring, because the list clips. Delete asks inline before it acts.
Default · hover · selected · focus
Second row hover and fourth row focus (inset ring) are simulated. The first row shows the resting delete at 0.6 on fine pointers.
Untitled · long title · delete focused · disabled
Disabled (--disabled + disabled) while a reply is in flight: one of the two fades kept on purpose; the selected row keeps full opacity and its disabled delete control fades to 0.5.
Confirm delete · --confirm · deleting · --deleting
-
Worklist triage ideas
Delete this chat? This can't be undone.
-
Scheduling contrast slots
Delete this chat? This can't be undone.
Focus goes to “Cancel” when the confirm opens; after deletion it moves to the next row or to “New chat”. While deleting, “Delete” is busy (fade and spinner) and “Cancel” takes the unavailable look.
Orchestrator layer · assets/orc/app.css
.orc-skeleton
Loading placeholders that replace false empty states. role="status" with an .orc-sr-only text; the shapes are aria-hidden. They pulse gently and stop under reduced motion.
List · .orc-skeleton (default list variant)
Messages · .orc-skeleton--thread
Reduced motion (simulated): static shapes
Orchestrator layer · assets/orc/app.css
.orc-thread
The conversation column: __notices (persistent status and alert slots above the scroll area, kept mounted when empty), __scroll (the only scrolling part) and __content, centred at --orc-thread-measure, which holds the __log (role="log", aria-live="polite", additions only), the __extra slot for the welcome or loading shapes, the typing status and the failed-turn and copy status slots. __jump floats above the composer while the reader is away from the end. Notices are baseline .inline-notices; errors fill the alert slot, info and warning fill the status slot; raw error codes are never shown.
Chat busy in another tab (warning)
This chat is still answering in another tab. Try again in a moment.
Server error, no Retry (error) with reference
Orchestrator couldn't respond. Try again, and if it keeps happening share this reference with support.
Reference: 3c9e1b2fOffline (warning)
You're offline. Messages won't send until your connection is back.
Chat no longer available (info) with action
This chat is no longer available. It may have been deleted in another tab.
An answer arrived while reading earlier · __jump “New answer”, .orc-composer--divided
Shift handover notes
Synthetic demo only — do not send real PHI.
The thread did not move when the answer arrived; the button scrolls to the end, pins the thread, hides itself and puts focus in the composer. It follows the scroll area in the DOM, so Tab reaches it before the composer. The hairline shows while the thread is not at its end.
Reading more than a screen above the end · __jump “Jump to latest”
CT protocol checklist
Synthetic demo only — do not send real PHI.
Same element and behaviour, with no unseen answer. Hidden (hidden) whenever the reader is at the end. Not a live region: the log announces answers itself.
Thread with a notice in the slot and the loading shapes in __extra
CT protocol checklist
You're offline. Messages won't send until your connection is back.
Orchestrator layer · assets/orc/app.css
.orc-message
The user's turn is a compact bubble on the end side (--color-surface-selected, border --color-primary-100, small tail corner) with an .orc-sr-only “You”. The assistant's answer is unboxed reading text under a quiet 1.5rem tile (--orc-assistant-tile-*) and the name “Orchestrator”; its turn id is data-turn-id, never visible text, and __actions holds “Copy answer” and “Copy reference”. Turns are --space-8 apart, a question and its answer --space-4, the name and the text --space-1. The user's text is plain text that keeps line breaks (white-space: pre-wrap) and wraps long strings; the answer's body is .orc-md, rendered from its Markdown.
Default — user and assistant, with time (__meta / __time). Tool use under an answer is one whole phrase: “Used 1 tool”, “Used {n} tools”.
__actions — visible on the newest answer; on earlier answers they appear on hover and when focus is inside the article (hover the first answer). Always visible on touch.
--pending — optimistic turn: dashed bubble edge and “Sending…” instead of the time; long strings wrap.
Failed, can be retried · --failed, __error-meta
Aligned to the bubble's end and no wider than it. The text stays in the bubble and the composer stays empty: “Retry” is the one way to send it again, with the same client turn id. The persistent alert outside the log says once, without the id: “Orchestrator didn't answer. Press Retry to send it again.”
Failed while offline · “Retry” unavailable, __retry-note
“Retry” keeps its name and stays in the Tab order (aria-disabled="true", described by the note); its handler ignores click and Enter. When the connection returns it becomes available and the note goes.
Refused, cannot be retried · no “Retry”
An invalid request or a turn id conflict: the sentence comes from the error mapping, there is no “Retry”, and the text returns to the composer unless a new draft was typed meanwhile.
Copied (simulated) · __copy shows check for 2 seconds
The persistent status outside the log says “Copied” (“Reference copied” for the reference). Without the Clipboard API the answer text is selected and the status says “Press Ctrl+C to copy”. Focus stays on the button.
At or below 40rem: touch sizes · a 24.375rem .orc-shell container
Synthetic demo only — do not send real PHI.
On touch (any-pointer: coarse, or a shell at or below 40rem): answer actions always visible and 2.75rem, “Retry” and “Copy reference” 2.75rem, the failed line wraps with “Retry” first to move, no assistant indent, and “Send” icon-only 2.75rem with its accessible name kept. The key hint is omitted.
Orchestrator layer · assets/orc/app.css
.orc-md
The answer's Markdown, rendered as DOM nodes inside .orc-message__body: paragraphs with line breaks, headings h3–h5 no larger than the title size, strong and em, inline code, fenced code with a language caption, lists with one nested level, quotes, GFM tables, rules and http(s) links with an arrow and “(opens in a new tab)” in their name. Blocks are --space-2 apart; code and tables sit on --color-surface-muted with hairline borders and scroll inside their own focusable container. Source refs such as [S1] stay text. User messages stay plain text. The markup below is the renderer's output for the prototype state chat-answer-markdown.
Unsafe or cut-off input stays text
No img, script or javascript: link is created: raw HTML and links with any scheme other than http: or https: show as the characters the answer contains. Unclosed ** and backticks stay as typed; an unclosed fence runs to the end of the answer.
At or below 40rem · a 24.375rem .orc-shell container
No answer indent. The table and the code block keep whole words and scroll sideways inside their own containers (Tab to them, then use the arrow keys); long links and text wrap, so the page never scrolls horizontally.
Orchestrator layer · assets/orc/app.css
.orc-typing
Shown after the log, in the place where the answer will appear, while a turn is in flight: the quiet tile, three decorative dots and the sentence, always visible as secondary meta text. A persistent role="status". From 10 seconds __elapsed adds “· 0:12” (tabular-nums, updated every second, aria-hidden); at 15 seconds the sentence changes once to “Still preparing a response…”. No stages or percentages. Under reduced motion and forced colours the dots are hidden.
Up to 10 seconds
From 10 seconds · __elapsed
From 15 seconds · the status changes once
Reduced motion (simulated)
In the thread · where the answer will appear
Only the sentence is announced; the elapsed time stays silent. Keep the container mounted after the log (not inside it) and change only the text at 15 seconds.
Orchestrator layer · assets/orc/app.css
.orc-welcome
The empty conversation, a product block of its own (no .empty-state, border, card or icon): the question as a one-line title, the explanation (__text), three example requests as full-width chip buttons (__suggestion; each sends its text, creating a chat first when none is selected) and the demo note. It follows the thread measure from the messages' edge; on tall windows it sits at the bottom above the composer, and when it overflows it starts at the top.
Default
What could make your working day easier?
Ask about worklists, reports, protocols or handovers. Use synthetic examples only.
This is a demo. Live clinical systems are not connected.
Suggestion states: default · hover · focus (simulated)
Suggestions disabled at the 20-chat limit, no chat selected
You've reached 20 chats. Delete one to start another.
Unavailable look (muted surface, strong border, secondary text), no hover tint; the limit sentence beside them is linked by aria-describedby.
Orchestrator layer · assets/orc/app.css
.orc-composer
Pinned under the thread at the same measure. The field is baseline .input.input--textarea (+ __field, __control) and holds “Send” (__send) in its bottom end corner: the send icon and “Send” (__send-label), 2.25rem, above 40rem on fine pointers; icon-only 2.75rem on touch. The field grows with the text from 2 to 12 lines (field-sizing: content; scripts set the height where it is unsupported). Under it one service line (__footer): the hint, note or error at the start, the counter from 7,500 bytes at the end; the hint shows only while the field has focus. The disclaimer is always the last line. “Send” is button--secondary with native disabled for an empty draft; for any other reason it is aria-disabled="true" and points at the reason.
Ready, empty · Send disabled
Synthetic demo only — do not send real PHI.
Typing, focused (simulated) · Send primary, hint shown
Synthetic demo only — do not send real PHI.
Sending · field cleared, Send busy
Synthetic demo only — do not send real PHI.
Near the limit · __count--warning
Synthetic demo only — do not send real PHI.
The numbers are aria-hidden; the field is described by “Message size: 7,940 of 8,000”. Crossing 7,500 is announced once through the status slot.
Over the limit · __error, __count--error, Send aria-disabled
Synthetic demo only — do not send real PHI.
The error replaces the hint; n is how many characters must go from the end to fit 8,000 bytes (“Remove 1 character to send.” for one).
No chat selected · __note, sending starts a chat
Synthetic demo only — do not send real PHI.
Creating the chat · text kept, Send busy
Synthetic demo only — do not send real PHI.
A second Send or Enter creates nothing. If creating fails, the text stays here and the thread shows the failure with a reference.
No chat selected at the 20-chat limit · unavailable, explained by the note
Synthetic demo only — do not send real PHI.
Offline · draft kept, Send described by the offline notice
You're offline. Messages won't send until your connection is back.
Synthetic demo only — do not send real PHI.
Draft restored after a refused turn (no Retry)
Synthetic demo only — do not send real PHI.
Only refusals without “Retry” return the text. After a failure that offers “Retry” the composer stays empty.
Loading · field and Send disabled
Synthetic demo only — do not send real PHI.
Orchestrator layer · assets/orc/app.css
.orc-disclaimer
The synthetic-data line under the composer and on the sign-in screen. Never hidden at any width or height; 0.75rem semibold in --color-text-secondary with a shield icon in --color-warning-700.
Synthetic demo only — do not send real PHI.
Orchestrator layer · assets/orc/app.css
.orc-reference and .orc-sr-only
.orc-reference shows a request id after a system failure: a line of its own under a notice sentence, or beside “Copy reference” in a failed turn. It is secondary text (--orc-reference-fg) in chat and sign-in alike, never the error's colour, and wraps anywhere instead of widening its block; __id uses the UI font with tabular numbers and selects with one click. .orc-sr-only hides text visually and keeps it for screen readers (“You”, field labels, loading text, the copy and size statuses).
We couldn't send the code. Try again in a moment.
Reference: 7f3a9c21A long id wraps anywhere:
Reference: 3c9e1b2f-6a7d-4e21-9b0c-5d8f2a41e7c3Hidden label here → “Message Orchestrator” (present for screen readers only).
Product scope · .orc-shell and .login-page
Focus, fields and unavailable buttons
Inside .orc-shell and .login-page the product sets the baseline's own custom properties, so the vendored files stay unchanged: --focus-ring, --input-focus-shadow and --input-invalid-shadow are --orc-focus-ring (a 0.125rem surface gap, then a 0.125rem --color-primary-700 ring, 6.70:1); in .orc-composer and .login-form the field edge is --orc-field-border (3.32:1) and the placeholder --orc-field-placeholder. Where an outer ring would be clipped (the thread scroller, rail rows) the inset ring is used. Focus is simulated here; Tab through the page to see the real ring.
Focus ring (simulated) · on the surface and on the page background
6.70:1 on the surface, 6.23:1 on the page background, 6.14:1 on muted surfaces. Elements not built on .button (rows, suggestions, the resizer) set the same box-shadow themselves, with a transparent outline that forced colours repaint.
Fields: rest · focused · invalid · invalid and focused (simulated)
Enter a valid email address.
Enter a valid email address.
The field edge reaches 3:1 at rest; the placeholder is secondary text. A focused invalid field keeps its danger border and gains the blue ring, so it differs from an unfocused one.
Unavailable buttons · filled, ghost, focused · busy keeps the fade
You're offline. Messages won't send until your connection is back.
:disabled or [aria-disabled="true"] without aria-busy: full opacity, cursor: not-allowed, no hover; filled variants take --orc-disabled-bg, --orc-disabled-border and --orc-disabled-fg (5.48:1) without a shadow, ghost stays transparent with --orc-disabled-fg. A focused one keeps its ring (second row, simulated). Busy buttons keep the baseline fade beside their spinner.
Where each applies
| Case | Markup |
|---|---|
| Send, empty draft | button--secondary, native disabled |
| Send at the chat limit, over the length limit, offline | aria-disabled="true" + aria-describedby → note, error or offline notice |
| Retry offline | aria-disabled="true" + aria-describedby → “Available when you're back online” |
| Sending, creating a chat, “Sending code…”, “Checking code…” | aria-busy="true" + aria-disabled="true" + spinner (fade kept) |
| “Send code”, “Sign in” | Never disabled for empty input; checked on press |
| Locked rows during a turn, resting delete | Fades kept on purpose: 0.55 (delete 0.5) and 0.6 |
Shared Medical pattern · assets/orc/login.css
login-* — sign in with a one-time code
The RIS/HIS sign-in, with the same form structure: 27rem panel, brand tile, .card--elevated, 2.75rem fields. The page content is anchored to the top, so the brand and the card stay put between the steps. The code step has one sent line with the email (.login-code-sent) and “Change email” under it, one code field (inputmode="numeric", autocomplete="one-time-code", no icon) with six fixed digit slots centred in a 3.25rem box: 1.5rem monospace digits, letter-spacing: .5em, and placeholder zeros (.login-otp__slots, hidden from assistive tech) that each typed digit replaces in place, and right under the field the resend row: the spam note and a fixed-size slot (.login-resend__slot) with the countdown as text, then “Resend code”. Input errors stay at the field without a reference; system failures are a notice between the field and the main button, with a reference. “Send code” and “Sign in” are never disabled for empty input; only their busy states are. Orchestrator adds the session-ended notice, the footnote and the fixed demo strip above the scrollable form. Below 40rem the panel takes the full width and fields use 1rem text. The prototype keeps status slots mounted and updates their contents; static specimens illustrate their populated state.
Full screen · email step after sign-out (toast) · .login-page
Sign in
Enter your work email. We'll send you a one-time code.
No password needed. Chats are deleted when you sign out.
Signed out
Full screen · code step, sent · the brand and card top stay where they were
Enter the code
We sent a 6-digit code to [email protected].
No password needed. Chats are deleted when you sign out.
Email · invalid (no reference)
Sign in
Enter your work email. We'll send you a one-time code.
Email · sending (busy)
Sign in
Enter your work email. We'll send you a one-time code.
Email · request failed (error notice with reference)
Sign in
Enter your work email. We'll send you a one-time code.
Email · too many code requests (warning, no reference)
Sign in
Enter your work email. We'll send you a one-time code.
Email · session ended
Your session ended. Sign in again to continue.
Sign in
Enter your work email. We'll send you a one-time code.
Code · sent, countdown in .login-resend__slot
Enter the code
We sent a 6-digit code to [email protected].
Tab order: “Change email”, the code field, “Resend code” when shown, “Sign in”. The countdown is not a live region.
Code · incomplete (field error, no reference)
Enter the code
We sent a 6-digit code to [email protected].
Code · wrong code while the countdown runs
Enter the code
We sent a 6-digit code to [email protected].
Code · checking (busy)
Enter the code
We sent a 6-digit code to [email protected].
Code · wrong code, “Resend code” available
Enter the code
We sent a 6-digit code to [email protected].
“Resend code” takes the countdown's slot at the same height, so the row does not shift; the status says “You can request a new code now.” once.
Code · new code sent
Enter the code
We sent a new code to [email protected].
The field is cleared and focused, and the countdown restarts at 0:45.
Code · new code failed (error notice above “Sign in”)
Enter the code
We sent a 6-digit code to [email protected].
The countdown does not restart and “Resend code” stays available.