dental-soap / style.css
DrZayed's picture
Native-quality Arabic handoff card
7cf8405
Raw
History Blame Contribute Delete
45.2 kB
/* ============================================================
DENTAL SOAP — Design System: "Clinical Editorial" (ivory/ink/amber)
Implemented from the Dental SOAP Design System bundle:
warm near-monochrome ink + ivory; amber is the ONLY chromatic
accent and is reserved exclusively for safety. No red. No green.
Flat warm surfaces — no gradients, no glassmorphism.
============================================================ */
/* ---------- Google Fonts ---------- */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Inter+Tight:wght@500;600;700&family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans+Arabic:wght@400;500;600;700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap');
/* ---------- Design tokens ---------- */
:root {
/* Ink ramp (warm near-blacks → warm grays) */
--ink-900: #0D0D0D;
--ink-800: #1A1815;
--ink-700: #2B2823;
--ink-600: #46423B;
--ink-500: #6B665C;
--ink-400: #8C887E;
--ink-300: #B4AFA4;
--ink-200: #D2CDC2;
/* Ivory ramp (warm off-whites → warm paper greys) */
--ivory-50: #FBFAF7;
--ivory-100: #F5F4F0;
--ivory-200: #EDEBE4;
--ivory-300: #E2DFD6;
--ivory-400: #D2CEC2;
/* Amber — SAFETY ONLY */
--amber-700: #B45309;
--amber-600: #D97706;
--amber-500: #F59E0B;
--amber-400: #FBBF24;
--amber-100: #FCEFD2;
--amber-50: #FDF6E7;
/* Legacy alias map (kept so every existing rule keeps resolving) */
--primary: var(--ink-900);
--primary-container: var(--ink-700);
--on-primary: var(--ivory-50);
--on-primary-container: var(--ivory-50);
--primary-fixed: var(--ivory-200);
--primary-fixed-dim: var(--ivory-400);
--surface: var(--ivory-100);
--surface-bright: var(--ivory-100);
--surface-container-lowest: var(--ivory-50);
--surface-container-low: var(--ivory-200);
--surface-container: var(--ivory-200);
--surface-container-high: var(--ivory-300);
--surface-container-highest: var(--ivory-300);
--surface-dim: var(--ivory-400);
--surface-tint: var(--ink-700);
--on-surface: var(--ink-900);
--on-surface-variant: var(--ink-600);
--inverse-surface: var(--ink-800);
--outline: var(--ink-300);
--outline-variant: var(--ivory-400);
/* "Error"/danger now routes to the amber safety system (no red) */
--error: var(--amber-700);
--error-container: var(--amber-100);
--on-error: var(--ink-900);
--on-error-container: var(--amber-700);
/* Tertiary: neutral ink (the old clinical green-teal is retired) */
--tertiary: var(--ink-300);
--tertiary-container: var(--ink-500);
--on-tertiary: var(--ivory-50);
--secondary: var(--ink-600);
--secondary-container: var(--ivory-200);
--on-secondary: var(--ivory-50);
--on-secondary-container: var(--ink-600);
/* Functional aliases */
--brand: var(--ink-900);
--brand-deep: var(--ink-800);
--ink: var(--ink-900);
--muted: var(--ink-500);
--page: var(--ivory-100);
--paper: var(--ivory-50);
/* "Gradients" — retired; vars now resolve to flat ink fills */
--grad-brand: var(--ink-900);
--grad-brand-hover: var(--ink-700);
--grad-deep: var(--ink-900);
/* "Glass" — retired; solid warm surfaces */
--glass-bg: var(--ivory-50);
--glass-blur: none;
--glass-border: var(--ivory-300);
/* Safety (amber system — urgency by intensity, never hue) */
--safety-accent: var(--amber-500);
--safety-fg: var(--amber-700);
--safety-bg: var(--amber-50);
--safety-bg-strong: var(--amber-100);
--safety-border: #E8C98A;
--safe-bg: var(--ivory-50); /* clear state: neutral, never green */
--safe-line: var(--ivory-400);
--danger: var(--amber-500);
--danger-bg: var(--amber-50);
--danger-line: #E8C98A;
--amber: var(--amber-700);
/* Radius — sheet feels like paper (4px); inputs 8px; cards 12px */
--radius: 12px;
--radius-sm: 8px;
--radius-xs: 6px;
--radius-sheet: 4px;
/* Shadows — soft, warm-tinted, ambient */
--shadow-card: 0 1px 3px rgba(26, 24, 21, 0.05), 0 6px 16px -8px rgba(26, 24, 21, 0.08);
--shadow-card-hover: 0 2px 6px rgba(26, 24, 21, 0.05), 0 14px 32px -12px rgba(26, 24, 21, 0.12);
--shadow-float: 0 1px 2px rgba(26, 24, 21, 0.05), 0 18px 48px -20px rgba(26, 24, 21, 0.22);
/* Typography */
--font-headline: 'Inter Tight', 'Inter', system-ui, sans-serif;
--font-body: 'Inter', system-ui, sans-serif;
--font-mono: 'IBM Plex Mono', 'Fira Mono', monospace;
--font-arabic: 'IBM Plex Sans Arabic', 'Noto Sans Arabic', 'Geeza Pro', sans-serif;
}
/* ---------- Color scheme lock (always light) ---------- */
:root { color-scheme: light; }
/* ---------- Single-theme guard (safety net) ----------
Dental SOAP has no dark variant. A synchronous <head> script forces
?__theme=light before the Gradio bundle loads. THIS block is the fallback
for the rare case that redirect is blocked (sandboxed iframe / strict CSP):
it re-pins Gradio's dark-theme variables to the light palette so .dark
renders identically to light. Without it, Gradio's dark --body-text-color
(#f5f5f4) repaints every text rule lacking !important to near-white on our
forced-ivory surfaces — invisible. */
.dark {
/* !important on each custom property is required: Gradio's own .dark
variable block has equal specificity and loads after this stylesheet,
so a plain re-pin loses the source-order tie (verified: --body-text-color
stayed #f5f5f4). !important wins the cascade regardless of order. */
color-scheme: light;
--body-text-color: var(--ink-900) !important;
--body-text-color-subdued: var(--ink-500) !important;
--background-fill-primary: var(--ivory-100) !important;
--background-fill-secondary: var(--ivory-50) !important;
--block-background-fill: var(--ivory-50) !important;
--block-label-text-color: var(--ink-600) !important;
--block-title-text-color: var(--ink-900) !important;
--input-background-fill: var(--ivory-50) !important;
--input-border-color: var(--ivory-400) !important;
--border-color-primary: var(--ivory-300) !important;
--border-color-accent: var(--ink-700) !important;
--panel-background-fill: var(--ivory-100) !important;
--color-accent: var(--ink-700) !important;
--link-text-color: var(--ink-900) !important;
}
/* ---------- Base / Background — flat warm ivory, no gradients ---------- */
html,
body,
gradio-app,
.main,
.wrap,
main.contain,
.gradio-container {
background: var(--surface) !important;
color: var(--ink) !important;
font-family: var(--font-body) !important;
}
body { background-attachment: fixed; }
/* ---------- Form controls ---------- */
textarea,
input,
select,
.gradio-container textarea,
.gradio-container input {
background: var(--surface-container-lowest) !important;
color: var(--ink) !important;
border: 1px solid var(--outline-variant) !important;
border-radius: var(--radius-sm) !important;
font-family: var(--font-body) !important;
transition: border-color 0.2s ease, box-shadow 0.2s ease !important;
}
textarea:focus,
input:focus,
.gradio-container textarea:focus,
.gradio-container input:focus {
border-color: var(--ink-900) !important;
box-shadow: 0 0 0 3px rgba(13, 13, 13, 0.08) !important;
outline: none !important;
}
/* ---------- Ghost document (pre-build empty state) ---------- */
/* A faint preview of the REAL handoff card: same paper, same ink band, real
section headings over unfilled lines — the empty state sells the artifact.
Static on purpose: the shimmer skeleton means "composing now"; the ghost
means "awaiting your story". */
.ghost-line {
height: 11px;
margin: 7px 0;
border-radius: 4px;
background: var(--ivory-200);
border: 1px dashed var(--ivory-400);
}
.ghost-sec { margin-top: 2px; }
.handoff-card.ghost .doc-body h3 { color: var(--ink-400) !important; }
.doc-badge.ghost-badge {
background: transparent;
border-style: dashed;
}
.ghost-cta {
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--ink-500);
margin: 14px 0 2px;
}
/* ---------- Container ---------- */
.gradio-container {
max-width: 1280px !important;
margin: 0 auto !important;
font-family: var(--font-body) !important;
color: var(--ink);
background: transparent !important;
padding-bottom: 80px !important;
/* Gradio accent (progress bars, generating pulse) on ink */
--color-accent: var(--ink-700);
}
/* ---------- Header ---------- */
.app-header {
display: flex;
justify-content: space-between;
align-items: flex-end;
gap: 16px;
flex-wrap: wrap;
padding: 20px 4px 10px;
}
.brand-row { display: flex; align-items: center; gap: 14px; }
.brand-mark {
flex: none;
width: 46px;
height: 46px;
border-radius: 12px;
background: var(--ink-900);
box-shadow: var(--shadow-card);
display: flex;
align-items: center;
justify-content: center;
}
.brand-name {
font-family: var(--font-headline);
font-size: 26px;
font-weight: 600;
line-height: 1;
color: var(--ink);
letter-spacing: -0.02em;
}
.brand-tag {
color: var(--muted);
font-size: 13px;
margin-top: 4px;
font-weight: 500;
font-family: var(--font-body);
}
.privacy-pill {
border: 1px solid var(--outline-variant);
background: var(--ivory-50);
color: var(--ink-600);
font-family: var(--font-mono);
font-size: 10.5px;
font-weight: 500;
letter-spacing: 0.10em;
text-transform: uppercase;
padding: 7px 16px;
border-radius: 9999px;
box-shadow: var(--shadow-card);
}
/* ---------- Step rail ---------- */
.step-rail { display: flex; gap: 8px; flex-wrap: wrap; margin: 4px 0 8px; }
.rail-chip {
display: flex;
align-items: center;
gap: 7px;
border: 1px solid var(--outline-variant);
background: var(--ivory-50);
border-radius: 9999px;
padding: 5px 14px 5px 6px;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.01em;
color: var(--ink-700);
box-shadow: var(--shadow-card);
font-family: var(--font-body);
}
.rail-chip .n {
width: 20px;
height: 20px;
border-radius: 9999px;
background: var(--ink-900);
color: var(--ivory-50);
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 600;
font-family: var(--font-mono);
}
/* ---------- Step cards ---------- */
/* Hairline borders + tonal layering; faint warm shadow. */
.step-card {
background: var(--surface-container-lowest) !important;
border: 1px solid var(--ivory-300) !important;
border-radius: var(--radius) !important;
padding: 22px 22px 18px !important;
box-shadow: var(--shadow-card) !important;
transition: box-shadow 0.25s ease, border-color 0.25s ease;
margin-bottom: 12px;
}
.step-card:hover {
box-shadow: var(--shadow-card-hover) !important;
border-color: var(--ivory-400) !important;
}
.step-card .block, .step-card .form, .step-card .styler {
border: none !important;
box-shadow: none !important;
background: transparent !important;
}
/* Flatten Gradio Soft theme's block-info chip — eyebrow style: mono, tracked */
span[data-testid="block-info"] {
background: transparent !important;
color: var(--muted) !important;
font-weight: 500 !important;
font-size: 11px !important;
letter-spacing: 0.10em !important;
text-transform: uppercase !important;
font-family: var(--font-mono) !important;
}
.step-head { display: flex; gap: 14px; align-items: flex-start; margin-bottom: 14px; }
.step-num {
flex: none;
width: 32px;
height: 32px;
border-radius: 9999px;
background: var(--ink-900);
color: var(--ivory-50);
font-weight: 600;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 1px;
box-shadow: var(--shadow-card);
font-family: var(--font-mono);
}
.step-title {
font-family: var(--font-headline);
font-weight: 600;
font-size: 16px;
color: var(--ink);
letter-spacing: -0.01em;
}
.step-sub {
color: var(--muted);
font-size: 12.5px;
margin-top: 3px;
font-family: var(--font-body);
}
/* ---------- Buttons ---------- */
/* Primary = ink fill with ivory text (never amber). */
button.primary, .primary.svelte-cmf5ev {
background: var(--ink-900) !important;
border: 1px solid transparent !important;
color: var(--ivory-50) !important;
border-radius: var(--radius) !important;
font-weight: 600 !important;
font-family: var(--font-body) !important;
letter-spacing: 0.01em !important;
box-shadow: var(--shadow-card) !important;
transition: background 0.14s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.14s cubic-bezier(0.4, 0, 0.2, 1),
transform 0.14s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
button.primary:hover {
background: var(--ink-700) !important;
box-shadow: var(--shadow-card-hover) !important;
}
button.primary:active { transform: translateY(0.5px) scale(0.992); }
/* Secondary / ghost buttons — outline on warm paper */
button:not(.primary) {
background: var(--ivory-50) !important;
border: 1px solid var(--outline-variant) !important;
color: var(--on-surface-variant) !important;
border-radius: var(--radius) !important;
font-family: var(--font-body) !important;
font-weight: 500 !important;
transition: background 0.2s ease, border-color 0.2s ease !important;
}
button:not(.primary):hover {
background: var(--surface-container) !important;
border-color: var(--ink-300) !important;
color: var(--ink-900) !important;
}
/* Example chips */
.example-chip {
border: 1px solid var(--outline-variant) !important;
background: var(--ivory-50) !important;
color: var(--ink-700) !important;
font-weight: 600 !important;
font-size: 12.5px !important;
border-radius: 9999px !important;
letter-spacing: 0.01em !important;
box-shadow: var(--shadow-card) !important;
transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease !important;
font-family: var(--font-body) !important;
}
.example-chip:hover {
border-color: var(--ink-300) !important;
background: var(--surface-container) !important;
box-shadow: var(--shadow-card-hover) !important;
color: var(--ink-900) !important;
}
/* ---------- Status line ---------- */
.status-line, .status-line p {
color: var(--muted) !important;
font-size: 12px !important;
font-weight: 500 !important;
letter-spacing: 0.03em !important;
font-family: var(--font-body) !important;
}
/* ---------- Safety panel ---------- */
/* Amber wash + 3px amber left accent for a fired rule.
Clear state stays NEUTRAL ink/ivory — never green. */
.safety-panel {
border: 1px solid var(--safety-border);
border-left: 3px solid var(--safety-accent);
background: var(--safety-bg);
color: var(--ink-900);
border-radius: var(--radius);
padding: 18px 20px;
box-shadow: var(--shadow-card);
transition: border-color 0.25s ease;
}
.safety-panel,
.safety-panel strong,
.safety-panel p,
.safety-panel .flag-title,
.safety-panel .flag-body { color: var(--ink-900) !important; }
.safety-panel.clear {
border-color: var(--ivory-400);
border-left: 3px solid var(--ink-300);
background: var(--ivory-50);
color: var(--ink-600);
box-shadow: var(--shadow-card);
}
.safety-panel.clear,
.safety-panel.clear strong,
.safety-panel.clear p { color: var(--ink-600) !important; }
.safety-kicker {
font-size: 10.5px;
letter-spacing: 0.12em;
text-transform: uppercase;
font-weight: 500;
opacity: 0.9;
margin-bottom: 6px;
font-family: var(--font-mono);
}
.safety-kicker { color: var(--safety-fg) !important; }
.safety-panel.clear .safety-kicker { color: var(--ink-400) !important; }
.safety-panel p { margin: 4px 0 0; font-size: 13.5px; line-height: 1.65; }
/* Flag blocks within safety panel */
.flag {
border-top: 1px solid var(--safety-border);
padding-top: 14px;
margin-top: 14px;
}
/* Tier pills — amber intensity encodes urgency (solid → tint → outline) */
.tier-pill {
display: inline-block;
font-size: 10.5px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
border-radius: var(--radius-xs);
padding: 4px 10px;
margin-bottom: 8px;
font-family: var(--font-mono);
}
.tier-emergency {
background: var(--amber-500);
color: var(--ink-900);
border: 1px solid var(--amber-500);
box-shadow: 0 2px 10px rgba(245, 158, 11, 0.35);
}
.tier-urgent {
background: var(--safety-bg-strong);
color: var(--safety-fg);
border: 1px solid var(--safety-accent);
}
.tier-discuss {
background: transparent;
color: var(--ink-600);
border: 1px solid var(--ink-300);
}
.flag-title {
font-family: var(--font-headline);
font-weight: 600;
font-size: 14.5px;
letter-spacing: -0.01em;
}
.flag-body { font-size: 13.5px; line-height: 1.65; margin-top: 4px; }
.evidence {
color: var(--muted);
font-size: 12.5px;
margin-top: 8px;
border-left: 2px solid var(--safety-accent);
padding-left: 10px;
font-style: italic;
font-family: var(--font-body);
}
/* Safety-flag provenance disclosure — collapsed by default, expands to the
exact rule + matched evidence spans. The deterministic "show your work". */
.flag-rule { margin-top: 9px; }
.flag-rule > summary {
cursor: pointer;
list-style: none;
display: inline-flex;
align-items: center;
gap: 8px;
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink-500);
-webkit-user-select: none;
user-select: none;
}
.flag-rule > summary::-webkit-details-marker { display: none; }
.flag-rule > summary::before {
content: "\25B8"; /* ▸ */
color: var(--ink-400);
font-size: 9px;
transition: transform 0.15s ease;
}
.flag-rule[open] > summary::before { transform: rotate(90deg); }
.flag-rule-k { color: var(--ink-600); }
.flag-rule-id {
background: var(--ivory-200);
border: 1px solid var(--ink-300);
border-radius: var(--radius-xs);
padding: 1px 6px;
color: var(--ink-700);
}
.flag-evidence {
margin-top: 8px;
border-left: 2px solid var(--safety-accent);
padding-left: 10px;
}
/* Static evidence copy for paper only — the interactive <details> twin above
cannot render its content when printed closed. */
.flag-evidence-print { display: none; }
.ev-span { font-size: 12.5px; color: var(--ink-600); font-style: italic; margin: 3px 0; line-height: 1.5; }
.ev-src {
font-style: normal;
font-family: var(--font-mono);
font-size: 9.5px;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--ink-400);
margin-right: 6px;
}
/* ---------- Handoff document card ---------- */
/* The one element with a real lift (paper on desk); near-sharp corners
so it reads as a physical Letter/A4 sheet. */
.handoff-card {
border: 1px solid var(--ivory-300);
background: var(--paper);
border-radius: var(--radius-sheet);
overflow: hidden;
box-shadow: 0 1px 2px rgba(26, 24, 21, 0.05), 0 18px 48px -20px rgba(26, 24, 21, 0.22);
}
/* Result reveal: the finished document settles onto the desk. Gradio swaps the
HTML node on every render, so the animation re-fires exactly once per build. */
@keyframes cardSettle {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: no-preference) {
.handoff-card, .safety-panel, .interrupt-card {
animation: cardSettle 0.45s cubic-bezier(0.19, 0.66, 0.28, 0.99) both;
}
}
.doc-band {
background: var(--ink-900);
padding: 20px 28px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
/* Force ivory text in band (Gradio prose overrides inherited) */
.doc-band, .doc-band * { color: var(--ivory-50) !important; }
.interrupt-band, .interrupt-band * { color: var(--ink-900) !important; }
.doc-band .product {
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
opacity: 0.7;
font-weight: 500;
}
.doc-band .artifact {
font-family: var(--font-headline);
font-size: 22px;
font-weight: 600;
margin-top: 4px;
letter-spacing: -0.02em;
}
.doc-meta {
font-family: var(--font-body);
font-size: 11.5px;
text-align: right;
line-height: 1.6;
max-width: 62%;
}
.doc-meta .patient-line {
font-weight: 600;
font-size: 13px;
letter-spacing: 0.01em;
margin-bottom: 7px;
}
.doc-badges {
display: flex;
flex-wrap: wrap;
gap: 6px;
justify-content: flex-end;
}
.doc-badge {
display: inline-flex;
align-items: center;
padding: 3px 11px;
border-radius: 999px;
font-size: 10.5px;
font-weight: 500;
letter-spacing: 0.05em;
font-family: var(--font-mono);
background: rgba(255, 255, 255, 0.10);
border: 1px solid rgba(255, 255, 255, 0.26);
white-space: nowrap;
}
.doc-badge.flag-clear {
background: rgba(255, 255, 255, 0.10);
border-color: rgba(255, 255, 255, 0.30);
}
.doc-badge.flag-alert {
background: rgba(245, 158, 11, 0.22);
border-color: rgba(245, 158, 11, 0.60);
}
.doc-body { padding: 10px 28px 28px; }
.doc-body,
.doc-body p,
.doc-body li,
.doc-body ol,
.doc-body ul { color: var(--ink) !important; }
/* Section headers: Title-Case content, mono tracked label treatment */
.doc-body h3 {
font-family: var(--font-mono);
font-size: 10.5px;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--ink-500);
border-top: 1px solid var(--ivory-300);
padding-top: 16px;
margin: 20px 0 8px;
font-weight: 500;
}
.doc-body h3 { color: var(--ink-500) !important; }
.doc-body p, .doc-body li { font-size: 13.5px; line-height: 1.70; font-family: var(--font-body); }
.doc-body p { margin: 5px 0 0; }
.doc-body ul, .doc-body ol { margin: 6px 0 0 20px; padding: 0; }
.doc-body li { margin-bottom: 6px; }
/* Plain section bullets — quiet ink dot.
Classed lists (tracker/bring checkboxes) keep their own markers. */
.doc-body ul:not([class]) { list-style: none; margin-left: 0; }
.doc-body ul:not([class]) > li { position: relative; padding-left: 22px; }
.doc-body ul:not([class]) > li::before {
content: "";
position: absolute;
left: 6px;
top: 0.62em;
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--ink-400);
}
/* Numbered dentist questions — circled mono counters */
.doc-body ol { list-style: none; counter-reset: dq; margin-left: 0; }
.doc-body ol > li {
counter-increment: dq;
position: relative;
padding-left: 32px;
margin-bottom: 9px;
}
.doc-body ol > li::before {
content: counter(dq);
position: absolute;
left: 0;
top: 1px;
width: 21px;
height: 21px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-mono);
font-size: 11px;
font-weight: 600;
background: transparent;
border: 1px solid var(--ink-300);
color: var(--ink-700) !important;
}
/* RTL mirror for the Arabic card */
.arabic .doc-body ul:not([class]) > li { padding-right: 22px; padding-left: 0; }
.arabic .doc-body ul:not([class]) > li::before { right: 6px; left: auto; }
.arabic .doc-body ol > li { padding-right: 32px; padding-left: 0; }
.arabic .doc-body ol > li::before { right: 0; left: auto; }
.arabic .doc-badges { justify-content: flex-start; }
/* Tracker checklist — custom checkbox via ::before */
ul.tracker { margin-left: 0 !important; }
ul.tracker li { list-style: none; position: relative; padding-left: 28px; }
ul.tracker li::before {
content: "";
position: absolute;
left: 0;
top: 4px;
width: 14px;
height: 14px;
border: 1.5px solid var(--ink-400);
border-radius: 4px;
background: var(--ivory-50);
}
/* IBM Plex Mono for clinical vitals/data values in the handoff card */
.doc-body .mono-value,
.doc-body code,
.doc-body kbd {
font-family: var(--font-mono);
font-size: 12.5px;
font-variant-numeric: tabular-nums;
background: var(--surface-container-low);
padding: 1px 6px;
border-radius: 4px;
color: var(--ink-700);
}
.doc-foot {
border-top: 1px solid var(--ivory-300);
margin-top: 20px;
padding-top: 14px;
color: var(--muted);
font-size: 11.5px;
line-height: 1.60;
font-family: var(--font-body);
}
.doc-foot { color: var(--muted) !important; }
.doc-foot-meta {
font-family: var(--font-mono);
font-size: 9.5px;
letter-spacing: 0.04em;
color: var(--ink-400) !important;
margin-bottom: 7px;
}
/* ---------- Hard interrupt card ---------- */
/* The ONLY solid-amber-filled element in the product: maximal urgency is
encoded by fill + scale + contrast, not by red. Ink text on amber. */
.interrupt-card {
border: 1.5px solid var(--amber-500);
background: var(--surface-container-lowest);
border-radius: var(--radius-sm);
overflow: hidden;
box-shadow: 0 1px 2px rgba(26, 24, 21, 0.05), 0 12px 32px rgba(245, 158, 11, 0.18), 0 28px 56px -16px rgba(26, 24, 21, 0.14);
}
.interrupt-band {
background: var(--amber-500);
color: var(--ink-900);
padding: 18px 28px;
font-family: var(--font-headline);
font-weight: 700;
font-size: 18px;
letter-spacing: -0.01em;
}
.interrupt-body { padding: 12px 28px 24px; }
.interrupt-body,
.interrupt-body p,
.interrupt-body li,
.interrupt-body strong { color: var(--ink-800) !important; }
.interrupt-body h3 {
font-family: var(--font-mono);
font-size: 10.5px;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--amber-700);
margin: 16px 0 5px;
font-weight: 600;
}
.interrupt-body h3 { color: var(--amber-700) !important; }
.interrupt-body p, .interrupt-body li { font-size: 13.5px; line-height: 1.65; }
/* ---------- Arabic RTL ---------- */
/* IBM Plex Sans Arabic; Arabic text ~10% larger than the English
equivalent, line-height 2.0 so descenders breathe (headline feature). */
.arabic {
direction: rtl;
text-align: right;
font-family: var(--font-arabic);
}
.arabic .doc-meta { text-align: left; }
.arabic .doc-body p,
.arabic .doc-body li {
font-size: 15px; /* 10% larger than 13.5px English base */
line-height: 2.0;
}
/* Untranslated patient/model narrative inside the Arabic card carries a small
"(as written by the patient)" annotation so English under an Arabic heading
reads as provenance, not a rendering bug. */
.arabic .as-written {
font-size: 12px;
color: var(--ink-400) !important;
font-style: normal;
}
/* Arabic script connects letters — Latin tracking rules (uppercase
letter-spacing on h3/artifact) visibly break the joins, and the mono
heading stack has no Arabic glyphs (random system fallback). Use the
loaded Plex Arabic and reset tracking. */
.arabic .doc-body h3,
.arabic .doc-band .artifact {
letter-spacing: 0;
font-family: var(--font-arabic);
}
.arabic .doc-body h3 { font-weight: 600; font-size: 12px; }
/* Patient text fields auto-detect direction: an Egyptian Arabic story aligns
right as it is typed, English stays left — no settings toggle needed. */
.gradio-container textarea,
.gradio-container input[type="text"] { unicode-bidi: plaintext; }
/* ---------- Disclaimer ribbon ---------- */
#disclaimer-block, .block:has(.disclaimer-bar) {
position: relative !important;
z-index: 2;
}
.disclaimer-bar {
display: flex;
align-items: flex-start;
gap: 14px;
background: var(--ivory-50);
border: 1px solid var(--ivory-300);
color: var(--on-surface-variant);
text-align: left;
font-size: 12.5px;
line-height: 1.55;
padding: 12px 16px;
border-radius: var(--radius);
margin: 8px 0 16px;
box-shadow: var(--shadow-card);
}
.disclaimer-bar, .disclaimer-bar * { color: var(--on-surface-variant) !important; }
.disclaimer-bar strong { color: var(--ink) !important; font-weight: 600; }
.disclaimer-kicker {
flex: none;
color: var(--ink-700) !important;
background: var(--ivory-200);
border-radius: 9999px;
padding: 4px 9px;
font-family: var(--font-mono);
font-size: 10px;
font-weight: 500;
letter-spacing: 0.10em;
line-height: 1.35;
text-transform: uppercase;
}
.disclaimer-copy { padding-top: 2px; }
/* ---------- Agent Activity Dashboard ---------- */
.agent-dashboard {
border: 1px solid var(--ivory-300);
background: var(--surface-container-lowest);
border-radius: var(--radius);
margin-bottom: 12px;
box-shadow: var(--shadow-card);
overflow: hidden;
}
/* Ink header band — mirrors the handoff card's doc-band so the workflow reads
as a first-class, authoritative panel rather than a footnote. */
.agent-dashboard-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
background: var(--ink-900);
padding: 12px 16px;
}
.agent-dashboard-title {
font-family: var(--font-mono);
font-size: 10.5px;
letter-spacing: 0.12em;
text-transform: uppercase;
font-weight: 500;
/* !important: the Space's SSR bundle loads prose CSS after this sheet and
wins the cascade tie. */
color: var(--ivory-50) !important;
}
.agent-dashboard-note {
color: var(--ink-300) !important;
font-size: 11.5px;
line-height: 1.45;
margin-top: 3px;
}
.workflow-route {
flex: none;
border: 1px solid var(--ink-600);
background: var(--ink-700);
color: var(--ivory-50) !important;
border-radius: 9999px;
padding: 4px 9px;
font-size: 9.5px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
font-family: var(--font-mono);
}
.agent-list {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
padding: 14px 16px 16px;
}
.agent-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
min-width: 0;
padding: 8px 10px;
border-radius: var(--radius-sm);
background: var(--surface-container-low);
font-size: 12.5px;
font-family: var(--font-body);
}
.agent-name {
display: block;
font-weight: 600;
color: var(--ink);
line-height: 1.25;
}
.agent-role {
display: block;
color: var(--muted);
font-size: 10.5px;
line-height: 1.35;
margin-top: 2px;
}
/* Provenance type badge: RULE = deterministic (emphasized solid ink) vs
LLM = model-driven (light outline). Makes "the model can never author a
safety flag" legible at a glance — the core trust differentiator. */
.agent-type {
font-family: var(--font-mono);
font-size: 8.5px;
font-weight: 600;
letter-spacing: 0.07em;
padding: 1px 5px;
border-radius: var(--radius-xs);
margin-left: 7px;
text-transform: uppercase;
white-space: nowrap;
}
.type-llm { background: var(--ivory-200); color: var(--ink-600) !important; border: 1px solid var(--ink-300); }
.type-rule { background: var(--ink-900); color: var(--ivory-50) !important; border: 1px solid var(--ink-900); }
/* Tabular figures everywhere clinical numbers appear (tooth #, pain x/10,
dates, counts) so columns and updating values stay aligned — a quiet but
strong "this was designed" signal. */
.doc-band, .doc-meta, .patient-line, .doc-badge, .tier-pill, .agent-status,
.workflow-route, .agent-type, .privacy-pill, .doc-body, .mono-value {
font-variant-numeric: tabular-nums;
}
/* ---------- Loading skeleton (handoff cold-start) ---------- */
@keyframes skelShimmer { 0% { background-position: -240px 0; } 100% { background-position: 240px 0; } }
.skel-line, .skel-h3 {
background: linear-gradient(90deg, var(--ivory-200) 25%, var(--ivory-300) 50%, var(--ivory-200) 75%);
background-size: 240px 100%;
animation: skelShimmer 1.25s linear infinite;
border-radius: 4px;
}
.skel-line { height: 11px; margin: 7px 0; }
.skel-sec { margin-top: 15px; }
.skel-sec .skel-line:last-child { width: 62%; }
.skel-h3 { height: 8px; width: 116px; margin-bottom: 10px; }
.skel-title, .skel-badge {
background: linear-gradient(90deg, rgba(251,250,247,0.14) 25%, rgba(251,250,247,0.34) 50%, rgba(251,250,247,0.14) 75%);
background-size: 240px 100%;
animation: skelShimmer 1.25s linear infinite;
}
.skel-title { height: 16px; width: 172px; margin-top: 8px; border-radius: 4px; }
.skel-badges { display: flex; gap: 6px; }
.skel-badge { width: 62px; height: 17px; border-radius: 9999px; }
.skel-note {
font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em;
text-transform: uppercase; color: var(--ink-400); margin-bottom: 4px;
}
.agent-status {
font-family: var(--font-mono);
font-size: 9.5px;
padding: 3px 7px;
border-radius: var(--radius-xs);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
display: inline-block;
min-width: 64px;
text-align: center;
white-space: nowrap;
}
/* Workflow statuses — neutral ink system (process info, not safety):
intensity encodes state. No green, no red, no off-palette hues.
color is !important: the live Space SSR bundle wins cascade ties and
repaints chip text ink-on-ink (Cached/Passed/flag-count unreadable). */
.status-idle { background: var(--surface-container); color: var(--muted) !important; border: 1px solid var(--outline-variant); }
.status-ready { background: var(--ivory-50); color: var(--ink-700) !important; border: 1px solid var(--ink-300); }
.status-active { background: var(--ink-700); color: var(--ivory-50) !important; border: 1px solid var(--ink-700); }
.status-complete { background: var(--ink-900); color: var(--ivory-50) !important; border: 1px solid var(--ink-900); }
.status-bypass { background: var(--ivory-200); color: var(--ink-600) !important; border: 1px dashed var(--ink-300); }
.status-template { background: var(--ivory-50); color: var(--ink-500) !important; border: 1px dashed var(--ivory-400); }
@keyframes agentPulse {
0% { opacity: 0.70; }
50% { opacity: 1.00; transform: scale(1.02); }
100% { opacity: 0.70; }
}
/* ---------- Tabs styling ---------- */
.gradio-container .tab-nav button {
font-family: var(--font-body) !important;
font-weight: 500 !important;
font-size: 13.5px !important;
color: var(--muted) !important;
background: transparent !important;
border: none !important;
border-bottom: 2px solid transparent !important;
border-radius: 0 !important;
padding: 10px 16px !important;
box-shadow: none !important;
transition: color 0.2s ease, border-color 0.2s ease !important;
}
.gradio-container .tab-nav button.selected,
.gradio-container .tab-nav button[aria-selected="true"] {
color: var(--ink-900) !important;
border-bottom-color: var(--ink-900) !important;
font-weight: 600 !important;
background: transparent !important;
box-shadow: none !important;
}
.gradio-container .tab-nav button:hover:not(.selected) {
color: var(--on-surface) !important;
background: var(--surface-container-low) !important;
}
/* ---------- Chatbot bubbles ---------- */
/* Guided interview identity and progress */
.interview-guide-card {
background: var(--ivory-50);
border: 1px solid var(--ivory-300);
border-radius: var(--radius);
box-shadow: var(--shadow-card);
padding: 16px 18px 14px;
margin-bottom: 10px;
}
.guide-identity {
display: flex;
align-items: center;
gap: 11px;
}
.guide-mini-avatar {
width: 38px;
height: 38px;
border-radius: 12px;
display: grid;
place-items: center;
flex: none;
color: var(--ivory-50);
background: var(--ink-900);
box-shadow: var(--shadow-card);
}
.guide-mini-avatar .material-symbols-outlined { font-size: 23px; }
.guide-name {
font-family: var(--font-headline);
font-weight: 600;
font-size: 14px;
color: var(--ink);
}
.guide-role {
color: var(--muted);
font-size: 11.5px;
margin-top: 1px;
}
.guide-stage {
margin-left: auto;
color: var(--ink-700);
background: var(--ivory-200);
border-radius: 9999px;
padding: 5px 10px;
font-size: 11.5px;
font-weight: 600;
white-space: nowrap;
}
.interview-progress-track {
height: 5px;
overflow: hidden;
border-radius: 9999px;
background: var(--surface-container);
margin: 13px 0 11px;
}
.interview-progress-track span {
display: block;
height: 100%;
border-radius: inherit;
background: var(--ink-900);
transition: width 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
.odipara-heading {
color: var(--muted);
font-family: var(--font-mono);
font-size: 9.5px;
font-weight: 500;
letter-spacing: 0.10em;
text-transform: uppercase;
margin-bottom: 6px;
}
.odipara-row {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.odipara-chip {
display: inline-flex;
align-items: center;
gap: 5px;
border: 1px solid var(--outline-variant);
border-radius: 9999px;
padding: 4px 8px 4px 5px;
color: var(--muted);
background: var(--ivory-50);
font-size: 10.5px;
font-weight: 600;
}
.odipara-letter {
width: 18px;
height: 18px;
display: grid;
place-items: center;
border-radius: 9999px;
background: var(--surface-container);
color: var(--muted);
font-size: 9px;
font-weight: 600;
font-family: var(--font-mono);
}
.odipara-chip.current {
color: var(--ink-900);
border-color: var(--ink-300);
background: var(--ivory-200);
}
.odipara-chip.current .odipara-letter {
color: var(--ivory-50);
background: var(--ink-900);
}
.odipara-chip.covered {
color: var(--ink-600);
border-color: var(--ivory-400);
background: var(--ivory-100);
}
.odipara-chip.covered .odipara-letter {
color: var(--ivory-50);
background: var(--ink-500);
}
.guide-guidance {
color: var(--muted);
font-size: 11.5px;
line-height: 1.45;
margin: 9px 0 0;
}
/* Assistant bubbles: sunken warm tint on left */
.gradio-container .message.bot,
.gradio-container .message.assistant {
background: var(--ivory-200) !important;
border: 1px solid var(--ivory-300) !important;
border-radius: 0 var(--radius) var(--radius) var(--radius) !important;
font-family: var(--font-body) !important;
font-size: 14px !important;
color: var(--ink) !important;
}
/* User bubbles: paper, right-aligned */
.gradio-container .message.user,
.gradio-container .message.human {
background: var(--surface-container-lowest) !important;
border: 1px solid var(--outline-variant) !important;
border-radius: var(--radius) 0 var(--radius) var(--radius) !important;
font-family: var(--font-body) !important;
font-size: 14px !important;
color: var(--ink) !important;
}
/* Chatbot container */
#interview-chat {
background: var(--ivory-100) !important;
border: 1px solid var(--outline-variant) !important;
border-radius: var(--radius) !important;
}
#interview-chat img[alt="avatar"],
#interview-chat .avatar-container img {
border-radius: 12px !important;
box-shadow: var(--shadow-card);
}
/* Gradio 6 leaves this pending bubble behind when unrelated example buttons
update the page. Interview requests still use the top-level progress bar. */
#interview-chat [role="status"][aria-label="Loading response"] {
display: none !important;
}
/* ---------- Checkbox groups as interactive tiles ---------- */
.gradio-container .checkbox-group label,
.gradio-container .radio-group label {
font-family: var(--font-body) !important;
font-size: 13.5px !important;
color: var(--on-surface-variant) !important;
padding: 8px 12px !important;
border-radius: var(--radius-xs) !important;
transition: background 0.15s ease, color 0.15s ease !important;
cursor: pointer;
}
.gradio-container .checkbox-group label:hover,
.gradio-container .radio-group label:hover {
background: var(--surface-container-low) !important;
color: var(--ink-900) !important;
}
/* Checked state: ink emphasis */
.gradio-container .checkbox-group input:checked + span,
.gradio-container .checkbox-group label:has(input:checked),
.gradio-container .radio-group input:checked + span {
color: var(--ink-900) !important;
font-weight: 600 !important;
}
/* Checkbox accent color */
.gradio-container input[type="checkbox"],
.gradio-container input[type="radio"] {
accent-color: var(--ink-900) !important;
}
/* ---------- Slider ---------- */
.gradio-container input[type="range"] {
accent-color: var(--ink-900) !important;
}
/* ---------- Section separators via whitespace ---------- */
.gradio-container .gap-6, .gradio-container .gap-4 { gap: 1.5rem !important; }
/* ---------- Run-state polish (pending / generating) ---------- */
/* Gradio fades in-flight output components to opacity .2. During a ZeroGPU
cold start that can last a minute, the whole artifact column reads as a
broken, washed-out page. Keep pending content readable. */
.gradio-container .pending {
opacity: 0.75 !important;
transition: opacity 0.25s ease;
}
/* Gradio's generating pulse ships as a hard 2px accent frame. Re-brand it:
thin, ink, rounded to match the card radius. */
.gradio-container .generating {
border: 1.5px solid var(--ink-300) !important;
border-radius: var(--radius-sm) !important;
}
/* ---------- Accordion chrome (Validated handoff JSON) ---------- */
/* NOTE: deliberately placed AFTER the generic `button:not(.primary)` rule —
both carry !important, so source order breaks the tie. Keep this below it. */
.gradio-container button.label-wrap {
font-family: var(--font-body) !important;
font-size: 12.5px !important;
font-weight: 600 !important;
color: var(--muted) !important;
background: var(--surface-container-low) !important;
border: 1px solid var(--outline-variant) !important;
border-radius: var(--radius-sm) !important;
padding: 9px 12px !important;
transition: background 0.15s ease, color 0.15s ease;
}
.gradio-container button.label-wrap:hover {
color: var(--ink-900) !important;
background: var(--surface-container) !important;
}
/* ---------- Keyboard focus ring — neutral ink, never amber ---------- */
.gradio-container :focus-visible {
outline: 2px solid rgba(13, 13, 13, 0.55) !important;
outline-offset: 2px;
}
/* ---------- Print: only the document zone ---------- */
@media print {
body * { visibility: hidden; }
.print-zone, .print-zone * { visibility: visible; }
.print-zone { position: absolute; left: 0; top: 0; width: 100%; }
.print-zone .no-print, .print-zone .no-print * { visibility: hidden; display: none !important; }
.handoff-card {
box-shadow: none;
border: 1px solid #D2CEC2;
background: #fff;
}
.safety-panel, .interrupt-card { backdrop-filter: none; -webkit-backdrop-filter: none; }
/* Inline literal so Safari print engine renders correctly */
.doc-band { background: #0D0D0D !important; }
.gradio-container { padding-bottom: 0 !important; }
.disclaimer-bar { display: none; }
/* Patient-safety: never split a flag or the handoff sections across a page */
.flag, .interrupt-card, .safety-panel, .skel-sec { page-break-inside: avoid; }
/* Clinical print legibility (web px ≈ too small on paper) */
.doc-body p, .doc-body li { font-size: 11pt; line-height: 1.55; }
.doc-body h3 { font-size: 8pt; }
.doc-foot, .doc-foot-meta { font-size: 9pt; }
/* Swap the interactive disclosure for its static print twin: a printed flag
must still show the rule id and the patient's triggering words. */
.flag-rule { display: none; }
.flag-evidence-print { display: block; }
.flag-evidence-print .ev-span { font-size: 10pt; }
/* Never let the reveal animation leave a half-faded card on paper. */
.handoff-card, .safety-panel, .interrupt-card { animation: none; }
}
/* ---------- Responsive ---------- */
@media (max-width: 900px) {
.app-header { flex-direction: column; align-items: flex-start; }
.privacy-pill { text-transform: none; }
.agent-list { grid-template-columns: 1fr; }
.doc-meta { text-align: left; }
.disclaimer-bar { font-size: 11.5px; padding: 10px 12px; margin-bottom: 16px; }
.doc-band .artifact { font-size: 18px; }
.interrupt-band { font-size: 15px; }
.step-card { padding: 16px 14px 12px !important; }
}
@media (max-width: 560px) {
.disclaimer-bar { display: block; }
.disclaimer-kicker { display: inline-block; margin-bottom: 7px; }
.agent-dashboard-head { flex-direction: column; }
.workflow-route { align-self: flex-start; }
.agent-row { align-items: flex-start; }
.agent-status { min-width: 58px; }
}
/* ---------- Bilingual side-by-side (the bilingual money shot) ---------- */
.bilingual-wrap {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
align-items: start;
}
@media (max-width: 900px) {
.bilingual-wrap { grid-template-columns: 1fr; }
}
@media print {
/* One card per printed page; never squeeze two columns onto A4 */
.bilingual-wrap { display: block; }
.bilingual-wrap .handoff-card.arabic { page-break-before: always; margin-top: 14px; }
}
/* ---------- Mobile layout: stack the input/output columns ---------- */
@media (max-width: 768px) {
/* Gradio's default column min-width (320px) cannot fit twice on a phone —
force the two top-level columns to stack instead of colliding. */
.gradio-container .row > .column,
.gradio-container .row > [class*="column"] {
min-width: 100% !important;
flex: 1 1 100% !important;
}
/* 44px minimum touch targets on the primary demo path */
.example-chip { min-height: 44px !important; }
.no-print button { min-height: 44px !important; }
.rail-chip { padding: 9px 14px; }
.guide-identity { align-items: flex-start; }
.guide-stage {
white-space: normal;
text-align: right;
max-width: 42%;
}
.odipara-chip { font-size: 10px; }
}