diff --git a/web/app.py b/web/app.py index 5b30731..5f1a984 100644 --- a/web/app.py +++ b/web/app.py @@ -69,6 +69,11 @@ async def index(): return FileResponse(STATIC_DIR / "index.html") +@app.get("/design-system.css") +async def design_system_css(): + return FileResponse(STATIC_DIR / "design-system.css", media_type="text/css") + + @app.post("/api/upload") async def upload_file(file: UploadFile = File(...)): """Upload a file to the temporary uploads directory.""" diff --git a/web/static/design-system.css b/web/static/design-system.css new file mode 100644 index 0000000..72ffcf9 --- /dev/null +++ b/web/static/design-system.css @@ -0,0 +1,244 @@ +/* ════════════════════════════════════════════════════════════ + * Ezer Mishpati — Design System + * Editorial/Judicial aesthetic for a Hebrew RTL judicial tool. + * + * Typography: Frank Ruhl Libre (display) + Assistant (body) + * Palette: Navy #0f172a + Cream #f5f1e8 + Gold #a97d3a + * ════════════════════════════════════════════════════════════ */ + +@import url('https://fonts.googleapis.com/css2?family=Frank+Ruhl+Libre:wght@400;500;700;900&family=Assistant:wght@300;400;500;600;700&display=swap'); + +:root { + /* ── Colors ─────────────────────────────────────────── */ + --color-navy: #0f172a; + --color-navy-soft: #1e293b; + --color-navy-dim: #334155; + + --color-cream: #f5f1e8; + --color-cream-deep: #ede8d8; + --color-parchment: #fbf8f0; + + --color-gold: #a97d3a; + --color-gold-deep: #8b6428; + --color-gold-soft: #c89a56; + --color-gold-wash: #fdf6e8; + + --color-ink: #1a1a2e; + --color-ink-soft: #3a3a52; + --color-ink-muted: #6b7280; + --color-ink-light: #9ca3af; + + --color-rule: #e5dfd0; /* cream-toned hairline */ + --color-rule-soft: #f0ead8; + + --color-surface: #ffffff; + --color-surface-raised: #fbf8f0; + --color-bg: var(--color-cream); + + /* Status colors — tuned to the palette */ + --color-success: #4a7c59; + --color-success-bg: #e8efe7; + --color-warn: #b8894a; + --color-warn-bg: #faf0dc; + --color-danger: #a54242; + --color-danger-bg: #f5e6e6; + --color-info: #4e6a8c; + --color-info-bg: #e6ecf3; + + /* ── Typography ─────────────────────────────────────── */ + --font-display: 'Frank Ruhl Libre', 'David Libre', Georgia, serif; + --font-body: 'Assistant', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + --font-mono: ui-monospace, 'Cascadia Code', 'SF Mono', Menlo, monospace; + + --text-xs: 0.75rem; + --text-sm: 0.85rem; + --text-base: 0.95rem; + --text-md: 1.05rem; + --text-lg: 1.2rem; + --text-xl: 1.45rem; + --text-2xl: 1.8rem; + --text-3xl: 2.3rem; + --text-4xl: 2.9rem; + + --leading-tight: 1.25; + --leading-snug: 1.4; + --leading-body: 1.65; + --leading-prose: 1.8; + + --weight-light: 300; + --weight-normal: 400; + --weight-medium: 500; + --weight-semi: 600; + --weight-bold: 700; + --weight-display: 900; + + /* ── Spacing scale (8px grid) ───────────────────────── */ + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 20px; + --space-6: 24px; + --space-7: 32px; + --space-8: 40px; + --space-9: 56px; + --space-10: 72px; + + /* ── Radii ──────────────────────────────────────────── */ + --radius-sm: 4px; + --radius: 6px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-xl: 16px; + --radius-pill: 999px; + + /* ── 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); + --shadow: 0 2px 6px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04); + --shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08), 0 2px 4px rgba(15, 23, 42, 0.04); + --shadow-lg: 0 10px 30px rgba(15, 23, 42, 0.12), 0 2px 6px rgba(15, 23, 42, 0.05); + --shadow-gold: 0 0 0 3px var(--color-gold-wash); + + /* ── Transitions ────────────────────────────────────── */ + --ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); + --t-fast: 120ms var(--ease-out); + --t: 180ms var(--ease-out); + --t-slow: 280ms var(--ease-out); +} + +/* ── Base overrides ──────────────────────────────────── */ + +html { font-size: 16px; } + +body { + font-family: var(--font-body); + font-weight: var(--weight-normal); + font-size: var(--text-base); + line-height: var(--leading-body); + color: var(--color-ink); + background: var(--color-bg); + direction: rtl; + text-align: right; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-feature-settings: "kern", "liga", "clig", "calt"; +} + +/* Display typography — serif for headings */ +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-display); + font-weight: var(--weight-bold); + line-height: var(--leading-tight); + color: var(--color-navy); + letter-spacing: -0.01em; +} + +h1 { font-size: var(--text-3xl); font-weight: var(--weight-display); } +h2 { font-size: var(--text-2xl); } +h3 { font-size: var(--text-xl); } +h4 { font-size: var(--text-lg); } +h5 { font-size: var(--text-md); } +h6 { font-size: var(--text-base); } + +/* Prose paragraphs — justify both sides for Hebrew legal text */ +p, +.prose { + text-align: justify; + text-justify: inter-word; + hyphens: auto; + line-height: var(--leading-body); +} + +/* Text that should NOT justify (short labels, meta) */ +.no-justify, .meta, .label, .caption, +th, td, button, input, select, label, nav { + text-align: right; +} + +/* Links */ +a { + color: var(--color-gold-deep); + text-decoration: none; + transition: color var(--t-fast); +} +a:hover { color: var(--color-gold); } + +/* Focus rings — gold, subtle */ +*:focus-visible { + outline: 2px solid var(--color-gold); + outline-offset: 2px; + border-radius: var(--radius-sm); +} + +/* Selection */ +::selection { + background: var(--color-gold-wash); + color: var(--color-navy); +} + +/* ── Utility classes ─────────────────────────────────── */ + +.text-display { font-family: var(--font-display); } +.text-body { font-family: var(--font-body); } +.text-mono { font-family: var(--font-mono); } + +.text-xs { font-size: var(--text-xs); } +.text-sm { font-size: var(--text-sm); } +.text-base { font-size: var(--text-base); } +.text-md { font-size: var(--text-md); } +.text-lg { font-size: var(--text-lg); } +.text-xl { font-size: var(--text-xl); } +.text-2xl { font-size: var(--text-2xl); } +.text-3xl { font-size: var(--text-3xl); } + +.text-muted { color: var(--color-ink-muted); } +.text-light { color: var(--color-ink-light); } +.text-gold { color: var(--color-gold-deep); } +.text-navy { color: var(--color-navy); } + +.weight-light { font-weight: var(--weight-light); } +.weight-normal { font-weight: var(--weight-normal); } +.weight-medium { font-weight: var(--weight-medium); } +.weight-bold { font-weight: var(--weight-bold); } + +.justify { text-align: justify; text-justify: inter-word; } +.start { text-align: right; } /* RTL start */ +.end { text-align: left; } /* RTL end */ +.center { text-align: center; } + +.ornament { + display: block; + text-align: center; + color: var(--color-gold); + font-family: var(--font-display); + letter-spacing: 0.3em; + margin: var(--space-6) 0; +} +.ornament::before { content: "❦"; font-size: 1.3em; } + +.divider { + border: 0; + height: 1px; + background: linear-gradient( + to left, + transparent 0%, + var(--color-rule) 20%, + var(--color-rule) 80%, + transparent 100% + ); + margin: var(--space-6) 0; +} + +.divider-gold { + border: 0; + height: 2px; + background: linear-gradient( + to left, + transparent 0%, + var(--color-gold) 50%, + transparent 100% + ); + margin: var(--space-6) 0; +} diff --git a/web/static/index.html b/web/static/index.html index 2b1207a..48c35b6 100644 --- a/web/static/index.html +++ b/web/static/index.html @@ -4,101 +4,402 @@