diff --git a/docs/DESIGN.md b/docs/DESIGN.md
new file mode 100644
index 0000000..b2d71ac
--- /dev/null
+++ b/docs/DESIGN.md
@@ -0,0 +1,431 @@
+# ROA2WEB Design System
+
+**Version:** 1.0.0 | **Updated:** 2026-03-23 | **Status:** Source of Truth
+
+---
+
+## 1. Design Philosophy
+
+ROA2WEB is an ERP application for accounting and financial data management. The design prioritizes:
+
+- **Data density over whitespace** — Users process hundreds of records daily; every pixel earns its space
+- **Scan-friendly hierarchy** — Bold totals, muted labels, color-coded sections guide the eye
+- **Compact, not cramped** — 32px input height, 8px gaps, systematic spacing scale
+- **Professional restraint** — No decorative elements, no hero sections, no gratuitous animation
+- **Trust at the pixel level** — Financial data demands precision; misaligned inputs erode confidence
+- **System fonts for speed** — No custom font loading; native OS typography feels fast and familiar
+- **Progressive disclosure** — Show summary first, reveal detail on demand (collapsible sections, tooltips, bottom sheets)
+
+---
+
+## 2. Visual Identity
+
+### Brand Colors
+
+| Role | Token | Hex (Light) | Usage |
+|------|-------|-------------|-------|
+| Primary | `--color-primary` | #2563eb | Actions, links, focus rings, active states |
+| Primary Dark | `--color-primary-dark` | #1d4ed8 | Hover states, pressed buttons |
+| Success | `--color-success` | #059669 | Approvals, positive values, confirmations |
+| Warning | `--color-warning` | #d97706 | Pending states, caution alerts, mismatches |
+| Error | `--color-error` | #dc2626 | Rejections, validation errors, negative values |
+| Info | `--color-info` | #0891b2 | Informational badges, OCR hints |
+
+### Surface Hierarchy
+
+```
+Ground (#f8fafc) → Section → Card (#ffffff) → Overlay
+ ↓ darkest ↓ lightest ↓ modal/dropdown
+```
+
+Each level uses progressively elevated surface tokens. In dark mode, the scale inverts (darker = lower, lighter = higher).
+
+| Token | Light | Dark | Purpose |
+|-------|-------|------|---------|
+| `--surface-ground` | #f8fafc | #0f172a | Page background |
+| `--surface-card` | #ffffff | #1e293b | Card/container backgrounds |
+| `--surface-hover` | #f1f5f9 | #334155 | Hover states, chips |
+| `--surface-border` | #e2e8f0 | #475569 | Borders, dividers |
+| `--text-color` | #111827 | #f9fafb | Primary text |
+| `--text-color-secondary` | #6b7280 | #d1d5db | Labels, hints, metadata |
+
+> **Full palette reference**: [DESIGN_TOKENS.md](./DESIGN_TOKENS.md)
+
+---
+
+## 3. Color System
+
+### Semantic Color Roles
+
+Always use semantic tokens, never raw hex values:
+
+```css
+/* Backgrounds */
+background: var(--surface-card); /* Cards, modals */
+background: var(--surface-ground); /* Page background */
+background: var(--surface-hover); /* Hover states, chips */
+
+/* Text */
+color: var(--text-color); /* Primary text */
+color: var(--text-color-secondary); /* Labels, hints */
+
+/* Borders */
+border-color: var(--surface-border); /* Standard borders */
+```
+
+### Status Color Scales
+
+Each status has a 50-900 scale. Use **100** for backgrounds, **800** for text in light mode:
+
+| Status | Background | Text | Border | Use For |
+|--------|-----------|------|--------|---------|
+| Success | `--green-100` | `--green-800` | `--green-300` | Approvals, positive values, OCR high confidence |
+| Warning | `--yellow-100` | `--yellow-800` | `--yellow-300` | Pending, caution, OCR medium confidence |
+| Error | `--red-100` | `--red-800` | `--red-300` | Rejections, errors, OCR low confidence |
+| Info | `--blue-100` | `--blue-800` | `--blue-300` | Informational, OCR hints, categorization |
+
+### OCR Confidence Colors
+
+| Level | Threshold | Background | Text | Icon |
+|-------|-----------|-----------|------|------|
+| High | >= 85% | `--green-100` | `--green-800` | `pi-check-circle` |
+| Medium | >= 60% | `--yellow-100` | `--yellow-800` | `pi-exclamation-circle` |
+| Low | < 60% | `--red-100` | `--red-800` | `pi-question-circle` |
+
+### Color-Coded Form Sections
+
+- **Green background** (`--green-50`): Financial values (TOTAL, payments, TVA)
+- **Blue background** (`--blue-50`): Categorization (expense type, description)
+- **Cyan background** (`--cyan-50`): Client/B2B information
+- **No background**: Standard form fields (supplier, document)
+
+---
+
+## 4. Typography
+
+### Font Stack
+
+```css
+font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
+font-family: var(--font-mono); /* For financial values, code */
+```
+
+### Scale
+
+| Token | Size | Usage |
+|-------|------|-------|
+| `--text-xs` | 12px | Labels, badges, metadata, timestamps, hints |
+| `--text-sm` | 14px | Table cells, form inputs, secondary text, buttons |
+| `--text-base` | 16px | Body text, primary content (default) |
+| `--text-lg` | 18px | Subheadings |
+| `--text-xl` | 20px | Card titles, section headings |
+| `--text-2xl` | 24px | Page titles, metric values, KPI numbers |
+| `--text-3xl` | 32px | Dashboard hero numbers |
+| `--text-4xl` | 40px | Reserved for exceptional emphasis |
+
+### Weight Rules
+
+| Token | Value | Usage |
+|-------|-------|-------|
+| `--font-normal` | 400 | Body text, table cells, descriptions |
+| `--font-medium` | 500 | Labels, buttons, badges, emphasis |
+| `--font-semibold` | 600 | Headings, financial totals, card titles |
+| `--font-bold` | 700 | TOTAL labels, strong emphasis, alerts |
+
+### Financial Numbers
+
+All financial values use `font-variant-numeric: tabular-nums` for column alignment.
+
+---
+
+## 5. Spacing & Layout
+
+### Spacing Scale
+
+| Token | Value | Primary Use |
+|-------|-------|------------|
+| `--space-xs` | 4px | Icon gaps, badge padding, tight internal spacing |
+| `--space-sm` | 8px | Related item gaps, compact form spacing |
+| `--space-md` | 16px | Standard component padding, section gaps |
+| `--space-lg` | 24px | Card padding, section separation |
+| `--space-xl` | 32px | Page margins, major separations |
+| `--space-2xl` | 48px | Layout sections |
+| `--space-3xl` | 64px | Page-level spacing |
+
+### Form Layout Rules
+
+- **Input height**: 32px for compact forms, 40px for standalone forms
+- **Input font**: `--text-sm` (14px) for form inputs
+- **Numeric inputs**: `text-align: right` always
+- **Labels**: `--text-xs` (12px), `--font-semibold`, `--text-color-secondary`
+- **Layout**: Inline labels next to inputs on desktop, stacked on mobile
+- **Row gaps**: `--space-sm` (8px) between form rows
+- **Row borders**: `1px solid var(--surface-border)` between rows
+
+### Breakpoints
+
+| Name | Width | Layout Behavior |
+|------|-------|----------------|
+| Extra-small | ≤360px | Payment inputs stack vertically, minimal padding |
+| Mobile | ≤768px | Single column, stacked forms, bottom sheets for filters |
+| Tablet | 769-1023px | Hybrid layout, wider cards |
+| Desktop | ≥1024px | Full multi-column, inline forms, side-by-side panels |
+| Wide | ≥1400px | Max content width, centered layout |
+
+---
+
+## 6. Component Patterns
+
+### Cards
+
+```css
+/* Standard card */
+background: var(--surface-card);
+border-radius: var(--radius-md); /* 8px */
+padding: var(--space-lg); /* 24px */
+box-shadow: var(--shadow-sm);
+
+/* Compact card (data-dense views) */
+padding: var(--space-md); /* 16px */
+
+/* Metric/KPI card */
+/* Icon + --text-2xl value + --text-xs label + trend indicator */
+```
+
+### Buttons
+
+- Primary: `--color-primary` background, white text
+- Secondary: Outlined or text-only (`severity="secondary"`)
+- Minimum touch target: 44x44px on mobile
+- Icon buttons: 32x32px on desktop, 44x44px on mobile
+
+### Tables (PrimeVue DataTable)
+
+- Separate Debit/Credit columns (never stacked in one cell)
+- Filter buttons on dedicated row below filters
+- Export exports ALL data, not just current page
+- All overrides in `vendor/primevue-overrides.css` — never use `:deep()` in components
+
+### Dropdowns in Compact Forms
+
+Use `dropdown-borderless` class for inline dropdowns:
+```html
+
+```
+
+> **Full pattern reference**: [CSS_PATTERNS.md](./CSS_PATTERNS.md)
+
+---
+
+## 7. Form Design
+
+### Receipt Form Layout (UnifiedReceiptForm)
+
+Ultra-compact flat layout with color-coded sections:
+
+```
+┌─────────────────────────────────────────────┐
+│ Furnizor [AutoComplete____] CUI [___] 🟢 ↻ │ ← white bg
+├─────────────────────────────────────────────┤
+│ OCR: Lidl Discount SRL (22891860) │ ← blue-50 hint
+├─────────────────────────────────────────────┤
+│ [Bon fiscal ▾] Nr. [___] Data [___] 🟢 │ ← white bg
+├─────────────────────────────────────────────┤
+│ TOTAL [_140px_] 🟢 Card [_120px_] │ ← green-50 bg
+│ Cash [_120px_] │
+│ Alte [_120px_] 🟢 │
+├─────────────────────────────────────────────┤
+│ TVA: [19% ▾][___] [9% ▾][___] 🟢 │ ← white bg
+├─────────────────────────────────────────────┤
+│ [Cheltuiala... ▾] │ ← blue-50 bg
+│ [Descriere...________________________] │
+├─────────────────────────────────────────────┤
+│ Atasamente: [📄 bon.pdf ✕] [➕] │ ← white bg
+└─────────────────────────────────────────────┘
+```
+
+### OCR Confidence Badges
+
+- **Placement**: Inline, next to the input they describe
+- **Display**: Icon-only in compact form (percentage hidden, available via tooltip)
+- **Sizing**: Fixed reserved width (20px) to prevent input misalignment
+- **Hover**: `opacity: 0.85 → 1` transition for discoverability
+- **Interaction**: `cursor: help`, tooltip shows full percentage on hover/tap
+
+### Input Conventions
+
+```css
+/* All compact form inputs */
+height: 32px;
+padding: var(--space-xs) var(--space-sm);
+font-size: var(--text-sm);
+border: 1px solid var(--surface-border);
+border-radius: var(--radius-sm);
+
+/* Financial inputs additionally */
+text-align: right;
+font-variant-numeric: tabular-nums;
+```
+
+---
+
+## 8. Mobile Strategy
+
+### Philosophy
+
+MD3-inspired (Material Design 3) touch-first design. Mobile is not "desktop shrunk" — it gets intentional layout.
+
+### Required Components (ALL mobile views)
+
+| Component | Location | Purpose |
+|-----------|----------|---------|
+| MobileTopBar | `@shared/components/mobile/MobileTopBar.vue` | Fixed header (56px), title, actions |
+| MobileBottomNav | `@shared/components/mobile/MobileBottomNav.vue` | Fixed footer (56px), tab navigation |
+| BottomSheet | `@shared/components/mobile/BottomSheet.vue` | Filters, extended options (slides up) |
+| MobileSelectionFooter | `@shared/components/mobile/MobileSelectionFooter.vue` | Batch actions when items selected |
+
+### Content Padding
+
+```css
+.mobile-content {
+ padding-top: 56px; /* MobileTopBar height */
+ padding-bottom: 56px; /* MobileBottomNav height */
+}
+```
+
+### Mobile Form Rules
+
+- **Full-width inputs**: Stacked vertically, `width: 100%`
+- **Payment inputs**: Equal-width side-by-side (`flex: 1 1 0%`), stack on extra-small (≤360px)
+- **Confidence badges**: Icon-only, no percentage text
+- **Filters**: Always in BottomSheet, never inline
+- **Selection actions**: Always in MobileSelectionFooter, never in header
+- **Upload zone**: Reduced height (24px min) after file selection
+
+### Touch Targets
+
+- **Minimum**: 44x44px for all interactive elements (buttons, links, checkboxes)
+- **Input font**: 16px minimum to prevent iOS zoom
+- **Active feedback**: `scale(0.98)` on touch for responsive feel
+
+> **Full mobile guide**: [MOBILE_PATTERNS.md](./MOBILE_PATTERNS.md)
+
+---
+
+## 9. Dark Mode
+
+### Three-Tier System
+
+| Mode | Trigger | CSS Selector |
+|------|---------|-------------|
+| Auto (default) | OS preference | `@media (prefers-color-scheme: dark)` + `:root:not([data-theme])` |
+| Light | User toggle | `[data-theme="light"]` on `` |
+| Dark | User toggle | `[data-theme="dark"]` on `` |
+
+### CSS Priority
+
+```
+[data-theme="dark/light"] > @media (prefers-color-scheme) > :root (light default)
+```
+
+### Implementation Pattern
+
+```css
+/* Light mode (default) */
+.component { background: var(--green-100); color: var(--green-800); }
+
+/* Dark mode — explicit toggle */
+[data-theme="dark"] .component {
+ background: rgba(34, 197, 94, 0.2);
+ color: var(--green-400);
+}
+
+/* Dark mode — auto (prefers-color-scheme) */
+@media (prefers-color-scheme: dark) {
+ :root:not([data-theme="light"]) .component {
+ background: rgba(34, 197, 94, 0.2);
+ color: var(--green-400);
+ }
+}
+```
+
+### Key Rules
+
+- **Always** use semantic tokens (`--surface-card`, `--text-color`) — they auto-adapt
+- **Status colors**: Use `rgba()` overlays in dark mode, not the inverted scale tokens
+- **Persistence**: `localStorage['user-theme']` stores user choice
+- **Toggle**: Theme button in `AppHeader.vue` cycles auto → light → dark
+
+### Testing Checklist
+
+1. Click theme button in header (cycles auto → light → dark)
+2. DevTools → Rendering → Emulate CSS media → `prefers-color-scheme: dark`
+3. Verify all text remains readable
+4. Verify colored sections (green, blue) are visible but not harsh
+
+---
+
+## 10. Accessibility
+
+### Targets
+
+- **WCAG 2.1 AA** compliance
+- **Color contrast**: `--color-text` provides 16.9:1 ratio, `--color-text-secondary` provides 4.6:1
+
+### Focus States
+
+```css
+outline: 2px solid var(--color-primary);
+outline-offset: 2px;
+```
+
+### Touch Targets
+
+- All interactive elements: minimum 44x44px on mobile
+- Added via `min-height: 44px; min-width: 44px` on touch devices
+
+### Heading Hierarchy
+
+- Sequential: h1 > h2 > h3 (no skipping levels)
+- One `
` per page (page title)
+- `` for sections, `` for subsections
+
+### Screen Reader Support
+
+- `.sr-only` class for visually hidden but accessible text
+- ARIA landmarks on major regions
+- Meaningful `alt` text on all images
+
+### Reduced Motion
+
+```css
+@media (prefers-reduced-motion: reduce) {
+ * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
+}
+```
+
+---
+
+## 11. Reference Index
+
+| Document | Purpose | Read When |
+|----------|---------|-----------|
+| [DESIGN_TOKENS.md](./DESIGN_TOKENS.md) | Complete token reference with all variables | Looking up specific token values |
+| [CSS_PATTERNS.md](./CSS_PATTERNS.md) | Reusable component CSS patterns | Building or styling components |
+| [ONBOARDING_CSS.md](./ONBOARDING_CSS.md) | CSS system quick start (5 min) | New to the project's frontend |
+| [MOBILE_PATTERNS.md](./MOBILE_PATTERNS.md) | Mobile component library and patterns | Building mobile views |
+| [ARCHITECTURE-DECISIONS.md](./ARCHITECTURE-DECISIONS.md) | Technical architecture decisions | Understanding "why" behind choices |
+
+### CSS File Structure
+
+```
+src/assets/css/
+├── core/ # Foundation: variables, tokens, md3-tokens, reset, typography
+├── layout/ # Grid, containers, navigation
+├── components/ # Cards, buttons, tables, forms, stats
+├── patterns/ # Interactive, dashboard, animations
+├── utilities/ # Spacing, display, text, flex, colors
+├── vendor/ # PrimeVue overrides (centralized, never :deep())
+├── mobile.css # Mobile optimizations (imported last)
+└── main.css # Entry point with import cascade
+```
diff --git a/src/assets/css/layout/containers.css b/src/assets/css/layout/containers.css
index ad07a08..fba9439 100644
--- a/src/assets/css/layout/containers.css
+++ b/src/assets/css/layout/containers.css
@@ -189,6 +189,12 @@
flex-direction: column;
align-items: stretch;
}
+
+ /* Keep compact upload action bar horizontal on mobile */
+ .compact-upload-zone .action-bar {
+ flex-direction: row;
+ align-items: center;
+ }
}
@media (max-width: 480px) {
diff --git a/src/modules/data-entry/components/ocr/OCRConfidenceIndicator.vue b/src/modules/data-entry/components/ocr/OCRConfidenceIndicator.vue
index f4e8c5f..8fd928a 100644
--- a/src/modules/data-entry/components/ocr/OCRConfidenceIndicator.vue
+++ b/src/modules/data-entry/components/ocr/OCRConfidenceIndicator.vue
@@ -73,17 +73,19 @@ const tooltipText = computed(() => {
.confidence-indicator {
display: inline-flex;
align-items: center;
- gap: 0.25rem;
- padding: 0.15rem 0.5rem;
- border-radius: 12px;
- font-size: 0.75rem;
- font-weight: 500;
+ gap: var(--space-xs);
+ padding: 2px var(--space-sm);
+ border-radius: var(--radius-lg);
+ font-size: var(--text-xs);
+ font-weight: var(--font-medium);
}
/* Sizes */
.size-small {
font-size: 0.7rem;
- padding: 0.1rem 0.35rem;
+ padding: 1px var(--space-xs);
+ border-radius: var(--radius-sm);
+ line-height: 1;
}
.size-small i {
@@ -96,7 +98,7 @@ const tooltipText = computed(() => {
.size-large {
font-size: 0.85rem;
- padding: 0.2rem 0.6rem;
+ padding: 2px var(--space-sm);
}
.size-large i {
@@ -105,21 +107,55 @@ const tooltipText = computed(() => {
/* Confidence levels */
.high {
- background: #dcfce7;
- color: #166534;
+ background: var(--green-100);
+ color: var(--green-800);
}
.medium {
- background: #fef9c3;
- color: #854d0e;
+ background: var(--yellow-100);
+ color: var(--yellow-800);
}
.low {
- background: #fee2e2;
- color: #991b1b;
+ background: var(--red-100);
+ color: var(--red-800);
}
.percentage {
font-variant-numeric: tabular-nums;
}
+
+/* Dark mode — explicit toggle */
+[data-theme="dark"] .high {
+ background: rgba(34, 197, 94, 0.2);
+ color: var(--green-400);
+}
+
+[data-theme="dark"] .medium {
+ background: rgba(234, 179, 8, 0.2);
+ color: var(--yellow-400);
+}
+
+[data-theme="dark"] .low {
+ background: rgba(220, 38, 38, 0.2);
+ color: var(--red-400);
+}
+
+/* Dark mode — auto (prefers-color-scheme) */
+@media (prefers-color-scheme: dark) {
+ :root:not([data-theme="light"]) .high {
+ background: rgba(34, 197, 94, 0.2);
+ color: var(--green-400);
+ }
+
+ :root:not([data-theme="light"]) .medium {
+ background: rgba(234, 179, 8, 0.2);
+ color: var(--yellow-400);
+ }
+
+ :root:not([data-theme="light"]) .low {
+ background: rgba(220, 38, 38, 0.2);
+ color: var(--red-400);
+ }
+}
diff --git a/src/modules/data-entry/components/receipts/CompactUploadZone.vue b/src/modules/data-entry/components/receipts/CompactUploadZone.vue
index 508a184..9f9c5b1 100644
--- a/src/modules/data-entry/components/receipts/CompactUploadZone.vue
+++ b/src/modules/data-entry/components/receipts/CompactUploadZone.vue
@@ -425,12 +425,17 @@ defineExpose({ reset, processOCR })
color: var(--text-color-secondary);
}
-/* Action bar - single line */
+/* Action bar - single line (override global .action-bar from containers.css) */
.action-bar {
display: flex;
+ flex-direction: row;
flex-wrap: nowrap;
gap: var(--space-sm);
- margin-top: 2px; /* minimal margin */
+ margin-top: 2px;
+ margin-bottom: 0;
+ padding: 0;
+ background: transparent;
+ border-radius: 0;
justify-content: center;
align-items: center;
}
@@ -490,7 +495,78 @@ defineExpose({ reset, processOCR })
font-size: var(--text-xs); /* 12px - uniform */
}
-/* Responsive */
+/* Mobile: compact upload strip */
+@media (max-width: 768px) {
+ .compact-upload-zone {
+ margin-bottom: 2px;
+ }
+
+ .upload-strip {
+ min-height: 24px;
+ padding: 1px var(--space-sm);
+ }
+
+ .upload-strip.has-file {
+ min-height: 20px;
+ }
+
+ .empty-state span {
+ font-size: var(--text-xs);
+ }
+
+ .file-name {
+ max-width: 180px;
+ font-size: var(--text-xs);
+ }
+
+ .file-size {
+ font-size: 0.65rem;
+ }
+
+ /* Action bar: compact row on mobile
+ Global override is in containers.css (.compact-upload-zone .action-bar) */
+ .action-bar {
+ margin-top: 1px;
+ gap: 2px;
+ flex-wrap: nowrap;
+ justify-content: center;
+ }
+
+ .action-bar :deep(.p-button) {
+ height: 24px;
+ min-width: 24px;
+ padding: 2px var(--space-xs);
+ font-size: 0.7rem;
+ }
+
+ /* Hide refresh button label, keep icon only */
+ .action-bar :deep(.p-button .p-button-label) {
+ font-size: 0.7rem;
+ }
+
+ .engine-selector {
+ min-width: 70px;
+ max-width: 110px;
+ }
+
+ .engine-selector :deep(.p-dropdown) {
+ min-height: 24px;
+ height: 24px;
+ font-size: 0.7rem;
+ padding: 0 2px;
+ }
+
+ .engine-selector :deep(.p-dropdown-label) {
+ padding: 2px var(--space-xs);
+ font-size: 0.7rem;
+ }
+
+ .engine-selector :deep(.p-dropdown-trigger) {
+ width: 18px;
+ }
+}
+
+/* Extra-small: hide long text */
@media (max-width: 480px) {
.strip-content span:not(.file-name) {
display: none;
diff --git a/src/modules/data-entry/components/receipts/UnifiedReceiptForm.vue b/src/modules/data-entry/components/receipts/UnifiedReceiptForm.vue
index f801879..99fbbec 100644
--- a/src/modules/data-entry/components/receipts/UnifiedReceiptForm.vue
+++ b/src/modules/data-entry/components/receipts/UnifiedReceiptForm.vue
@@ -108,28 +108,28 @@
/>
-
-