Files
cyclone/src/index.css
T
Tyler fbe9940a3f feat(ui): cohesive frontend polish — design system + per-screen refinement
Distill the UI into a single, recognizable voice: a precision
instrument for one operator on one machine. Bloomberg-coded chrome,
warm-paper detail surfaces, mono-heavy numerics, with one editorial
serif accent for moments of weight.

Design system
- Three-font stack: Geist Sans (UI), Geist Mono (data), Instrument
  Serif (editorial display). No Inter, no system fonts.
- Two surfaces: dark chrome (--background, --accent, --signal) and
  warm paper detail surfaces (--surface, --surface-ink*).
- Inbox has its own Ticker Tape terminal palette (--tt-*).
- Shared component classes: .eyebrow, .mono, .display, .surface,
  .surface-2, .row-hover, .nav-active, .kbd, .editorial, .hairline.
- --m-* token aliases for the legacy drawer components so test-
  asserted class strings keep resolving to the same hue family.

Drawers (Claim + Remit)
- Editorial display face for totals (paid/adjustment amounts).
- Color-coded money tiles: green-tinted paid card, amber-tinted
  adjustment card when non-zero, muted otherwise.
- Tabs get accent-blue underline + CSS-driven active state.
- Validation banners with proper background opacity + ring-around-
  dot success badge.
- StateHistoryTimeline: dashed border, ring around dots, ↳ prefix
  for remit ids.
- DiagnosesList, PartiesGrid, MatchedRemitCard, CasAdjustmentsPanel:
  refined typography, dashed dividers, italic descriptions, mono
  amounts, font-semibold totals, hover row tints.

Inbox Ticker Tape
- Custom RowCheckbox with sr-only input + amber accent.
- Alternating row striping, hover tints, accent rail with inset
  shadow on selection.
- Refined sparkline with glow at high values.
- BulkBar: bottom-floating bar with amber count chip, larger shadow.
- CandidateBreakdown: animated progress bars with amber gradient.
- InboxHeader: Instrument Serif headline, mono day/date stamp,
  pulsing amber status dot.

Dialogs & search
- NewClaimDialog: editorial title, mono NPI/CPT/amount fields.
- SearchBar: refined input row with mono, footer with pulsing
  loading indicator.
- KeyboardCheatsheet: monogram icon chip, hover row states, refined
  eyebrow header.

Primitives
- StatusBadge / ClaimStateBadge: per-state dot indicators.
- SelectItem: data-[highlighted]:outline tokens for keyboard a11y.
- Table primitives: refined header treatment, hover/focus states.

Tests + build
- 354/354 tests passing across 59 files.
- Vite build clean (53.84 kB CSS / 560.86 kB JS).
- Eyebrow assertions updated to match the consolidated .eyebrow
  class (intact visual contract, abstracted class string).
- Badge variant tokens updated to the polished bg-muted/80 /
  /0.14 opacity scale.
2026-06-20 22:27:01 -06:00

416 lines
13 KiB
CSS

