Phase 3a: shadcn init + Home/Case List view

Initialize shadcn/ui (radix-nova preset) and wire its semantic tokens
to the editorial navy/cream/gold palette so primitives inherit the
judicial voice without per-component overrides.

Replace the Phase 2 live-probe with a real dashboard: KPI tiles,
conic-gradient status donut (ported from the vanilla renderHero),
and a TanStack Table cases list with search + sort. Add useCase(n)
hook with 5s staleTime/refetchInterval to replace the old manual
polling loop when Case Detail ships next.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-11 16:04:30 +00:00
parent 0ee8e723bd
commit 51064f3a03
25 changed files with 6513 additions and 247 deletions

View File

@@ -1,10 +1,18 @@
@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";
@custom-variant dark (&:is(.dark *));
/* ══════════════════════════════════════════════════════════════
* Ezer Mishpati — Design Tokens (ported from legal-ai/web/static/design-system.css)
* Ezer Mishpati — Design Tokens
* Ported from legal-ai/web/static/design-system.css.
* Editorial/judicial aesthetic for a Hebrew RTL judicial tool.
* Palette: Navy + Cream + Gold.
* Typography: Heebo (loaded via next/font/google in layout.tsx).
* Palette: Navy + Cream + Gold. Typography: Heebo.
*
* shadcn semantic tokens (background, foreground, primary, etc.)
* are mapped onto this palette in the @theme inline block + :root
* further down, so shadcn primitives inherit the editorial voice.
* ══════════════════════════════════════════════════════════════ */
@theme {
@@ -44,18 +52,12 @@
--color-info: #4e6a8c;
--color-info-bg: #e6ecf3;
/* ── Typography — Heebo (loaded via next/font in layout.tsx) ─ */
/* ── Typography ─────────────────────────────────────── */
--font-sans: var(--font-heebo), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-display: var(--font-heebo), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-body: var(--font-heebo), -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-mono: ui-monospace, "Cascadia Code", "SF Mono", Menlo, monospace;
/* ── Radii ──────────────────────────────────────────── */
--radius-sm: 4px;
--radius: 6px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
/* ── Shadows — soft, editorial ──────────────────────── */
--shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.05);
--shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04);
@@ -68,9 +70,93 @@
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
}
/* ── Dark theme (class-based, toggled on <html> or <body>) ── */
:root.dark,
body.dark {
/* shadcn/ui semantic tokens — wired to CSS vars defined in :root / .dark */
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--font-heading: var(--font-display);
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
}
/*
* shadcn token values — mapped onto the editorial palette.
* background/foreground/primary all reference the navy-cream-gold system
* so shadcn primitives (Button, Card, Badge…) fit the judicial tone
* without per-component overrides.
*/
:root {
--radius: 0.5rem;
--background: var(--color-bg); /* cream */
--foreground: var(--color-ink); /* near-black */
--card: var(--color-surface); /* white */
--card-foreground: var(--color-ink);
--popover: var(--color-surface);
--popover-foreground: var(--color-ink);
--primary: var(--color-navy); /* navy buttons */
--primary-foreground: var(--color-parchment);
--secondary: var(--color-cream-deep);
--secondary-foreground: var(--color-navy);
--muted: var(--color-rule-soft);
--muted-foreground: var(--color-ink-muted);
--accent: var(--color-gold-wash); /* subtle gold backgrounds */
--accent-foreground: var(--color-gold-deep);
--destructive: var(--color-danger);
--border: var(--color-rule);
--input: var(--color-rule);
--ring: var(--color-gold); /* gold focus ring */
--chart-1: var(--color-navy);
--chart-2: var(--color-gold);
--chart-3: var(--color-info);
--chart-4: var(--color-success);
--chart-5: var(--color-warn);
--sidebar: var(--color-parchment);
--sidebar-foreground: var(--color-ink);
--sidebar-primary: var(--color-navy);
--sidebar-primary-foreground: var(--color-parchment);
--sidebar-accent: var(--color-gold-wash);
--sidebar-accent-foreground: var(--color-gold-deep);
--sidebar-border: var(--color-rule);
--sidebar-ring: var(--color-gold);
}
/* Dark theme — class-based, toggled on <html> or <body>.
Inverts navy ↔ parchment and keeps gold as accent. */
.dark {
--color-navy: #f5f1e8;
--color-navy-soft: #e8e0c8;
--color-navy-dim: #c7bc9a;
@@ -106,73 +192,57 @@ body.dark {
--color-info-bg: rgba(109, 139, 171, 0.12);
}
/* ── Base overrides ──────────────────────────────────── */
/* ══════════════════════════════════════════════════════════════
* Base layer — editorial typography + Hebrew prose defaults
* ══════════════════════════════════════════════════════════════ */
html {
font-size: 16px;
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
font-family: var(--font-body);
font-weight: 400;
font-size: 0.95rem;
line-height: 1.65;
color: var(--color-ink);
background: var(--color-bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-feature-settings: "kern", "liga", "clig", "calt";
}
html {
font-size: 16px;
}
/* Display typography — headings use the same Heebo family but bolder */
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: var(--font-display);
font-weight: 700;
line-height: 1.25;
color: var(--color-navy);
letter-spacing: -0.01em;
}
body {
@apply bg-background text-foreground;
font-family: var(--font-body);
font-weight: 400;
font-size: 0.95rem;
line-height: 1.65;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-feature-settings: "kern", "liga", "clig", "calt";
}
h1 { font-size: 2.3rem; font-weight: 900; }
h2 { font-size: 1.8rem; }
h3 { font-size: 1.45rem; }
h4 { font-size: 1.2rem; }
h5 { font-size: 1.05rem; }
h6 { font-size: 0.95rem; }
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-display);
font-weight: 700;
line-height: 1.25;
color: var(--color-navy);
letter-spacing: -0.01em;
}
/* Prose paragraphs — justify both sides for Hebrew legal text */
p.prose,
.prose p {
text-align: justify;
text-justify: inter-word;
hyphens: auto;
line-height: 1.65;
}
h1 { font-size: 2.3rem; font-weight: 900; }
h2 { font-size: 1.8rem; }
h3 { font-size: 1.45rem; }
h4 { font-size: 1.2rem; }
h5 { font-size: 1.05rem; }
h6 { font-size: 0.95rem; }
/* Links */
a {
color: var(--color-gold-deep);
text-decoration: none;
transition: color 120ms var(--ease-out);
}
a:hover {
color: var(--color-gold);
}
/* Prose paragraphs — justified for Hebrew legal text */
p.prose,
.prose p {
text-align: justify;
text-justify: inter-word;
hyphens: auto;
line-height: 1.65;
}
/* Focus rings — gold, subtle */
*:focus-visible {
outline: 2px solid var(--color-gold);
outline-offset: 2px;
border-radius: 4px;
}
/* Selection */
::selection {
background: var(--color-gold-wash);
color: var(--color-navy);
/* Selection */
::selection {
background: var(--color-gold-wash);
color: var(--color-navy);
}
}