Claims denied showed Ava's affected_claims (225) while "See the claims" opened 237 rows — her quantify query and the link's filters are two different queries. The tile now asks the record view for the link's own filter set (limit 1, pagination.total_count) and shows that; her figure appears underneath only when it disagrees. The URL→filter mapping moves to lib/claimsLink so the table and the tile share one definition. While a review runs the trace is the page: the layout widens to the screen, the list takes the viewport height, follows the newest step unless the viewer scrolled up, and shows a working row with a spinner and an indeterminate strip under the header. Reduced motion honoured. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
276 lines
9.0 KiB
CSS
276 lines
9.0 KiB
CSS
@import 'tailwindcss';
|
|
|
|
/* Northlake Health — Denials Desk. Light and dark.
|
|
=====================================================================
|
|
HOW THEMING WORKS HERE
|
|
|
|
`@theme` below defines the LIGHT theme, and light is also the fallback:
|
|
every token has a value here, so a document with no `data-theme` renders
|
|
correctly rather than unstyled. The dark theme is the
|
|
`:root[data-theme='dark']` block further down, which redefines the SAME
|
|
token names. Tailwind v4 compiles `bg-panel` to
|
|
`background-color: var(--color-panel)`, so overriding the variable swaps the
|
|
entire app — including opacity modifiers like `bg-brand/10`, which compile to
|
|
a `color-mix()` over the same var.
|
|
|
|
Consequently: NEVER put a literal colour in a component. A hex in a className
|
|
is invisible to the toggle and will be wrong in one of the two themes.
|
|
|
|
THE ONE RULE THAT CARRIES THIS APP'S MEANING
|
|
|
|
**Violet is the AI, and nothing else is violet.** Every number, sentence and
|
|
panel that Ava produced is tinted with it; everything a person or a rule
|
|
produced is not. On a screen whose whole claim is "an agent did this work and
|
|
you can check it", the reader has to be able to tell the two apart without
|
|
reading a label. Red is reserved for a case that needs a human and for a
|
|
reject — so when red appears in the queue it means something.
|
|
|
|
Every colour used for text is checked at WCAG AA against the surfaces it
|
|
actually appears on, in both themes. */
|
|
@theme {
|
|
/* --- Structural ---------------------------------------------------- */
|
|
--color-ink: #0f2233; /* primary text */
|
|
--color-muted: #4a6076; /* secondary text */
|
|
--color-faint: #5a7089; /* labels, placeholders, dashes */
|
|
--color-bg: #f2f7f8; /* page canvas — a cool clinical tint */
|
|
--color-panel: #ffffff; /* card face */
|
|
--color-panel2: #f1f6f8; /* input fill, row hover */
|
|
--color-panel3: #e3eef1; /* pressed / hover on panel2 */
|
|
--color-line: #d5e4e8; /* borders */
|
|
|
|
/* Brand accent — a clinical teal. Used BOTH as a fill and as text, so it
|
|
carries a companion for whatever sits on top of it. */
|
|
--color-brand: #0e7c86;
|
|
--color-brand-fg: #ffffff;
|
|
--color-brand-hover: #0b636b;
|
|
|
|
/* Fixed in both themes: the mark's own colours. */
|
|
--color-navy: #0e7c86;
|
|
--color-signal: #c2333a; /* needs-a-human and reject */
|
|
--color-signal-hover: #9f262c;
|
|
|
|
/* --- Semantic tones ------------------------------------------------
|
|
Six meanings, three roles each: `<tone>` is text, `<tone>-soft` is the chip
|
|
wash, `<tone>-edge` is the ring. Every `<tone>` passes AA on its own wash
|
|
AND on the page and panel, because these are used both inside a badge and
|
|
as bare text (a stat value, a rate). */
|
|
--color-idle: #55504a;
|
|
--color-idle-soft: #f2f0ed;
|
|
--color-idle-edge: #dad4cc;
|
|
|
|
/* THE most important tone in this app. Amber is Awaiting Review — the pile a
|
|
human owns — and it is deliberately the only amber in the pipeline, so it
|
|
reads as "needs you" without reading as an error. Findings waiting for a
|
|
decision are not a problem; they are the product. */
|
|
--color-warn: #8a5a00;
|
|
--color-warn-soft: #fff8e8;
|
|
--color-warn-edge: #f0c368;
|
|
|
|
--color-info: #0f5c78;
|
|
--color-info-soft: #e7f2f6;
|
|
--color-info-edge: #a4cadb;
|
|
|
|
--color-good: #00713d;
|
|
--color-good-soft: #e4f6ec;
|
|
--color-good-edge: #93d3b0;
|
|
|
|
--color-bad: #b31e23;
|
|
--color-bad-soft: #fdeced;
|
|
--color-bad-edge: #eda9ac;
|
|
|
|
/* The AI's own colour — see the rule at the top. */
|
|
--color-ai: #5b2ca0;
|
|
--color-ai-soft: #f5f1fd;
|
|
--color-ai-edge: #c3adec;
|
|
|
|
--color-scrim: rgb(6 28 40 / 0.45);
|
|
|
|
/* Inter: a screen typeface for dense tabular work, self-hosted through
|
|
@fontsource-variable/inter (imported in main.tsx) rather than a CDN — this
|
|
build is promoted between environments and dropped behind hospital
|
|
proxies, and a font fetched from fonts.googleapis.com is one that
|
|
sometimes does not arrive. */
|
|
--font-sans:
|
|
'Inter Variable', Inter, system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
|
|
--font-mono:
|
|
ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
|
|
}
|
|
|
|
/* Non-Tailwind vars: consumed by the hand-rolled rules at the bottom rather
|
|
than by generated utilities, so they live outside `@theme`. */
|
|
:root {
|
|
/* Tinted with navy rather than black, so the lift reads as part of the
|
|
palette. In dark it goes near-black and deeper — a navy shadow on a
|
|
navy canvas is invisible, so dark leans on the border instead. */
|
|
--lift: 0 1px 2px rgb(6 40 52 / 0.07), 0 1px 1px rgb(6 40 52 / 0.04);
|
|
--lift-lg: 0 12px 32px rgb(6 40 52 / 0.16), 0 2px 8px rgb(6 40 52 / 0.08);
|
|
--sb-thumb: #c4d8dd;
|
|
--sb-thumb-hover: #a6c2c9;
|
|
/* Makes the browser render native widgets — date picker, select dropdown,
|
|
scrollbar gutter — to match. Without it a dark console opens a blinding
|
|
white calendar. */
|
|
color-scheme: light;
|
|
}
|
|
|
|
/* =====================================================================
|
|
DARK — the navy-led palette. Only tokens are redefined; no utility,
|
|
component or layout rule is duplicated.
|
|
===================================================================== */
|
|
:root[data-theme='dark'] {
|
|
--color-ink: #e7f1f5;
|
|
--color-muted: #a3c0cc;
|
|
--color-faint: #93b2c0;
|
|
--color-bg: #07161f;
|
|
--color-panel: #10233a;
|
|
--color-panel2: #17304a;
|
|
--color-panel3: #1f3d59;
|
|
--color-line: #22405c;
|
|
|
|
/* The accent lightens and the text on it darkens — the inverse of light. A
|
|
#0E7C86 fill would sink into the canvas, and #0E7C86 text on it is
|
|
unreadable. */
|
|
--color-brand: #4fc3cd;
|
|
--color-brand-fg: #06202a;
|
|
--color-brand-hover: #6fd2da;
|
|
|
|
/* The signal red is unchanged: white on it clears AA either way, and it
|
|
still clears 3:1 against the dark canvas as a large component. */
|
|
--color-signal-hover: #e8686e;
|
|
|
|
--color-idle: #d6cdbf;
|
|
--color-idle-soft: #2a2724;
|
|
--color-idle-edge: #4b453b;
|
|
|
|
--color-warn: #f7cd7a;
|
|
--color-warn-soft: #3a2a06;
|
|
--color-warn-edge: #8a681f;
|
|
|
|
--color-info: #9fd0e2;
|
|
--color-info-soft: #0d3a4c;
|
|
--color-info-edge: #3f7f99;
|
|
|
|
--color-good: #5fd694;
|
|
--color-good-soft: #052a18;
|
|
--color-good-edge: #10683b;
|
|
|
|
--color-bad: #f4a3a6;
|
|
--color-bad-soft: #3c1416;
|
|
--color-bad-edge: #8c3a3d;
|
|
|
|
--color-ai: #c9b3f2;
|
|
--color-ai-soft: #221542;
|
|
--color-ai-edge: #5a3fa2;
|
|
|
|
--color-scrim: rgb(2 10 14 / 0.66);
|
|
|
|
--lift: 0 1px 2px rgb(0 0 0 / 0.4);
|
|
--lift-lg: 0 16px 40px rgb(0 0 0 / 0.55), 0 2px 8px rgb(0 0 0 / 0.4);
|
|
--sb-thumb: #1e4a5e;
|
|
--sb-thumb-hover: #2a6376;
|
|
color-scheme: dark;
|
|
}
|
|
|
|
html,
|
|
body,
|
|
#root {
|
|
height: 100%;
|
|
}
|
|
body {
|
|
margin: 0;
|
|
background: var(--color-bg);
|
|
color: var(--color-ink);
|
|
font-family: var(--font-sans);
|
|
font-size: 15px;
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
/* Tabular figures everywhere numbers line up in a column — amounts,
|
|
ratios, file numbers. Proportional digits make a money table look
|
|
ragged, and this app is mostly money. */
|
|
.tnum {
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
::-webkit-scrollbar {
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
::-webkit-scrollbar-thumb {
|
|
background: var(--sb-thumb);
|
|
border-radius: 6px;
|
|
}
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: var(--sb-thumb-hover);
|
|
}
|
|
|
|
@keyframes nl-pulse {
|
|
0%,
|
|
100% {
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
opacity: 0.35;
|
|
}
|
|
}
|
|
.live-dot {
|
|
animation: nl-pulse 1.6s ease-in-out infinite;
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.live-dot {
|
|
animation: none;
|
|
}
|
|
}
|
|
|
|
.lift {
|
|
box-shadow: var(--lift);
|
|
}
|
|
.lift-lg {
|
|
box-shadow: var(--lift-lg);
|
|
}
|
|
|
|
/* A SQL statement, as Ava sent it. Monospace, wrapped rather than scrolled —
|
|
these are read, not edited, and a horizontal scrollbar inside a timeline row
|
|
hides the end of the query, which is usually the interesting part. */
|
|
.sql {
|
|
font-family: var(--font-mono);
|
|
font-size: 11.5px;
|
|
line-height: 1.55;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
}
|
|
|
|
/* Review in progress: an indeterminate strip under the trace header, and
|
|
three breathing dots on the "working" row. Both honour reduced motion. */
|
|
@keyframes nl-slide {
|
|
0% { transform: translateX(-100%); }
|
|
100% { transform: translateX(300%); }
|
|
}
|
|
.progress-strip {
|
|
position: relative;
|
|
height: 3px;
|
|
overflow: hidden;
|
|
background: color-mix(in srgb, var(--color-ai) 18%, transparent);
|
|
}
|
|
.progress-strip::after {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0 auto 0 0;
|
|
width: 33%;
|
|
background: var(--color-ai);
|
|
animation: nl-slide 1.8s ease-in-out infinite;
|
|
}
|
|
.working-dots { display: inline-flex; gap: 4px; align-items: center; }
|
|
.working-dots i {
|
|
width: 6px; height: 6px; border-radius: 9999px;
|
|
background: var(--color-ai);
|
|
animation: nl-pulse 1.2s ease-in-out infinite;
|
|
}
|
|
.working-dots i:nth-child(2) { animation-delay: 0.2s; }
|
|
.working-dots i:nth-child(3) { animation-delay: 0.4s; }
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.progress-strip::after, .working-dots i { animation: none; }
|
|
}
|