/* =====================================================================
Cyclone — design system
---------------------------------------------------------------------
A precision instrument for one operator on one machine. The visual
language borrows from Bloomberg terminals and Linear-grade
engineering tools: dark, hairline-strict, mono-heavy, with one
editorial serif accent for the moments that deserve weight.
---------------------------------------------------------------------
Type stack:
- Geist Sans · UI, body, controls
- Geist Mono · every number, every id, every code-like datum
- Instrument Serif · editorial display (hero numbers, headlines)
===================================================================== */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
/* ---- Core surfaces ----
A graphite canvas with a whisper of blue. Slightly warm at the
midtones (--card) to keep the chrome from feeling sterile. */
--background: 222 18% 5%; /* #0b0c10 — near-black, cool */
--foreground: 220 14% 96%; /* #f4f5f7 — paper white */
--card: 222 16% 8%; /* #11131a */
--card-foreground: 220 14% 96%;
--popover: 222 18% 9%; /* #13151c */
--popover-foreground: 220 14% 96%;
--muted: 222 12% 13%; /* #1f2229 */
--muted-foreground: 220 8% 64%; /* #9b9ea6 */
--secondary: 222 12% 13%;
--secondary-foreground: 220 14% 96%;
/* ---- Accents ----
Electric blue (primary) for actions and emphasis. Amber (signal)
is reserved for "needs attention" states and the Inbox terminal
accents — never decorative. */
--accent: 212 100% 60%; /* #2e8aff — electric, modern */
--accent-foreground: 0 0% 100%;
--signal: 36 92% 56%; /* #f3a531 — warm amber */
--primary: 212 100% 60%;
--primary-foreground: 0 0% 100%;
--destructive: 358 82% 60%; /* #ec4458 */
--destructive-foreground: 0 0% 100%;
--success: 152 64% 46%; /* #2cbd76 */
--success-foreground: 152 70% 8%;
--warning: 36 92% 56%; /* #f3a531 */
--warning-foreground: 26 80% 12%;
--border: 222 10% 16%; /* #262932 */
--input: 222 10% 16%;
--ring: 212 100% 60%;
/* ---- Sidebar ---- */
--sidebar: 222 20% 4%; /* darker than the page */
--sidebar-foreground: 220 14% 96%;
--sidebar-accent: 212 100% 60%;
--sidebar-border: 222 12% 12%;
/* ---- Document surface (drawer detail panels) ----
A faint warm paper. Keeps detail views distinct from the chrome
without going full light. Pairs with editorial serif headlines. */
--surface: 36 18% 96%; /* #f6f3ec */
--surface-ink: 30 12% 9%; /* #181614 */
--surface-ink-2: 30 6% 32%; /* #4f4b46 */
--surface-ink-3: 30 4% 52%; /* #85807a */
--surface-line: 30 14% 14%; /* #24221f */
--surface-line-soft: 30 14% 14% / 0.12;
/* ---- Ticker-tape terminal (Inbox) ----
Bloomberg-coded: three accent rails on near-black, with the
paper-tone ink for legibility. */
--tt-bg: 220 22% 5%; /* #0a0d12 */
--tt-bg-elev: 220 18% 9%; /* #13171f */
--tt-ink: 36 18% 88%; /* #e2ddd0 */
--tt-ink-dim: 220 6% 58%; /* #8e9098 */
--tt-amber: 36 88% 56%; /* #efa836 */
--tt-oxblood: 354 50% 40%; /* #8a2d35 */
--tt-ink-blue: 212 38% 36%; /* #2e4d77 */
--tt-muted: 220 4% 38%; /* #5b5d62 */
/* ---- Legacy aliases (--m-*)
Kept as thin shims so any older component still using the
"warm paper" surface family resolves to the same hue family.
New components should prefer --surface-* / --destructive / etc. */
--m-surface: hsl(var(--surface));
--m-ink-primary: hsl(var(--surface-ink));
--m-ink-secondary: hsl(var(--surface-ink-2));
--m-ink-tertiary: hsl(var(--surface-ink-3));
--m-border-heavy: hsl(var(--surface-line));
--m-font-mono: var(--font-mono);
--m-error: hsl(var(--destructive));
--m-error-bg: hsl(var(--destructive) / 0.08);
--m-success: hsl(var(--success));
--m-warning: hsl(var(--warning));
--m-accent: hsl(var(--accent));
--radius: 0.625rem; /* 10px — slightly tighter */
/* ---- Type stack ---- */
--font-sans: "Geist", ui-sans-serif, -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
--font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
--font-display: "Instrument Serif", "Iowan Old Style", "Apple Garamond", "Baskerville", "Times New Roman", serif;
}
* {
@apply border-border;
}
html, body, #root {
height: 100%;
}
body {
@apply bg-background text-foreground;
font-family: var(--font-sans);
font-feature-settings: "ss01", "cv11", "calt";
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
/* A single, precise light source anchored top-right.
One light, not two — an instrument, not a wash. */
body::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
z-index: 0;
background: radial-gradient(
55rem 35rem at 100% -15%,
hsla(212, 100%, 60%, 0.07),
transparent 62%
);
}
/* Hairline grid — the substrate of a precision instrument.
Sits behind everything, gives the dark surfaces a sense of
scale and the work a sense of "field". */
body::after {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
z-index: 0;
opacity: 0.5;
background-image:
linear-gradient(to right, hsl(222 10% 16% / 0.18) 1px, transparent 1px),
linear-gradient(to bottom, hsl(222 10% 16% / 0.18) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, black 30%, transparent 80%);
-webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, black 30%, transparent 80%);
}
/* Custom scrollbar — minimal, hairline, Apple-style */
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: hsl(var(--border));
border-radius: 999px;
border: 2px solid transparent;
background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
background: hsl(var(--muted-foreground) / 0.4);
background-clip: content-box;
}
/* Focus ring — accent, 2px, with offset.
Components override this with Tailwind focus-visible:ring-* utilities
for a ring+offset combo that matches the rest of the design system. */
:focus-visible {
outline: 2px solid hsl(var(--ring));
outline-offset: 2px;
border-radius: 6px;
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
scroll-behavior: auto !important;
}
}
}
@layer components {
/* Tabular numerals everywhere a number is shown */
.num {
font-variant-numeric: tabular-nums;
font-feature-settings: "tnum", "ss01";
}
/* Display face — Instrument Serif. Used for the moments that need
editorial weight: hero numbers, page titles in document surfaces,
and the brand mark. */
.display {
font-family: var(--font-display);
font-weight: 400;
letter-spacing: -0.015em;
font-feature-settings: "ss01";
}
/* Data mono — Geist Mono with tabular-nums. The voice of every
id, code, dollar amount, and timestamp in the app. */
.mono {
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
letter-spacing: -0.01em;
}
/* Soft card surface — a 1px hairline + a 1px inner highlight to
give the surface a sense of depth without a heavy shadow. */
.surface {
background-color: hsl(var(--card));
border: 1px solid hsl(var(--border));
box-shadow:
inset 0 1px 0 0 hsl(0 0% 100% / 0.04),
0 1px 2px 0 hsl(0 0% 0% / 0.4);
}
/* Subtle elevated surface — used for cards that should lift a
little more than the default. */
.surface-2 {
background: linear-gradient(180deg, hsl(222 16% 9%) 0%, hsl(222 16% 7.5%) 100%);
border: 1px solid hsl(var(--border));
box-shadow:
inset 0 1px 0 0 hsl(0 0% 100% / 0.05),
0 1px 3px 0 hsl(0 0% 0% / 0.5);
}
/* Hairline — a 1px separator that reads as a divider, not a border */
.hairline {
border-color: hsl(var(--border) / 0.6);
}
/* Active nav indicator — a thin left-aligned accent line with a
subtle glow that matches the instrument aesthetic. */
.nav-active {
background-color: hsl(var(--muted) / 0.6);
color: hsl(var(--foreground));
}
.nav-active::before {
content: "";
position: absolute;
left: -0.75rem;
top: 0.5rem;
bottom: 0.5rem;
width: 2px;
border-radius: 2px;
background-color: hsl(var(--accent));
box-shadow: 0 0 12px hsl(var(--accent) / 0.5);
}
/* Skip link — invisible until focused. */
.skip-link {
position: fixed;
top: 0.75rem;
left: 0.75rem;
z-index: 100;
display: inline-flex;
align-items: center;
height: 2.25rem;
padding: 0 0.875rem;
border-radius: 0.625rem;
background-color: hsl(var(--card));
color: hsl(var(--foreground));
border: 1px solid hsl(var(--border));
box-shadow:
inset 0 1px 0 0 hsl(0 0% 100% / 0.04),
0 8px 24px hsl(0 0% 0% / 0.45);
font-size: 12.5px;
font-weight: 600;
letter-spacing: 0.01em;
transform: translateY(-200%);
transition: transform 150ms ease-out;
}
.skip-link:focus-visible {
transform: translateY(0);
outline: none;
box-shadow:
inset 0 1px 0 0 hsl(0 0% 100% / 0.04),
0 0 0 2px hsl(var(--background)),
0 0 0 4px hsl(var(--ring)),
0 8px 24px hsl(0 0% 0% / 0.45);
}
@media (prefers-reduced-motion: reduce) {
.skip-link {
transition: none;
}
}
/* Keyboard hint chip */
.kbd {
display: inline-flex;
align-items: center;
gap: 0.15rem;
height: 1.375rem;
min-width: 1.375rem;
padding: 0 0.4rem;
border-radius: 0.375rem;
border: 1px solid hsl(var(--border));
background: hsl(var(--muted) / 0.5);
color: hsl(var(--muted-foreground));
font-family: var(--font-mono);
font-size: 10.5px;
font-weight: 500;
letter-spacing: 0.04em;
line-height: 1;
}
/* Section label — the all-caps eyebrow used everywhere */
.eyebrow {
font-size: 10.5px;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
color: hsl(var(--muted-foreground));
}
/* Editorial serif headline — for hero numbers and document
surfaces. A single line height that breathes. */
.editorial {
font-family: var(--font-display);
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.05;
}
/* Row hover state — slightly more refined than the default
table hover. A subtle tint + a left accent strip. */
.row-hover {
transition: background-color 150ms ease;
}
.row-hover:hover {
background-color: hsl(var(--muted) / 0.4);
}
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
.text-pretty {
text-wrap: pretty;
}
.ring-inset-hairline {
box-shadow: inset 0 0 0 1px hsl(var(--border) / 0.6);
}
}
/* ----------------------------------------------------------------------
Print styles — a clean paper-record rendering of detail drawers.
---------------------------------------------------------------------- */
@media print {
aside, header, [data-print="hide"], button[aria-label^="Close"] {
display: none !important;
}
:root {
--background: #ffffff;
--foreground: #000000;
--card: #ffffff;
--card-foreground: #000000;
--muted: #f5f5f5;
--muted-foreground: #444444;
--border: #cccccc;
--accent: #1a1a1a;
}
body {
background: #ffffff !important;
color: #000000 !important;
}
[role="dialog"] {
position: static !important;
inset: auto !important;
width: 100% !important;
max-width: 100% !important;
height: auto !important;
transform: none !important;
background: #ffffff !important;
box-shadow: none !important;
border: none !important;
}
[data-state="open"][data-radix-dialog-overlay],
.animate-fade-in,
.animate-shimmer,
.animate-scan {
animation: none !important;
transition: none !important;
}
[role="dialog"] * {
overflow: visible !important;
}
table { page-break-inside: avoid; }
section, h1, h2, h3 { page-break-after: avoid; }
*, *::before, *::after {
animation-duration: 0s !important;
}
}