docs(cannamanage): add CannaManage wiki pages and mockup images

- 11 wiki pages: CannaManage-Home + 01-10 covering full Phase 0 docs
- 5 mockup images in docs/wiki/images/
- Updated _Sidebar.md with CannaManage section
This commit is contained in:
Patrick Plate
2026-04-06 11:21:35 +02:00
parent 97ccafc0d7
commit cda8946c75
17 changed files with 5733 additions and 0 deletions
@@ -0,0 +1,550 @@
# CannaManage — Wireframes & UI Mockups
**Phase 4a | Document 6 of 7**
**Date:** 2026-04-06
**Stack:** Spring Boot 3.x · PrimeFaces JSF · PostgreSQL
---
## Table of Contents
1. [Design System Overview](#1-design-system-overview)
2. [Admin Portal Screens](#2-admin-portal-screens)
3. [Member Portal Screens](#3-member-portal-screens)
4. [Navigation & Information Architecture](#4-navigation--information-architecture)
5. [Responsive Design Notes](#5-responsive-design-notes)
6. [Accessibility](#6-accessibility)
---
## 1. Design System Overview
### 1.1 Color Palette
| Token | Hex | Usage |
|---|---|---|
| `--color-primary` | `#2D5016` | Sidebar background, primary buttons, active nav items |
| `--color-primary-medium` | `#4A7C28` | Hover states, section headers, badge outlines |
| `--color-accent` | `#8BC34A` | Highlights, progress bars filled, success indicators |
| `--color-bg` | `#F5F5F5` | Page background, card backgrounds |
| `--color-text` | `#1A1A1A` | Body text, table cell content |
| `--color-warning` | `#FF6B35` | Quota >80%, low stock, warnings |
| `--color-error` | `#D32F2F` | Quota exceeded, recalled batches, destructive actions |
| `--color-white` | `#FFFFFF` | Sidebar text, button labels on dark bg, card surfaces |
### 1.2 Typography
| Element | Font | Size | Weight |
|---|---|---|---|
| H1 — Page title | Inter | 24px | 600 |
| H2 — Section heading | Inter | 18px | 600 |
| H3 — Card title | Inter | 14px | 600 |
| Body / table rows | Inter | 14px | 400 |
| Caption / label | Inter | 12px | 400 |
| Mono (codes, IDs) | JetBrains Mono | 13px | 400 |
### 1.3 Component Library
All UI components come from **PrimeFaces 13.x** (JSF-based). No external React/Angular dependencies in MVP.
| Component | Usage |
|---|---|
| `p:panel` | Section containers, card wrappers |
| `p:dataTable` with `p:column` | Tabular data: distributions, members, batches |
| `p:paginator` | Pagination on all tables |
| `p:inputText` | Single-line text fields |
| `p:inputNumber` | Weight inputs (gram precision) |
| `p:selectOneMenu` | Dropdown selects (member, strain, batch) |
| `p:calendar` | Date range pickers for reports |
| `p:progressBar` | Quota consumption display |
| `p:commandButton` | Primary and secondary actions |
| `p:confirmDialog` | Dangerous actions (recall, delete) |
| `p:messages` / `p:message` | Inline validation errors |
| `p:badge` | Status indicators (AVAILABLE, LOW, RECALLED) |
| `p:sidebar` | Mobile nav drawer (member portal) |
| `p:dialog` | Modal overlays |
### 1.4 Layout Grid
```
┌────────────────────────────────────────────────────┐
│ TOP NAVBAR (56px) club name · avatar · logout │
├──────────────┬─────────────────────────────────────┤
│ │ │
│ SIDEBAR │ MAIN CONTENT │
│ (240px) │ (fluid, min 784px) │
│ fixed │ │
│ │ │
└──────────────┴─────────────────────────────────────┘
```
- **Sidebar:** fixed left, `#2D5016` background, white nav labels with `#8BC34A` icons
- **Top Navbar:** `#FFFFFF` with bottom border `#E0E0E0`, breadcrumb left, user controls right
- **Main Content:** `#F5F5F5` background, 24px padding, max content width 1200px centered
---
## 2. Admin Portal Screens
### Screen 1 — Admin Dashboard
![Admin Dashboard](images/mockup-admin-dashboard.png)
#### ASCII Wireframe
```
┌─────────────────────────────────────────────────────────────────────┐
│ 🌿 CannaManage Grüne Oase Berlin e.V. 👤 Max M. [⏻] │
├────────────┬────────────────────────────────────────────────────────┤
│ │ Dashboard 🗓 April 2026 │
│ 📊 Dashboard◄│ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌───────────────┐ │
│ 👥 Members│ │ Total Members│ │ Distributions│ │ Stock Available│ │
│ │ │ │ │ This Month │ │ │ │
│ 📋 Distrib│ │ 142 │ │ 87 │ │ 3,240 g │ │
│ │ │ ▲ +3 MoM │ │ ▲ +12 MoM │ │ ▼ -800g MoM │ │
│ 📦 Stock │ └──────────────┘ └──────────────┘ └───────────────┘ │
│ │ │
│ 📄 Reports│ Recent Distributions [+ New Entry] │
│ │ ┌─────────────────────────────────────────────────┐ │
│ ✅ Complian│ │ Member │ Strain │ Qty │ Date │ ✓ │ │
│ │ ├─────────────┼─────────────┼───────┼───────┼────┤ │
│ ⚙ Settings│ │ Müller, A. │ OG Kush B12 │ 5.0g │ 06.04 │ ✓ │ │
│ │ │ Schmidt, K. │ Amnesia H09 │ 3.5g │ 06.04 │ ✓ │ │
│ │ │ Weber, T. │ OG Kush B12 │ 7.0g │ 05.04 │ ✓ │ │
│ │ │ … │ │ │ │ │ │
└────────────┴──┴─────────────────────────────────────────────────────┘
```
#### Components & Behavior
| Component | PrimeFaces | Behavior |
|---|---|---|
| KPI Cards | `p:panel` with custom CSS | Auto-refreshed via `@poll` every 60s |
| Recent Distributions table | `p:dataTable` (5 rows, no paginator) | Row click → navigate to distribution detail |
| Member column link | `p:commandLink` | Navigate to `/admin/members/{id}` |
| `+ New Entry` button | `p:commandButton` style="primary" | Navigate to `/admin/distributions/new` |
| Trend indicators | Custom CSS `<span>` | Green ▲ / Red ▼ with delta value |
---
### Screen 2 — Distribution Recording Form
![Distribution Form](images/mockup-distribution-form.png)
#### ASCII Wireframe
```
┌─────────────────────────────────────────────────────────────────────┐
│ 🌿 CannaManage Grüne Oase Berlin e.V. 👤 Max M. [⏻] │
├────────────┬────────────────────────────────────────────────────────┤
│ │ Distributions New Distribution │
│ 📊 Dashbrd│ │
│ │ ┌──────────────────────────────────────────────────┐ │
│ 👥 Members│ │ Member * │ │
│ │ │ ┌──────────────────────────────────────────┐ │ │
│ 📋 Distrib◄│ │ │ 🔍 Search by name or member no. │ │ │
│ │ │ └──────────────────────────────────────────┘ │ │
│ 📦 Stock │ │ │ │
│ │ │ Strain / Batch * │ │
│ 📄 Reports│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ │ Select available batch ▼ │ │ │
│ ✅ Complian│ │ └──────────────────────────────────────────┘ │ │
│ │ │ │ │
│ ⚙ Settings│ │ Weight (grams) * │ │
│ │ │ ┌──────────┐ │ │
│ │ │ │ 0.0 g │ ← p:inputNumber min=0.1 max=25 │ │
│ │ │ └──────────┘ │ │
│ │ │ │ │
│ │ │ Monthly Quota — Müller, Anna │ │
│ │ │ ████████████░░░░░░░░ 32.5g / 50g 65% │ │
│ │ │ [████████████████░░░] <- p:progressBar │ │
│ │ │ │ │
│ │ │ [ Record Distribution ] [Cancel] │ │
│ │ └──────────────────────────────────────────────────┘ │
└────────────┴────────────────────────────────────────────────────────┘
```
#### Compliance UX — Real-Time Quota Indicator
The quota progress bar updates live as the weight field changes (via `f:ajax event="keyup"`):
| Quota Used After Distribution | Bar Color | Submit Button | Message |
|---|---|---|---|
| 079% | `#8BC34A` (green) | Enabled | — |
| 8099% | `#FF6B35` (orange) | Enabled | "⚠ Approaching monthly limit" |
| 100% | `#D32F2F` (red) | **Disabled** | "🚫 Monthly limit reached (50g)" |
| Over-21 member, >30g monthly | `#D32F2F` (red) | **Disabled** | "🚫 Under-21 limit reached (30g)" |
#### Components & Behavior
| Component | PrimeFaces | Behavior |
|---|---|---|
| Member search | `p:selectOneMenu` with `p:ajax` filter | Filters on type, shows name + member no. |
| Strain/Batch dropdown | `p:selectOneMenu` | Populated after member selection; shows only `AVAILABLE` batches |
| Weight input | `p:inputNumber` min=`0.1` max=`25.0` step=`0.1` | Triggers quota recalculation on blur |
| Quota bar | `p:progressBar` with dynamic `value` | Color class applied via `styleClass` computed in backing bean |
| Submit | `p:commandButton` | Disabled via `disabled="#{bean.quotaExceeded}"` |
| Cancel | `p:link` | Returns to distribution log without saving |
---
### Screen 3 — Stock Management
![Stock Management](images/mockup-stock-management.png)
#### ASCII Wireframe
```
┌─────────────────────────────────────────────────────────────────────┐
│ 🌿 CannaManage Grüne Oase Berlin e.V. 👤 Max M. [⏻] │
├────────────┬────────────────────────────────────────────────────────┤
│ │ Stock Management [+ Add Batch] │
│ 📊 Dashbrd│ │
│ │ ┌──────────────────────┐ ┌────────────────────┐ │
│ 👥 Members│ │ 🔍 Filter by strain │ │ Status: All ▼ │ │
│ │ └──────────────────────┘ └────────────────────┘ │
│ 📋 Distrib│ │
│ │ ┌───────────────────────────────────────────────────┐ │
│ 📦 Stock ◄│ │ Strain │Batch│THC% │CBD%│ Qty │Status│Act │ │
│ │ ├──────────────┼─────┼─────┼────┼───────┼──────┼────┤ │
│ 📄 Reports│ │ OG Kush │B-12 │ 19% │ 1% │ 850g │ ● │[R] │ │
│ │ │ Amnesia Haze │H-09 │ 22% │<1% │ 72g │ ⚠ │[R] │ │
│ ✅ Complian│ │ Blue Dream │D-05 │ 17% │ 2% │ 0g │ — │[R] │ │
│ │ │ Hindu Kush │K-21 │ 8% │15% │ 340g │ ✓ │[R] │ │
│ ⚙ Settings│ │ AK-47 #4 │A-03 │ 20% │ 1% │ RECALLED │ ⛔ │[R] │ │
│ │ └───────────────────────────────────────────────────┘ │
│ │ [◄ 1 2 3 … ►] Showing 1-10/42 │
└────────────┴────────────────────────────────────────────────────────┘
```
#### Status Badges
| Badge | Color | Icon | Condition |
|---|---|---|---|
| `AVAILABLE` | `#4A7C28` bg | ✓ checkmark | `qty > 100g` and not recalled |
| `LOW` | `#FF6B35` bg | ⚠ warning | `0 < qty ≤ 100g` |
| `EXHAUSTED` | `#9E9E9E` bg | — dash | `qty = 0` |
| `RECALLED` | `#D32F2F` bg | ⛔ stop | `recall_date IS NOT NULL` |
#### Components & Behavior
| Component | PrimeFaces | Behavior |
|---|---|---|
| Strain filter | `p:inputText` with `filterBy` | Filters table client-side on keyup |
| Status filter | `p:selectOneMenu` | Filters table rows by status value |
| Batch table | `p:dataTable` lazy=`true` | Server-side pagination, 10 rows/page |
| Status badge | Custom CSS `<span class="badge badge-{status}">` | Icon + text label (not color alone) |
| Recall button | `p:commandButton` styleClass=`p-button-danger` | Opens `p:confirmDialog` before executing |
| Confirm dialog | `p:confirmDialog` | "Recall batch B-12 (OG Kush, 850g)? This cannot be undone." |
| Add Batch | `p:commandButton` | Opens `p:dialog` with batch entry form |
---
### Screen 4 — Compliance Report Generation
![Compliance Report](images/mockup-compliance-report.png)
#### ASCII Wireframe
```
┌─────────────────────────────────────────────────────────────────────┐
│ 🌿 CannaManage Grüne Oase Berlin e.V. 👤 Max M. [⏻] │
├────────────┬────────────────────────────────────────────────────────┤
│ │ Reports Monthly Compliance Report │
│ 📊 Dashbrd│ │
│ │ ┌─────────────────────────────────────────────────┐ │
│ 👥 Members│ │ Reporting Period │ │
│ │ │ Month: [ March ▼ ] Year: [ 2026 ▼ ] │ │
│ 📋 Distrib│ │ [ Generate Report ] │ │
│ │ └─────────────────────────────────────────────────┘ │
│ 📦 Stock │ │
│ │ ┌─────────────────────────────────────────────────┐ │
│ 📄 Reports◄│ │ PDF PREVIEW │ │
│ │ │ ┌─────────────────────────────────────────┐ │ │
│ ✅ Complian│ │ │ 🌿 CannaManage — Monthly Report Mar 2026 │ │ │
│ │ │ │ Club: Grüne Oase Berlin e.V. │ │ │
│ ⚙ Settings│ │ │ ─────────────────────────────────────── │ │ │
│ │ │ │ Total Members: 142 │ │ │
│ │ │ │ Active Members (distributed): 87 │ │ │
│ │ │ │ Total Distributed: 435.5g │ │ │
│ │ │ └─────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ │ [⬇ Download PDF] [⬇ Download CSV] │ │
│ │ └─────────────────────────────────────────────────┘ │
│ │ │
│ │ Summary Table │
│ │ ┌────────────────────────────────────────────────┐ │
│ │ │ Metric │ Value │ Limit │ │
│ │ ├──────────────────────┼───────────┼─────────────┤ │
│ │ │ Members >50g/month │ 0 │ Must be 0 │ │
│ │ │ Members >30g (U21) │ 0 │ Must be 0 │ │
│ │ │ Recalled Batches │ 1 │ — (info) │ │
│ │ │ Avg grams / member │ 5.0g │ — │ │
│ │ └────────────────────────────────────────────────┘ │
└────────────┴────────────────────────────────────────────────────────┘
```
#### Components & Behavior
| Component | PrimeFaces | Behavior |
|---|---|---|
| Month selector | `p:selectOneMenu` | Months JanDec |
| Year selector | `p:selectOneMenu` | Current year ± 2 |
| Generate button | `p:commandButton` | Calls report service; shows spinner; renders PDF thumbnail |
| PDF preview | `<iframe>` embedding `/report/preview?month=3&year=2026` | Generated by iText 7 in `cannamanage-report` module |
| Download PDF | `p:commandButton` | Streams PDF response from REST endpoint |
| Download CSV | `p:commandButton` | Streams CSV response (member-level data) |
| Summary table | `p:dataTable` | Computed compliance metrics; zero violations = green row |
---
## 3. Member Portal Screens
### Screen 5 — Member Dashboard / Quota View
![Member Quota](images/mockup-member-quota.png)
#### ASCII Wireframe
```
┌────────────────────────────────────────────────────┐
│ 🌿 CannaManage Anna Müller #M-0042 │
│ ────────────────────────────────────────────── │
│ │
│ Monthly Quota Remaining │
│ │
│ ╭───────────────╮ │
│ │ │ │
│ │ 17.5 g │ │
│ │ remaining │ │
│ │ │ │
│ │ of 50g/month │ │
│ ╰───────────────╯ │
│ ▓▓▓▓▓▓▓▓▓▓▓░░░░░░░ 65% used │
│ │
│ Distribution History │
│ ┌────────────────────────────────────────────┐ │
│ │ Date │ Strain │ Quantity │ │
│ ├────────────┼──────────────┼────────────────┤ │
│ │ 06.04.2026 │ OG Kush │ 5.0g │ │
│ │ 02.04.2026 │ Amnesia Haze │ 12.5g │ │
│ │ 28.03.2026 │ OG Kush │ 15.0g │ │
│ └────────────┴──────────────┴────────────────┘ │
│ │
│ Available Strains │
│ ┌────────────────────────────────────────────┐ │
│ │ Strain │ Availability │ │
│ ├──────────────┼─────────────────────────────┤ │
│ │ OG Kush │ ● Available │ │
│ │ Amnesia Haze │ ⚠ Limited │ │
│ │ Hindu Kush │ ● Available │ │
│ └────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────┘
```
#### Compliance Note — Available Strains Display
Per CanG §§67, members may NOT see specific batch quantities or total stock levels. The **Available Strains** table shows only:
- Strain name
- Availability status (Available / Limited / Unavailable)
Quantities, batch codes, and THC/CBD percentages are **not exposed** in the member portal.
#### Components & Behavior
| Component | PrimeFaces | Behavior |
|---|---|---|
| Quota circle | Custom CSS radial progress (`conic-gradient`) | Computed from monthly total; color matches threshold rules |
| Quota bar | `p:progressBar` | Same color logic as admin distribution form |
| History table | `p:dataTable` | Last 10 distributions; sorted newest first; no pagination in MVP |
| Strains table | `p:dataTable` | `status` column: text + icon only, no quantities |
---
### Screen 6 — Member Login
> *No mockup image — ASCII wireframe only.*
#### ASCII Wireframe
```
┌──────────────────────────────────────────┐
│ │
│ 🌿 CannaManage │
│ │
│ ┌──────────────────────────────────┐ │
│ │ E-Mail Address │ │
│ │ ┌────────────────────────────┐ │ │
│ │ │ you@example.com │ │ │
│ │ └────────────────────────────┘ │ │
│ │ │ │
│ │ Password │ │
│ │ ┌────────────────────────────┐ │ │
│ │ │ •••••••••••• │ │ │
│ │ └────────────────────────────┘ │ │
│ │ │ │
│ │ [ ████ Log In ████████████ ] │ │
│ └──────────────────────────────────┘ │
│ │
│ Problems logging in? │
│ Contact your club administrator. │
│ │
└──────────────────────────────────────────┘
```
#### Design Decisions
- **No self-registration link** — member accounts are created exclusively by admins via the admin portal. The login page has no "Create account" or "Sign up" flow.
- **No forgot-password link** — password resets are initiated by the club admin only. The login page directs users to contact their admin, avoiding email-based reset flows that would require verified email infrastructure in MVP.
- **No social login** — DSGVO compliance and club accountability require traceable credential management.
- **Form submission:** POST to `/login` (Spring Security form login), redirect to `/member/dashboard` on success.
#### Components & Behavior
| Component | PrimeFaces | Behavior |
|---|---|---|
| Email field | `p:inputText` with `required="true"` | Bean Validation `@Email` |
| Password field | `p:password` feedback=`false` | No strength meter on login |
| Login button | `p:commandButton` | Submit form; shows `p:messages` on failure |
| Error message | `p:messages` | "Invalid email or password." (never specific about which field failed) |
---
## 4. Navigation & Information Architecture
```mermaid
graph TD
Root["CannaManage Root"]
Root --> AdminPortal["Admin Portal /admin/"]
Root --> MemberPortal["Member Portal /member/"]
AdminPortal --> AdminDash["Dashboard (default)"]
AdminPortal --> Members["Members"]
Members --> MemberList["Member List"]
Members --> MemberDetail["Member Detail"]
AdminPortal --> Distributions["Distributions"]
Distributions --> DistLog["Distribution Log"]
Distributions --> NewDist["New Distribution"]
AdminPortal --> Stock["Stock"]
Stock --> Strains["Strains"]
Stock --> Batches["Batches"]
AdminPortal --> Reports["Reports"]
Reports --> MonthlyReport["Monthly Compliance"]
Reports --> MemberExport["Member Export"]
Reports --> RecallReport["Batch Recall Report"]
AdminPortal --> Compliance["Compliance"]
Compliance --> PreventionOfficer["Prevention Officer Info"]
AdminPortal --> Settings["Settings"]
Settings --> ClubProfile["Club Profile"]
MemberPortal --> MemberDash["Dashboard / Quota"]
MemberPortal --> DistHistory["Distribution History"]
MemberPortal --> StockAvail["Stock Availability"]
```
### URL Structure
| Path | Description | Role |
|---|---|---|
| `/login` | Login page | Public |
| `/admin/dashboard` | Admin home | `ROLE_ADMIN` |
| `/admin/members` | Member list | `ROLE_ADMIN` |
| `/admin/members/{id}` | Member detail | `ROLE_ADMIN` |
| `/admin/distributions` | Distribution log | `ROLE_ADMIN` |
| `/admin/distributions/new` | New distribution form | `ROLE_ADMIN` |
| `/admin/stock/strains` | Strain catalog | `ROLE_ADMIN` |
| `/admin/stock/batches` | Batch management | `ROLE_ADMIN` |
| `/admin/reports/monthly` | Compliance reports | `ROLE_ADMIN` |
| `/admin/reports/members` | Member data export | `ROLE_ADMIN` |
| `/admin/reports/recall` | Recall report | `ROLE_ADMIN` |
| `/admin/compliance` | Prevention officer | `ROLE_ADMIN` |
| `/admin/settings` | Club settings | `ROLE_ADMIN` |
| `/member/dashboard` | Member quota view | `ROLE_MEMBER` |
| `/member/distributions` | Personal history | `ROLE_MEMBER` |
| `/member/stock` | Strain availability | `ROLE_MEMBER` |
---
## 5. Responsive Design Notes
### MVP (v1) — Desktop-First
Target viewport: **1024px+**. PrimeFaces responsive grid (`p:panelGrid` with responsive columns, `ui-g-12 ui-md-6 ui-lg-4`) handles most layout adaptation down to tablet without custom media queries.
| Breakpoint | Behavior |
|---|---|
| `≥ 1280px` | Full layout — sidebar + content side-by-side |
| `10241279px` | Sidebar collapses to icon-only (60px); tooltips on hover |
| `7681023px` | Sidebar hidden; hamburger menu in top navbar |
| `< 768px` | Admin portal degraded (tables scroll horizontally) |
### Member Portal — Mobile-First from Day One
Members will typically check quota status on their phone. The member portal is designed mobile-first regardless of MVP/v2 timeline.
| Breakpoint | Behavior |
|---|---|
| `≥ 1024px` | Two-column layout: quota circle left, history right |
| `7681023px` | Single-column, full-width cards |
| `375767px` | Single-column, compact quota ring, condensed table |
| `< 375px` | Minimum supported; no horizontal scroll |
### v2 Roadmap
- PWA manifest + service worker (offline quota display)
- 768px and 375px explicit breakpoints with design tokens
- Touch-friendly `p:sidebar` for mobile member nav
- Push notifications for low quota warnings
---
## 6. Accessibility
CannaManage targets **WCAG 2.1 AA** compliance across both portals.
### Keyboard Navigation
| Element | Keyboard Behavior |
|---|---|
| Navigation sidebar | Tab navigates items; Enter activates |
| Data tables | Tab to table; arrow keys for row navigation |
| Dropdown menus | Enter/Space to open; arrow keys to navigate; Escape to close |
| Modal dialogs | Focus trapped inside; Escape to close; first focusable element receives focus on open |
| Confirmation dialogs | Tab between Confirm and Cancel; Enter on focused button |
### Screen Reader Support
- All `p:inputText` / `p:inputNumber` fields have `<label>` with `for` attribute
- `aria-label` set on icon-only buttons (e.g., recall action column)
- `aria-live="polite"` region on quota bar — announces percentage changes
- `aria-describedby` links compliance warning messages to the weight input
- PrimeFaces generates `role="grid"` and `aria-rowcount` on all data tables
### Color Independence
Status badges must never rely on color alone:
| Status | Color | Icon | Text label |
|---|---|---|---|
| AVAILABLE | Green | ✓ | "Available" |
| LOW | Orange | ⚠ | "Low Stock" |
| RECALLED | Red | ⛔ | "Recalled" |
| EXHAUSTED | Gray | — | "Exhausted" |
Quota progress bar additionally shows numeric percentage text alongside color change.
### Contrast Ratios
| Foreground | Background | Ratio | AA pass |
|---|---|---|---|
| `#FFFFFF` | `#2D5016` | 9.1:1 | ✅ |
| `#1A1A1A` | `#F5F5F5` | 16.0:1 | ✅ |
| `#1A1A1A` | `#FFFFFF` | 19.0:1 | ✅ |
| `#FFFFFF` | `#D32F2F` | 5.1:1 | ✅ |
| `#FFFFFF` | `#FF6B35` | 3.1:1 | ⚠ verify at large text only |
---
*Next: [07-CODING-STANDARDS.md](07-CODING-STANDARDS.md)*