# Air Maniax Ahmedabad — Staff Management CRM · Design System

> **Project:** Air Maniax Ahmedabad — Staff Management CRM (Indoor Inflatable Adventure Park, 400 capacity)
> **Vendor:** Fruxinfo Private Limited
> **Workflow stage:** Phase D / **Step 2 — Design** (Planning ✅ → **Design (this doc)** → dev-mode packs → Development)
> **Governing sources:** `docs/PROJECT_PLAN.md` (LOCKED 2026-06-18 — §3 roles/hierarchy, §4 modules, §4a photo evidence, §4b approval cascade, §4c Game Zones, §9 resolved decisions) + `docs/design/SCREEN_LIST.md` (**95 Phase-1 screens across 9 roles**) + `docs/CHANGE_LIST_v1.1.md` (CL-1…CL-5, applied 2026-06-19).
> **Stack:** Next.js (App Router) + Tailwind (web) · Node/Express + Prisma + MySQL 8 (API) · Flutter (mobile, P2).
> **Brand source:** `MANIAX logo.pdf` — orange→red triangle, blue→navy graffiti "AIR MANIAX" wordmark, deep-navy silhouette figure.
> **Last updated:** 2026-06-19 — **Change List v1.1 applied:** 9 roles (added Maintenance TL + Technician), Upload Check List CRUD, Action / Work-Order state machine + **Held** status, rotation-aware reassignment, Trainee TL assist-only, Cleaning Supervisor assign. (Initial lock 2026-06-18.)

---

## 1. Overview & Brand Principles

Air Maniax is high-energy on the floor and safety-serious behind the scenes. The CRM has two audiences with opposite needs, and the design system serves both from one token set:

- **Floor staff (Monitors, Cleaning Supervisor)** — work one-handed on a phone, between rides, under bright park lighting. They tick checklists, snap evidence photos, and submit. They need **huge tap targets, an unmissable G/A toggle, a camera that is one tap away, and zero ambiguity** about what is done vs. what still needs action.
- **Managers (Operation Head, Store Manager, Team Leader)** — work on the web at a desk. They scan dashboards, approve up the cascade, and read positive/negative reports. They need **density, clarity, and an instantly legible approval state-machine** across many Game Zones.

### Brand principles

1. **Energetic, never chaotic.** The logo's orange→red is electric. We use it *structurally* — primary actions, active nav, KPI accents, brand chrome — never as decorative noise. The workspace stays calm (off-white surfaces, navy structure) so the orange means "act here".
2. **Safety is the product.** This is a checklist-and-evidence system for a venue where children play on inflatables. Negative ("Action required") items are loud, red-orange, photo-backed, and impossible to bury. Positive compliance is reassuringly green.
3. **Status is always visible.** Every checklist shows exactly where it is in the Filled → TL → Store Manager → Operation Head cascade. "Pending" is honest: a higher role sees Pending until the item is approved all the way up.
4. **Mobile-first for the floor, dense for the desk.** Same tokens, two shells. Floor screens are thumb-reachable; manager screens are information-rich.
5. **One source of truth.** Game Zone = branch/site (one Store Manager each). zone/ride = activity area inside it. The chrome makes the user's current Game Zone scope obvious at all times.

---

## 2. Color Tokens

Derived directly from the logo: **Primary Orange** (triangle, energy), **Accent Red-Orange** (the hot lower edge of the triangle → alerts/negative/destructive), **Primary Blue** and **Light Blue** (wordmark gradient → links/info/accents), **Deep Navy** (silhouette + wordmark shadow → sidebar, headers, primary text).

### 2.1 Brand

| Token | Hex | Usage |
|---|---|---|
| `brand-orange` | `#F58220` | Primary actions/CTAs, active nav accent, brand energy, KPI accent digits, FAB |
| `brand-orange-soft` | `#FFF1E4` | Orange tint — hover wash on primary-ghost, selected-row wash, badge backgrounds |
| `brand-red` | `#ED1C24` | Alerts, **negative / Action-required (A)** emphasis, destructive actions, overdue |
| `brand-red-soft` | `#FDE7E8` | Red-orange tint — danger badge bg, A-item row highlight, error field wash |
| `brand-blue` | `#2E6DB4` | Links, secondary actions, info, in-progress status, focus ring base |
| `brand-blue-soft` | `#E9F1FB` | Blue tint — info badge bg, link hover wash, submitted-status wash |
| `brand-light-blue` | `#7FB2E5` | Accents, hover highlights, secondary chips, chart accents, avatar rings |
| `brand-navy` | `#16284A` | Sidebar background, top-bar (mobile), section headers, **primary text** |
| `brand-navy-soft` | `#22325A` | Sidebar hover/active fill, navy elevated panels, navy card on dark |

### 2.2 Surface & neutral ramp

| Token | Hex | Usage |
|---|---|---|
| `bg` | `#F5F7FA` | App page background (off-white) |
| `surface` | `#FFFFFF` | Cards, modals, table bodies, KPI tiles, form panels |
| `surface-2` | `#EEF2F7` | Table zebra alt rows, hover rows, secondary panels, input fill |
| `surface-3` | `#E2E8F1` | Table header bg, popover bg, elevated/well backgrounds |
| `gray-50` | `#F8FAFC` | Lightest fill, disabled surface |
| `gray-100` | `#F1F5F9` | Subtle fill, skeleton base |
| `gray-200` | `#E2E8F0` | **Default border / divider / hairline** |
| `gray-300` | `#CBD5E1` | Stronger border, input border, modal edge |
| `gray-400` | `#94A3B8` | Placeholder text, disabled text, low-emphasis icons |
| `gray-500` | `#64748B` | Secondary text, captions, helper text, muted icons |
| `gray-600` | `#475569` | Body text on light (when navy is too heavy for long runs) |
| `gray-700` | `#334155` | Strong secondary text |
| `gray-800` | `#1E293B` | Near-primary text alternative |
| `gray-900` | `#0F172A` | Maximum-contrast text |

> **Primary text** is `brand-navy` `#16284A` for headers/labels/short UI text; long body paragraphs may use `gray-600`/`gray-700` for reduced fatigue. Secondary/meta text is `gray-500`.

### 2.3 Semantic

| Token | Hex | Bg-tint (`<hex>1F`) | Usage |
|---|---|---|---|
| `success` | `#1E9E5A` | `#1E9E5A1F` | **G (Good / positive / compliant)**, Approved, Done, Available, healthy |
| `warning` | `#E0860B` | `#E0860B1F` | Pending-soon, due-soon, awaiting-approval (amber), low-attention |
| `danger` | `#ED1C24` | `#ED1C241F` | **A (Action required / negative)**, Overdue, Rejected/Sent-back, errors, breach (uses `brand-red`) |
| `info` | `#2E6DB4` | `#2E6DB41F` | Neutral status, In-progress, Submitted, informational (uses `brand-blue`) |

> **Rule:** semantic colors carry **meaning only** — never decorative. Badge background = `<hex>1F` (~12% opacity); foreground text/icon = solid hex. **G is always green, A is always red-orange** everywhere they appear (toggles, badges, report headers, item rows).

### 2.4 Checklist G/A & approval state-machine colors

The checklist is the heart of Phase 1. These status tokens drive `StatusBadge`, the cascade timeline, dashboards, and reports. Use **exactly** these mappings.

#### G/A item response

| Token | Label | Hex | Bg-tint | Where |
|---|---|---|---|---|
| `status-G` | **G — Good / Positive** | `#1E9E5A` (`success`) | `#1E9E5A1F` | ChecklistItem toggle (selected), positive report, item row |
| `status-A` | **A — Action required / Negative** | `#ED1C24` (`danger`) | `#ED1C241F` | ChecklistItem toggle (selected), negative report, A-item row + photo-required flag |

#### Approval state-machine (§4b)

| State token | Badge label | Hex | Bg-tint | Meaning |
|---|---|---|---|---|
| `st-not-started` | Not started | `#94A3B8` (`gray-400`) | `#94A3B81F` | Auto-assigned, no items ticked yet |
| `st-in-progress` | In progress | `#2E6DB4` (`info`) | `#2E6DB41F` | Some items ticked / draft saved |
| `st-submitted` | Submitted | `#7FB2E5` (`brand-light-blue`) | `#7FB2E51F` | Filler submitted with photos; awaiting TL |
| `st-tl-approved` | TL Approved | `#3B82C4` | `#3B82C41F` | First-level approved; awaiting Store Manager |
| `st-sm-approved` | SM Approved | `#2E6DB4` (`brand-blue`) | `#2E6DB41F` | Second-level approved; awaiting Operation Head |
| `st-oh-approved` | Done | `#1E9E5A` (`success`) | `#1E9E5A1F` | Operation-Head approved — fully done |
| `st-held` | Action in progress / Held *(CL-2)* | `#C2410C` (deep-orange) | `#C2410C1F` | **Single-track hold:** the checklist has ≥1 open A-item routed to maintenance; it cannot reach Done until **every** A-item is resolved (fixed/closed) |
| `st-pending` | Pending | `#E0860B` (`warning`) | `#E0860B1F` | **Derived** (§9 #5): not yet fully approved up the chain; what OH/SM see for anything below their level |
| `st-sent-back` | Sent back | `#ED1C24` (`danger`) | `#ED1C241F` | Rejected at any approval step → returns to original filler (§9 #3) |
| `st-overdue` | Overdue | `#ED1C24` (`danger`, **solid-fill** badge) | — | Past due / missed; escalates up |

> **Pending is a derived visibility rule, not a stored status** (PROJECT_PLAN §9 #5). The same checklist instance renders `Submitted`/`TL Approved`/… to the filler and lower roles, but renders `Pending` (amber) to any higher role until it clears that role's level. Overdue is the only status that uses a **solid** red fill (white text) so it screams on a dense table.
> **Held (CL-2)** uses a distinct **deep-orange** (between amber Pending and red A) so a checklist waiting on a maintenance action is visually separate from one merely awaiting approval. A `Held` checklist shows a count of open A-items and links to their Work-Orders.

#### Action / Maintenance Work-Order state-machine *(CL-2, new)*

An A-item on a submitted checklist spawns a **Work-Order (WO)** that runs its own track: operations TL **routes** it → Maintenance TL **assigns** to a Technician → Technician **fixes** (Done) or **returns** with a reason → Maintenance TL **closes** internally or **escalates/outsources** to the Store Manager. The parent checklist stays **Held** until all its WOs are closed.

| State token | Badge label | Hex | Bg-tint | Meaning |
|---|---|---|---|---|
| `wo-open` | Open | `#94A3B8` (`gray-400`) | `#94A3B81F` | A-item flagged; not yet routed |
| `wo-routed` | Routed to Maintenance | `#7FB2E5` (`brand-light-blue`) | `#7FB2E51F` | Operations TL routed it to the Maintenance TL |
| `wo-assigned` | Assigned | `#2E6DB4` (`brand-blue`) | `#2E6DB41F` | Maintenance TL assigned it to a Technician |
| `wo-in-progress` | In progress | `#C2410C` (deep-orange) | `#C2410C1F` | Technician working the fix |
| `wo-returned` | Returned | `#E0860B` (`warning`) | `#E0860B1F` | Technician couldn't fix internally — back to Maintenance TL with reason |
| `wo-done` | Fixed / Closed | `#1E9E5A` (`success`) | `#1E9E5A1F` | Fixed internally (with photo) → action closed |
| `wo-outsourced` | Outsourced | `#16284A` (`brand-navy`) | `#16284A1F` | Escalated to Store Manager for outsourcing (the only state that reaches SM) |
| `wo-overdue` | Overdue | `#ED1C24` (`danger`, **solid-fill**) | — | WO past its SLA; escalates up |

> **Locked rule (CL-2):** only **outsource** WOs reach the Store Manager (`wo-outsourced`); internally-fixed actions **close at the maintenance level** (`wo-done`). Reuse the same `StatusBadge` + `ApprovalTimeline` components for WOs (see §6.11/§6.14) with this token set.

### 2.5 WCAG contrast pairings (accessible foreground × background)

| Foreground | Background | Ratio (approx) | Verdict |
|---|---|---|---|
| `#FFFFFF` white | `brand-navy` `#16284A` | ~13.5:1 | AAA — default sidebar/header text |
| `#FFFFFF` white | `brand-orange` `#F58220` | ~2.6:1 | **Fails for small text** → use white only on orange for **bold ≥16px / icons / buttons (large-text path, AA)**; do NOT put small (<16px) white text on orange |
| `brand-navy` `#16284A` | `brand-orange` `#F58220` | ~5.6:1 | **AA pass** — preferred label color for filled-orange buttons & on-orange chips |
| `#FFFFFF` white | `brand-red` `#ED1C24` | ~4.0:1 | AA for ≥18px / bold ≥14px — fine for destructive button labels & solid Overdue badge; avoid tiny captions |
| `brand-navy` `#16284A` | `bg` `#F5F7FA` | ~12.8:1 | AAA — primary text on app background |
| `brand-navy` `#16284A` | `surface` `#FFFFFF` | ~13.9:1 | AAA — primary text on cards |
| `gray-500` `#64748B` | `surface` `#FFFFFF` | ~4.8:1 | AA — secondary/caption text |
| `brand-blue` `#2E6DB4` | `surface` `#FFFFFF` | ~4.6:1 | AA — links |
| `success` `#1E9E5A` | `#1E9E5A1F` tint | (badge) | foreground on its own tint stays legible; pair with navy/white only for solid fills |

> **Locked accessibility rule:** filled `brand-orange` buttons use **navy** label text (not white) for AA on small labels. White-on-orange is reserved for large/bold text and icon glyphs. Solid `brand-red` (Overdue / destructive) uses white text at ≥14px bold only.

### 2.6 CSS variables (`:root`)

```css
:root {
  /* brand */
  --brand-orange:       #F58220;
  --brand-orange-soft:  #FFF1E4;
  --brand-red:          #ED1C24;
  --brand-red-soft:     #FDE7E8;
  --brand-blue:         #2E6DB4;
  --brand-blue-soft:    #E9F1FB;
  --brand-light-blue:   #7FB2E5;
  --brand-navy:         #16284A;
  --brand-navy-soft:    #22325A;

  /* surface + neutrals */
  --bg:        #F5F7FA;
  --surface:   #FFFFFF;
  --surface-2: #EEF2F7;
  --surface-3: #E2E8F1;
  --gray-50:  #F8FAFC;  --gray-100: #F1F5F9;  --gray-200: #E2E8F0;
  --gray-300: #CBD5E1;  --gray-400: #94A3B8;  --gray-500: #64748B;
  --gray-600: #475569;  --gray-700: #334155;  --gray-800: #1E293B;
  --gray-900: #0F172A;

  /* semantic */
  --success: #1E9E5A;  --warning: #E0860B;  --danger: #ED1C24;  --info: #2E6DB4;

  /* checklist G/A */
  --status-g: #1E9E5A;  --status-a: #ED1C24;

  /* approval state-machine */
  --st-not-started: #94A3B8;
  --st-in-progress: #2E6DB4;
  --st-submitted:   #7FB2E5;
  --st-tl-approved: #3B82C4;
  --st-sm-approved: #2E6DB4;
  --st-oh-approved: #1E9E5A;
  --st-held:        #C2410C;   /* CL-2: Action in progress / Held */
  --st-pending:     #E0860B;
  --st-sent-back:   #ED1C24;
  --st-overdue:     #ED1C24;

  /* action / maintenance work-order state-machine (CL-2) */
  --wo-open:        #94A3B8;
  --wo-routed:      #7FB2E5;
  --wo-assigned:    #2E6DB4;
  --wo-in-progress: #C2410C;
  --wo-returned:    #E0860B;
  --wo-done:        #1E9E5A;
  --wo-outsourced:  #16284A;
  --wo-overdue:     #ED1C24;

  /* radius / shadow */
  --radius-sm: 4px; --radius: 8px; --radius-md: 12px; --radius-lg: 16px; --radius-full: 9999px;
  --shadow-sm: 0 1px 2px rgba(22,40,74,0.06);
  --shadow:    0 2px 8px rgba(22,40,74,0.08);
  --shadow-md: 0 8px 24px rgba(22,40,74,0.12);
  --ring-orange: 0 0 0 3px rgba(245,130,32,0.35);
  --ring-blue:   0 0 0 3px rgba(46,109,180,0.35);
}
```

### 2.7 `tailwind.config.ts` — `theme.extend.colors`

```ts
// tailwind.config.ts
import type { Config } from 'tailwindcss'

const config: Config = {
  content: ['./app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'],
  theme: {
    extend: {
      colors: {
        brand: {
          orange:      '#F58220',
          'orange-soft':'#FFF1E4',
          red:         '#ED1C24',
          'red-soft':  '#FDE7E8',
          blue:        '#2E6DB4',
          'blue-soft': '#E9F1FB',
          'light-blue':'#7FB2E5',
          navy:        '#16284A',
          'navy-soft': '#22325A',
        },
        bg:        '#F5F7FA',
        surface:   '#FFFFFF',
        'surface-2':'#EEF2F7',
        'surface-3':'#E2E8F1',
        gray: {
          50:'#F8FAFC',100:'#F1F5F9',200:'#E2E8F0',300:'#CBD5E1',400:'#94A3B8',
          500:'#64748B',600:'#475569',700:'#334155',800:'#1E293B',900:'#0F172A',
        },
        success: '#1E9E5A',
        warning: '#E0860B',
        danger:  '#ED1C24',
        info:    '#2E6DB4',
        // checklist + approval state-machine
        'status-g': '#1E9E5A',
        'status-a': '#ED1C24',
        st: {
          'not-started':'#94A3B8',
          'in-progress':'#2E6DB4',
          submitted:    '#7FB2E5',
          'tl-approved':'#3B82C4',
          'sm-approved':'#2E6DB4',
          'oh-approved':'#1E9E5A',
          held:         '#C2410C',   // CL-2: Action in progress / Held
          pending:      '#E0860B',
          'sent-back':  '#ED1C24',
          overdue:      '#ED1C24',
        },
        // action / maintenance work-order state-machine (CL-2)
        wo: {
          open:        '#94A3B8',
          routed:      '#7FB2E5',
          assigned:    '#2E6DB4',
          'in-progress':'#C2410C',
          returned:    '#E0860B',
          done:        '#1E9E5A',
          outsourced:  '#16284A',
          overdue:     '#ED1C24',
        },
      },
      fontFamily: {
        display: ['"DM Sans"', 'system-ui', 'sans-serif'],
        sans:    ['"DM Sans"', 'system-ui', 'sans-serif'],
      },
      borderRadius: { sm:'4px', DEFAULT:'8px', md:'12px', lg:'16px', full:'9999px' },
      boxShadow: {
        sm: '0 1px 2px rgba(22,40,74,0.06)',
        DEFAULT: '0 2px 8px rgba(22,40,74,0.08)',
        md: '0 8px 24px rgba(22,40,74,0.12)',
      },
    },
  },
  plugins: [],
}
export default config
```

> **Flutter parity (P2):** mirror these exact hexes into a `AmColors` Dart class (`AmColors.brandOrange = Color(0xFFF58220)`, etc.) and a `AmStatus` enum→Color map for the state-machine. One token list, two platforms.

---

## 3. Typography — DM Sans

**One family: [DM Sans](https://fonts.google.com/specimen/DM+Sans)** (variable, opsz 9..40), weights 400 / 500 / 600 / 700. It carries display and 14px body comfortably, so there is no second font load. Headers may use a **heavier/condensed display treatment** (700 + tight tracking, optional uppercase) to echo the logo's graffiti energy, but **all body stays DM Sans regular for legibility**.

Load in web:

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,400;9..40,500;9..40,600;9..40,700&display=swap" rel="stylesheet">
```

```css
body { font-family: 'DM Sans', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
.am-display { font-weight: 700; letter-spacing: -0.02em; } /* energetic header treatment */
```

### Type scale (all DM Sans)

| Token | Size / line-height / weight | Tracking | Usage |
|---|---|---|---|
| `display` | 40 / 46 / 700 | -0.02em | KPI big numbers, hero/landing H1, dashboard headline metric |
| `h1` | 30 / 38 / 700 | -0.02em | Page title (one per screen) |
| `h2` | 24 / 32 / 700 | -0.01em | Section header (use sparingly) |
| `h3` | 18 / 26 / 600 | normal | Card titles, modal title, table-group header |
| `h4` | 16 / 24 / 600 | normal | Subsection titles, form section labels |
| `body` | 14 / 22 / 400 | normal | Default body, table cells, form values |
| `body-md` | 14 / 22 / 500 | normal | Emphasized body, selected nav label |
| `body-sm` | 13 / 20 / 400 | normal | Dense tables, secondary text, meta |
| `caption` | 12 / 16 / 500 | normal | Helper text, timestamps, badge sublabels |
| `eyebrow` | 11 / 16 / 600 UPPERCASE | +0.10em | Field labels, KPI labels, table column headers, nav group labels |
| `button` | 14 / 20 / 600 | +0.01em | Button labels (mobile primary buttons 16/24/600) |

### Wordmark / logo usage rules

- The **AIR MANIAX logo (orange triangle + figure + blue graffiti wordmark)** is the brand mark used **everywhere** — login/auth screens, navy sidebar header, mobile top bar, the launcher/prototype hero, loading splash, PDF/Excel report headers, favicon. **Decision updated 2026-06-19 (client request): use the real logo image in all chrome, not a typographic lockup.**
- **Asset:** `docs/design/assets/air-maniax-logo.png` (source: project-root `AIR Manix.png`, 2319×1905, transparent PNG). Referenced via a relative `<img>` with **em-relative height** (`height:2.2em`) so it auto-scales per context: sidebar header ≈ 44px, mobile top bar ≈ 37px, login hero ≈ 105px, launcher hero 140px. The transparent badge sits cleanly on navy (orange triangle + light-blue wordmark provide contrast; the navy figure reads against the orange triangle).
- **Never** recolor, stretch, or add effects to the logo. Keep clear space ≥ the height of the triangle's base stroke on all sides. On a busy photo background, place it on a solid navy/white chip.
- *(Migration note: `scripts/apply-logo.cjs` replaced the old `<span class="text-brand-orange">AIR</span> MANIAX` typographic lockups across all dev-mode HTML; the prototype generator injects the logo into launcher/role-index/demo-bar chrome.)*
- Do not show a language switcher, tenant switcher, or theme switcher beside the wordmark (single-tenant, English-only).

---

## 4. Spacing, Radius, Shadows, Breakpoints

### Spacing — base unit 4px

`4 · 8 · 12 · 16 · 20 · 24 · 32 · 40 · 48 · 64`

| Token | Px | Use |
|---|---|---|
| `space-1` | 4 | Icon↔label gap |
| `space-2` | 8 | Form-row inner gap, chip padding |
| `space-3` | 12 | Card inner gap, list-row padding |
| `space-4` | 16 | Default block/section padding |
| `space-5` | 20 | KPI card padding |
| `space-6` | 24 | Card padding, modal padding, section spacing |
| `space-8` | 32 | Section-to-section vertical rhythm |
| `space-10` | 40 | Page top padding (web) |
| `space-12` | 48 | Large gaps, empty-state vertical |
| `space-16` | 64 | Hero/auth vertical |

### Radius

- `radius-sm` 4px — badges, chips, small inputs, checklist toggles
- `radius` 8px — buttons, inputs, default (energetic-but-clean rounding)
- `radius-md` 12px — cards, KPI tiles, modals, photo thumbnails
- `radius-lg` 16px — bottom sheets (top corners), feature cards
- `radius-full` — pills, avatars, FAB, status dots

### Shadows / elevation

Light theme → depth from soft navy-tinted shadows + hairlines (never harsh black).

- `shadow-sm` `0 1px 2px rgba(22,40,74,.06)` — cards at rest, table container
- `shadow` `0 2px 8px rgba(22,40,74,.08)` — raised cards, dropdowns, sticky topbar bottom
- `shadow-md` `0 8px 24px rgba(22,40,74,.12)` — modals, popovers, toasts, bottom sheet, FAB
- **Focus rings:** orange (`--ring-orange`) on primary controls; blue (`--ring-blue`) on inputs/links. Always 3px, no offset gap on dark; 2px offset on light.

### Breakpoints

| Name | Width | Target |
|---|---|---|
| `mobile` | 360–430 | **Flutter floor app (portrait)** — design baseline 390px |
| `tablet` | 768 | Manager tablet (web responsive) |
| `laptop` | 1024 | Small desktop |
| `desktop` | **1440** | **Primary web target** (OH/SM/TL admin panel) |
| `wide` | 1680+ | Large monitors (max content width capped at 1440 content well + fluid gutters) |

> **Flutter token parity:** spacing scale, radius, and shadow elevations map 1:1 into Flutter `AmSpacing`, `AmRadius`, and `BoxShadow` constants so the P2 mobile build is visually identical to the web companion screens.

---

## 5. Iconography

- **Library: [Lucide](https://lucide.dev)** (MIT, consistent 2px stroke, broad coverage). Web via `lucide-react`; Flutter via `lucide_icons` (or bundled SVGs).
- **Sizes:** 16px (dense table/inline) · 20px (sidebar nav, buttons, form affordances) · 24px (topbar, mobile bottom-nav, FAB, checklist camera/toggle).
- **Default color:** `brand-navy` / `gray-500`; **active/primary intent** → `brand-orange`; **destructive/A** → `brand-red`.
- **Key glyphs (canonical):** `check-circle-2` = G/approved · `alert-triangle` = A/Action-required · `camera` = photo capture · `image` = evidence thumbnail · `clock` = time/overdue · `list-checks` = checklist · `clipboard-list` = Upload Check List / template builder *(CL-1)* · `calendar-days` = roster · `users` = team · `user-plus` = assign (technician / cleaning staff / reassign) *(CL-2/CL-4/CL-5)* · `wrench` = maintenance / work-order *(CL-2)* · `git-branch` / `network` = hierarchy · `building-2` = Game Zone · `gamepad-2`/`map-pin` = activity zone/ride · `bell` = notifications · `arrow-up-circle` = escalation/cascade-up / outsource · `undo-2` = send-back / return-with-reason · `upload` = bulk upload.
- **Never use emoji** in chrome or components.

---

## 6. Component Catalog

Each component lists purpose, key props/variants, and representative Screen IDs (from `SCREEN_LIST.md`) that use it.

### 6.1 AppShell
**Purpose:** the outer frame. Web = navy sidebar + topbar + content; mobile = top app bar + content + bottom nav.
**Props:** `surface: 'web' | 'mobile'`, `role`, `gameZoneScope` (`all | own | n/a`), `activeNav`.
**Used by:** every non-auth screen. See §8 canonical shells.

### 6.2 Sidebar (web, data-driven per role)
**Purpose:** fixed left nav, derived from the role's nav list (§7) — never hardcoded per screen.
**Spec:** fixed **240px**, full-height, bg `brand-navy`, structure: wordmark lockup block (64px) → nav groups (eyebrow group label in `brand-light-blue` + items) → flex spacer → utility cluster (Notifications · Profile · Settings · Logout) → user chip.
**Nav item:** 44px tall, 12px padding, Lucide 20px + `body-md` label. Hover: bg `brand-navy-soft`. **Active:** bg `brand-navy-soft` + **3px `brand-orange` left-edge bar** + icon & label `brand-orange`.
**Props:** `items[]`, `groups[]`, `active`, `collapsed`.
**Used by:** all OH (`OH-*`) and SM (`SM-*`) web screens; TL web (`TL-*` web surface).

### 6.3 Topbar
**Purpose:** top chrome on web (64px, sticky) — breadcrumb/page context left, global search center, scope chip + bell + profile right.
**Scope affordances (critical):**
- **OH:** a **Game-Zone selector chip** — `building-2` icon + "All Game Zones ▾" (default) or the chosen branch; switching re-scopes all `[GZ: all]` views. Canonical label: `Game Zone · All` / `Game Zone · <name>`.
- **SM:** a **non-interactive branch badge** — `building-2` + the SM's single Game Zone name (`Game Zone · <name>`), since SM is locked to one branch (§4c).
- **TL:** branch badge + team indicator.
**Props:** `gzSelector: boolean`, `gzBadge: string`, `search`, `notifCount`.
**Used by:** all web screens. (`OH-DASH`, `OH-CHK-OVERVIEW`, `SM-DASH`, `TL-DASH`, …)

### 6.4 Button
**Heights:** 44px default (web) / 52px mobile-primary (thumb) / 36px dense (table inline).
**Radius** 8px. Label `button` (mobile 16px).

| Variant | Fill | Text | Border | Hover | Use |
|---|---|---|---|---|---|
| **Primary** | `brand-orange` | **`brand-navy`** | none | darken 6% | Main CTA (Submit, Publish, Approve, + New) |
| **Secondary** | transparent | `brand-blue` | 1px `brand-blue` | bg `brand-blue-soft` | Secondary action |
| **Ghost** | transparent | `brand-navy` | none | bg `surface-2` | Tertiary, table inline |
| **Approve** | `success` | white | none | darken 6% | Approve step in cascade |
| **Destructive / Send-back** | `brand-red` | white | none | darken 6% | Reject / Send back / Delete |
| **FAB (mobile)** | `brand-orange` circle | navy icon | none | scale 1.04 | Camera / primary mobile action |

> Approve = green, Send-back/Reject = red-orange — matched to the G/A and state-machine semantics so the action color always equals the outcome color.
**Used by:** `*-CHK-APPROVE`, `*-CHK-VERIFY`, `*-CHK-SUBMIT`, `SM-ROSTER-PUBLISH`, `OH-USER-NEW`, etc.

### 6.5 Input / Select / FormField
**Field:** 44px (mobile 52px), radius 8px, bg `surface`, border 1px `gray-300`, padding 12×14. Label above in `eyebrow` `gray-500`. Helper/error below in `caption`.
**States:** focus → border `brand-blue` + `--ring-blue`; error → border `danger` + helper in `danger`; disabled → bg `gray-50`, text `gray-400`.
**Variants:** text, password (with reveal), email/phone, select (Lucide `chevron-down`), multiselect (certified-rides chips), date, textarea (notes), search.
**Used by:** all auth screens, `OH-USER-NEW`, `SM-USER-NEW`, `OH-GZ-NEW`, roster templates, checklist notes.

### 6.6 DataTable
**Purpose:** rosters, user lists, checklist overviews, reports.
**Spec:** container `surface` + `shadow-sm` + radius-md; header row bg `surface-3`, `eyebrow` header text, sticky; body rows 52px (`body`), 1px `gray-200` divider; optional zebra `surface-2` for >10 rows; hover `surface-2`; selected row 3px `brand-orange` left edge + `brand-orange-soft` wash.
**Features:** search bar, column sort, status-badge cells, inline action icons (View/Edit), row checkboxes + bulk-action bar, pagination footer ("Showing 1–25 of 248", page size select), `Export ▾` (PDF/Excel), filter chips, empty state.
**Used by:** `OH-USERS`, `OH-GZ-LIST`, `OH-CHK-OVERVIEW`, `SM-USERS`, `SM-CHK-OVERVIEW`, `TL-CHK-OVERVIEW`, all `*-REP-*`.

### 6.7 Modal / Drawer
**Modal:** overlay rgba(22,40,74,.55); panel `surface`, radius-md, `shadow-md`, widths 480/640/880. Header H3 + close X; body 24px; footer right-aligned (Cancel ghost + Primary/Approve/Destructive).
**Drawer:** right-side 420px slide-in for detail/quick-edit (user detail, checklist detail with photo viewer) without leaving the list.
**Patterns:** confirm (delete/reassign), send-back-reason (required textarea), assign/override, publish-roster confirm (fires auto-assign).
**Used by:** `OH-GZ-NEW`, `OH-USER-EDIT` (reassign-manager confirm), `*-CHK-APPROVE` (send-back-reason), `SM-ROSTER-PUBLISH`, `SM-CHK-ASSIGN`.

### 6.8 BottomSheet (mobile)
**Purpose:** mobile equivalent of modal/drawer for floor flows — slides from bottom, radius-lg top corners, drag handle, `shadow-md`.
**Uses:** photo source picker (Camera / Gallery), G/A note entry, submit-confirm, send-back banner detail, filter sheet.
**Used by:** `SCM-CHK-FILL`, `MON-CHK-FILL`, `CS-CHK-FILL`, `*-CHK-SUBMIT`.

### 6.9 Toast / Snackbar
Top-right (web) / top (mobile), 360px, radius-md, `shadow-md`, semantic 4px left edge + Lucide icon + message + dismiss. Auto-dismiss: success/info 3s, warning 5s, error/sent-back manual. Used for: submit success, approve success, send-back issued, upload complete, network-retry.

### 6.10 KpiCard
**Purpose:** dashboard metrics. `surface`, radius-md, padding 20px. `eyebrow` label (`gray-500`) → `display` number (in `brand-navy`, or `brand-orange` for the headline metric) → optional trend/`StatusBadge` row.
**Variants:** plain count, count + delta, count + status pill (e.g. "Pending · 12" amber).
**Used by:** `OH-DASH` (per-GZ compliance, Pending counts, overdue, roster gaps), `SM-DASH`, `TL-DASH`, mobile dashes (`SCM-DASH`, `MON-DASH`, `CS-DASH`).

### 6.11 StatusBadge (the state-machine + G/A) — critical
**Purpose:** render any checklist/approval state with the locked color map (§2.4) and G/A.
**Props:** `state` (`not-started | in-progress | submitted | tl-approved | sm-approved | oh-approved | held | pending | sent-back | overdue`) **or** `ga` (`G | A`) **or** `wo` (`open | routed | assigned | in-progress | returned | done | outsourced | overdue`) *(CL-2 work-order track)*; `size: sm|md`; `solid?: boolean`.
**Style:** 22px pill, radius-full, `eyebrow` 11px text; default = `<color>1F` bg + solid `<color>` text; **`overdue` and solid variants** = solid fill + white text. G = green dot+label, A = red-orange dot+label.
**Pending derivation:** the consuming view passes the **viewer's role**; the badge component resolves to `Pending` (amber) when the instance isn't yet approved through the viewer's level (§9 #5) — the stored sub-state is shown to the filler/lower roles.
**Used by:** every checklist surface — `*-CHK-OVERVIEW`, `*-CHK-STATUS`, `*-CHK-TODAY`, `OH-DASH`, reports, ChecklistItem.

### 6.12 PhotoUpload / Evidence component — critical (§4a)
**Purpose:** capture and attach mandatory evidence photos. Two modes, same component:
1. **Completion evidence** — one or more photos required before a checklist can be submitted (gates `*-CHK-SUBMIT`).
2. **Per-A-item evidence** — when an item is set to **A (Action required)**, a separate photo of the issue is **required** before submit.
**Behavior:** big tap target (mobile ≥ 96px tile), `camera` primary + `image` gallery (via BottomSheet on mobile); shows upload progress bar; thumbnail (radius-md) with `×` remove; **blocks submit with an inline `danger` message if a required photo is missing** ("A-item needs a photo", "Add completion photo to submit"). Stores against checklist instance + item with uploader, timestamp, device (per §4a). Multipart POST to the Express API → **local-disk storage on the VPS** (no presigned cloud URLs).
**Viewer mode:** managers see a photo gallery + lightbox (full-screen, swipe, item context caption) in approval screens and negative reports.
**Empty placeholder:** dashed `gray-300` border, `camera` icon `gray-400`, "No photo" caption.
**Used by:** `SCM-CHK-FILL`, `MON-CHK-FILL`, `CS-CHK-FILL`, `TL-CHK-FILL`, `*-CHK-SUBMIT` (gate), `*-CHK-APPROVE`/`*-CHK-VERIFY` (viewer), `OH-REP-NEGATIVE`, `SM-REP-NEGATIVE`, `TL-REP-NEGATIVE`.

### 6.13 ChecklistItem row — critical
**Purpose:** one checklist line item with the **G/A toggle + note + time + photo** in a thumb-friendly row.
**Layout (mobile):** item text (top, `body-md` navy) → **segmented G | A toggle** (large, 44px tall: G = green when selected, A = red-orange when selected; nothing selected = neutral, prompts action) → conditional **note** field + **time** auto-stamp (editable) → conditional **A-photo** tile (appears + becomes required when A is chosen) → initials capture.
**States:** untouched (neutral), G-selected (green left rail), A-selected (red-orange left rail + photo-required flag), draft-saved, error (missing required A-photo).
**Props:** `item`, `value: 'G'|'A'|null`, `note`, `time`, `photos[]`, `requiresPhotoOnA: true`.
**Used by:** `SCM-CHK-FILL`, `MON-CHK-FILL`, `CS-CHK-FILL`, `TL-CHK-FILL`.

### 6.14 ApprovalTimeline / Cascade component — critical (§4b)
**Purpose:** show the full cascade — **Filled by → TL → Store Manager → Operation Head** — with each step's state, who/when, and a clear "pending here" marker.
**Layout:** horizontal (web) / vertical (mobile) stepper of 4 nodes:
`Filled (Monitor/CS) ✓` → `TL` → `Store Manager` → `Operation Head (Done)`.
Each node: actor name + role, timestamp, StatusBadge (approved ✓ green / pending amber / current = ring / sent-back red). A **sent-back** node shows the reject reason inline and routes back to the original filler (§9 #3).
**Props:** `steps[] {role, actor, at, state}`, `viewerRole`, `sentBackReason?`, `variant?: 'approval' | 'workorder'` *(CL-2)*.
**Work-Order variant (CL-2):** the same stepper renders the maintenance track — `Routed (Ops TL) → Maintenance TL → Technician → Closed/Outsourced` — using the `wo-*` token set, with a Technician **return-reason** shown inline (parallel to send-back). When a checklist is **Held**, its status view embeds this WO timeline per open A-item.
**Used by:** `*-CHK-STATUS` (filler view), `*-CHK-OVERVIEW` (drilldown), `*-CHK-APPROVE`/`*-CHK-VERIFY`, `OH-CHK-OVERVIEW`; **work-order variant:** `MTL-WO-DETAIL`, `MTL-WO-REVIEW`, `TECH-WO-DETAIL`, `OH-WO-OVERVIEW`, `SM-WO-OUTSOURCE`.

### 6.15 EmptyState
Centered Lucide icon (`gray-400`) + `h3` headline + `body-sm` `gray-500` subtitle + optional primary action. Variants: no-data, no-results (after filter), all-clear (positive — green check, "All checklists compliant"), nothing-assigned (floor: "No checklists assigned for your shift yet").
**Used by:** `SH-NOTIF`, `*-CHK-TODAY`, reports with no results, rosters with no shifts.

### 6.16 Tabs
Underline tabs, active = `brand-orange` underline + `brand-navy` label; inactive `gray-500`. Used for: `OH-GZ-DETAIL` (staff/rides/rosters/checklist tabs), overview Active/Overdue tabs, report period tabs.

### 6.17 Stepper / Wizard
**Purpose:** multi-step flows. Numbered steps with progress; Back/Continue action bar (replaces bottom-nav on mobile sub-steps).
**Uses:** **roster bulk-upload** (Upload file → Validate/dry-run → Review errors → Commit) and onboarding (`OH-USER-NEW`: details → role → reports-to → Game Zone → certified rides). Mobile fill→submit is a light 2-step (Fill → Review/Submit).
**Used by:** `SM-ROSTER-UPLOAD`, `OH-USER-NEW`, `SM-USER-NEW`, `*-CHK-SUBMIT`.

### 6.18 OccupancyMeter (future — P3, defined now for token parity)
Horizontal capacity meter (of 400): green <70%, amber 70–90%, red >90% (uses `success`/`warning`/`danger`). Not used in Phase 1 screens; tokens reserved so the P3 Capacity dashboard inherits the palette. **Stubbed, not built in Phase 1.**

### 6.19 Calendar / Roster grid
**Purpose:** day/week/month roster views and the drag-drop shift builder.
**Spec:** column-per-day (week) or cell-per-day (month); rows = staff or rides; shift blocks colored by shift template (Morning `brand-light-blue` wash / Evening `brand-blue` wash); leave overlay (`gray-300` hatch); **gap-alert badge** (`danger`) where a required ride/shift is unstaffed; drag-drop in builder with conflict warning.
**Used by:** `SM-ROSTER`, `TL-ROSTER`, `OH-ROSTER-ALL`, `SM-ROSTER-LEAVE`, `*-ROSTER-OWN`, `TL-ROSTER-VIEW`.

### 6.20 HierarchyTree (supporting)
**Purpose:** the **9-role** org tree — OH → SM → { **operations TL/Trainee** → Monitors/CS } **and** { **Maintenance TL** → Technicians } (CL-2). Expand/collapse nodes, per-Game-Zone filter (OH), role-colored node chips.
**Used by:** `OH-HIER-TREE`, `SM-HIER-TREE`.

### 6.21 ChecklistBuilder (Upload Check List) — critical *(CL-1, new)*
**Purpose:** create/edit a checklist template ("Upload Check List"). Replaces the old view-only templates screen.
**Builder fields:** template **name**; **frequency** (`daily | weekly | monthly | quarterly | yearly | opening | closing`); **ride / activity zone**; **which role fills it** (role picker); **Game-Zone scope**; **sections → items** (each item: text, **test method**, input type **G/A**, `requires-photo-on-A`).
**Layout:** left = section/item tree with add/reorder (drag); right = item editor. Uses **Stepper** (§6.17): *Details → Sections & Items → Assign (role/ride/shift) → Review*. Save = `Primary` (orange) button.
**Assign sub-step:** maps the template to role / ride / shift / GZ scope → feeds the auto-assign engine (FOUNDATION_SPEC §3). Edit reopens the same builder.
**RBAC:** OH (all GZ) + SM (own GZ) full; **TL = team-scoped subset** (own team). 
**Used by:** `OH-CHK-UPLOAD` / `OH-CHK-TPL-ASSIGN` / `OH-CHK-TPL-EDIT`, `SM-CHK-UPLOAD` / `SM-CHK-TPL-ASSIGN` / `SM-CHK-TPL-EDIT`, `TL-CHK-UPLOAD`.

### 6.22 WorkOrderCard / WorkOrderDetail — critical *(CL-2, new)*
**Purpose:** represent one Action work-order branched off an A-item.
**Card:** source checklist + ride + A-item text, `StatusBadge wo=…`, assigned Technician avatar, age/SLA chip; tap → detail.
**Detail:** the source A-item + its **issue photos**, reason/return history, the **WorkOrder variant of ApprovalTimeline** (§6.14), and a context action bar (Assign / Reassign / Mark Done+photo / Return-with-reason / Escalate-Outsource) gated by role. Technician **Done** requires a **fix photo** (reuses PhotoUpload §6.12, `kind: COMPLETION`); **Return** requires a reason (reuses the send-back reason modal pattern).
**Used by:** `MTL-WO-QUEUE`, `MTL-WO-DETAIL`, `MTL-WO-ASSIGN`, `MTL-WO-REVIEW`, `MTL-WO-ESCALATE`, `TECH-WO-TODAY`, `TECH-WO-DETAIL`, `TECH-WO-DONE`, `TECH-WO-RETURN`, `OH-WO-OVERVIEW`, `SM-WO-OUTSOURCE`.

### 6.23 AllocationBoard (assign work) *(CL-4 reassign + CL-5 cleaning)*
**Purpose:** a light staff↔target allocation surface, shared by two flows:
1. **TL mid-shift reassign (CL-4):** reassign a monitor to a different ride mid-shift; on save the new ride's checklists auto-assign to the new monitor, the old ride re-targets to whoever now holds it, and the monitor's mobile `*-CHK-TODAY` updates live (shows a **ride-changed banner**).
2. **Cleaning Supervisor assign (CL-5):** the CS allocates cleaning staff to areas/zones, then fills the cleaning checklist himself.
**Layout:** two-column picker (staff list ↔ ride/area) with confirm; conflict + re-target warnings. Web = modal/drawer (`TL-ROSTER-REASSIGN`); mobile = BottomSheet (`CS-ASSIGN`).
**Used by:** `TL-ROSTER-REASSIGN`, `CS-ASSIGN`.

### 6.24 HeldBanner / Action-hold indicator *(CL-2)*
**Purpose:** make the **single-track hold** unmistakable: when a checklist has open A-item work-orders it shows the `st-held` (deep-orange) badge plus a "**N actions open — checklist held until resolved**" banner that links to each WO. Once every WO closes, the checklist resumes its approval cascade to Done.
**Used by:** `TL-CHK-VERIFY`, `*-CHK-OVERVIEW`, `*-CHK-STATUS`, `OH-CHK-OVERVIEW`.

---

## 7. Per-Role Sidebar / Bottom-Nav Definitions

Derived strictly from the access matrix (PROJECT_PLAN §3, amended by CHANGE_LIST v1.1) and `SCREEN_LIST.md` surfaces. **9 roles:** web-primary = OH, SM, **Maintenance TL** (CL-2); Both = TL/Trainee TL; mobile-primary = SCM, MON, CS, **Technician** (CL-2). Utility cluster (**Notifications · Profile · Settings · Logout**) + user chip are appended to every web sidebar and are not repeated below. Mobile roles get exactly 5 bottom-nav slots. **Game-Zone scope lives in the topbar** (OH selector / SM-TL-MTL badge), not the nav list.

### Web sidebars

#### Operation Head (OH) — web-primary · `[GZ: all]` · 10 nav items, 3 groups

| Group | Item | Lucide | Target screen |
|---|---|---|---|
| OVERVIEW | Dashboard | `layout-dashboard` | `OH-DASH` |
| OVERVIEW | Game Zones | `building-2` | `OH-GZ-LIST` |
| OVERVIEW | Hierarchy | `network` | `OH-HIER-TREE` |
| PEOPLE | Users | `users` | `OH-USERS` |
| PEOPLE | Roles & Access | `shield-check` | `OH-ROLES` |
| OPERATIONS | Roster | `calendar-days` | `OH-ROSTER-ALL` |
| OPERATIONS | Checklists | `list-checks` | `OH-CHK-OVERVIEW` |
| OPERATIONS | Upload Check List *(CL-1)* | `clipboard-list` | `OH-CHK-UPLOAD` (→ Assign / Edit) |
| OPERATIONS | Work-Orders *(CL-2)* | `wrench` | `OH-WO-OVERVIEW` |
| OPERATIONS | Reports | `bar-chart-3` | `OH-REP-POSITIVE` (tabs → Negative / Overdue) |

> OH topbar shows the **Game-Zone selector** (`Game Zone · All ▾`). Rides master (`OH-RIDE-MASTER`) is reached from within `OH-GZ-DETAIL`; `OH-CHK-APPROVE` opens from the Checklists overview.

#### Store Manager (SM) — web-primary · `[GZ: own]` · 8 nav items, 3 groups

| Group | Item | Lucide | Target screen |
|---|---|---|---|
| OVERVIEW | Dashboard | `layout-dashboard` | `SM-DASH` |
| OVERVIEW | Hierarchy | `network` | `SM-HIER-TREE` |
| PEOPLE | Team / Users | `users` | `SM-USERS` |
| OPERATIONS | Roster | `calendar-days` | `SM-ROSTER` (→ Upload / Templates / Leave / Publish) |
| OPERATIONS | Checklists | `list-checks` | `SM-CHK-OVERVIEW` (→ Assign / Approve) |
| OPERATIONS | Upload Check List *(CL-1, replaces Templates)* | `clipboard-list` | `SM-CHK-UPLOAD` (→ Assign / Edit) |
| OPERATIONS | Outsource Inbox *(CL-2)* | `wrench` | `SM-WO-OUTSOURCE` |
| OPERATIONS | Reports | `bar-chart-3` | `SM-REP-POSITIVE` (tabs → Negative / Overdue) |

> SM topbar shows a **non-interactive branch badge** (`Game Zone · <name>`). Everything scoped to the SM's one Game Zone. The old view-only **Templates** item is replaced by the full **Upload Check List** builder (CL-1); the **Outsource Inbox** receives only escalated/outsource work-orders from the Maintenance TL (CL-2).

#### Team Leader / Trainee TL — Both (web sidebar) · `[GZ: own · team]` · 7 nav items, 2 groups

> **Trainee TL = assist-only (CL-3, amends §9 #4):** identical **nav**, but **Approve / Send-back / Route-to-Maintenance actions are disabled** (read/assist subset). The RBAC resolver hides/disables those buttons; only a full TL approves. Same screens, gated controls.

| Group | Item | Lucide | Target screen |
|---|---|---|---|
| OVERVIEW | Dashboard | `layout-dashboard` | `TL-DASH` |
| OVERVIEW | My Team | `users` | `TL-TEAM` |
| OPERATIONS | Roster | `calendar-days` | `TL-ROSTER` (→ `TL-ROSTER-VIEW` / **Mid-Shift Reassign** CL-4) |
| OPERATIONS | Checklists | `list-checks` | `TL-CHK-OVERVIEW` (→ Verify [+ **Route to Maintenance** CL-2] / Assign / Fill) |
| OPERATIONS | Upload Check List *(CL-1)* | `clipboard-list` | `TL-CHK-UPLOAD` (team-scoped) |
| OPERATIONS | Reports | `bar-chart-3` | `TL-REP-NEGATIVE` |
| OPERATIONS | Fill Checklist | `clipboard-check` | `TL-CHK-FILL` |

> TL topbar shows branch badge + team indicator. On **mobile**, TL uses the bottom-nav below. The **Route to Maintenance** action lives inside `TL-CHK-VERIFY` (sends an A-item to the Maintenance TL and **Holds** the checklist).

#### Maintenance Team Leader (MTL) — web-primary + mobile companion · `[GZ: own]` · 5 nav items, 2 groups *(CL-2, new)*

| Group | Item | Lucide | Target screen |
|---|---|---|---|
| OVERVIEW | Dashboard | `layout-dashboard` | `MTL-DASH` |
| OPERATIONS | Work-Order Queue | `wrench` | `MTL-WO-QUEUE` (→ Detail / Assign / Review) |
| OPERATIONS | Assign | `user-plus` | `MTL-WO-ASSIGN` |
| OPERATIONS | Escalate / Outsource | `arrow-up-circle` | `MTL-WO-ESCALATE` |
| PEOPLE | Maintenance Team | `users` | `MTL-TEAM` |

> MTL topbar shows a **branch badge** (`Game Zone · <name>`) like SM/TL. Reports to the Store Manager; only **outsource** WOs are escalated up (`MTL-WO-ESCALATE` → `SM-WO-OUTSOURCE`). Mobile companion mirrors the queue + review actions for on-floor use.

### Mobile bottom-navs (5 slots each)

#### Team Leader / Trainee TL — mobile (Both)

| Slot | Label | Lucide | Target |
|---|---|---|---|
| 1 | Home | `layout-dashboard` | `TL-DASH` |
| 2 | Verify | `clipboard-check` | `TL-CHK-VERIFY` / `TL-CHK-OVERVIEW` |
| 3 | Roster | `calendar-days` | `TL-ROSTER-VIEW` |
| 4 | Alerts | `bell` | `SH-NOTIF` |
| 5 | More | `more-horizontal` | Profile / Settings / Logout |

#### Senior Court Monitor (SCM) — mobile-primary · `[GZ: own · team]`

| Slot | Label | Lucide | Target |
|---|---|---|---|
| 1 | Home | `layout-dashboard` | `SCM-DASH` |
| 2 | Checklists | `list-checks` | `SCM-CHK-TODAY` → `SCM-CHK-FILL` / `SCM-CHK-SUBMIT` |
| 3 | Status | `git-branch` | `SCM-CHK-STATUS` |
| 4 | Roster | `calendar-days` | `SCM-ROSTER-OWN` |
| 5 | More | `more-horizontal` | Alerts / Profile / Logout |

#### Staff Court Monitor (MON) — mobile-primary · `[GZ: own · team]`

| Slot | Label | Lucide | Target |
|---|---|---|---|
| 1 | Home | `layout-dashboard` | `MON-DASH` |
| 2 | Checklists | `list-checks` | `MON-CHK-TODAY` → `MON-CHK-FILL` / `MON-CHK-SUBMIT` |
| 3 | Status | `git-branch` | `MON-CHK-STATUS` |
| 4 | Roster | `calendar-days` | `MON-ROSTER-OWN` |
| 5 | More | `more-horizontal` | Alerts / Profile / Logout |

#### Cleaning Supervisor (CS) — mobile-primary · `[GZ: own · team]`

| Slot | Label | Lucide | Target |
|---|---|---|---|
| 1 | Home | `layout-dashboard` | `CS-DASH` |
| 2 | Cleaning | `spray-can` | `CS-CHK-TODAY` → `CS-CHK-FILL` / `CS-CHK-SUBMIT` |
| 3 | Assign *(CL-5)* | `user-plus` | `CS-ASSIGN` (allocate cleaning staff to areas) |
| 4 | Status | `git-branch` | `CS-CHK-STATUS` |
| 5 | More | `more-horizontal` | Roster / Alerts / Profile / Logout |

> CS slot-3 **Assign** (CL-5) reflects the supervisor's light task-allocation right; `CS-ROSTER-OWN` moves into **More**. The CS still **fills** the cleaning checklist himself.

#### Technician (TECH) — mobile-primary · `[GZ: own · team]` *(CL-2, new)*

| Slot | Label | Lucide | Target |
|---|---|---|---|
| 1 | Home | `layout-dashboard` | `TECH-DASH` |
| 2 | Work-Orders | `wrench` | `TECH-WO-TODAY` → `TECH-WO-DETAIL` |
| 3 | Done | `check-circle-2` | `TECH-WO-DONE` (mark fixed + photo) |
| 4 | Returned | `undo-2` | `TECH-WO-RETURN` (return with reason) |
| 5 | More | `more-horizontal` | Alerts / Profile / Logout |

> **Filler-role parity:** SCM / MON / CS share the identical fill→submit→status nav shape (none can approve, per §3) — only the auto-assigned templates differ (ride-safety vs cleaning/hygiene) and CS's slot-2 label/icon reflects cleaning. Monitors/CS have **no Approve/Verify slot**. The thin web read-only fallback for these roles reuses the same five destinations as a minimal left rail. The **Technician** (CL-2) is a maintenance filler: instead of checklists he works **Work-Orders** (Done-with-photo / Return-with-reason), reusing the same PhotoUpload + reason patterns; no approval slot.

---

## 8. Canonical Shells

### 8.1 Web shell (OH / SM / TL) — navy 240px sidebar + topbar + content

```
┌───────────────────────────────────────────────────────────────────────────────┐
│ ▌AIR MANIAX   [ search… ]        🏢 Game Zone · All ▾    🔔3   Asha (OH) ▾     │ ← Topbar 64px
├──────────┬────────────────────────────────────────────────────────────────────┤
│ AIR      │  Dashboard › Checklists › Overview                                  │ ← Breadcrumb 40px
│ MANIAX   ├────────────────────────────────────────────────────────────────────┤
│          │  Checklist Status Overview                       [ Export ▾ ]       │ ← Page H1 + action
│ OVERVIEW │  ┌ KPI ┐ ┌ KPI ┐ ┌ KPI ┐ ┌ KPI ┐                                   │
│ ▸Dashbd  │  │Pend │ │Over │ │Done │ │Gaps │                                    │
│  Game…   │  └─────┘ └─────┘ └─────┘ └─────┘                                    │
│  Hierar  │  ┌────────────────── DataTable ──────────────────┐                  │
│ PEOPLE   │  │ Ride · Shift · Filler · Cascade · Status      │                  │
│  Users   │  └───────────────────────────────────────────────┘                  │
│ ……       │                                                                     │
│ ──────── │                                                                     │
│ 🔔 Notif │                                                                     │
│ 👤 chip  │                                                                     │
└──────────┴────────────────────────────────────────────────────────────────────┘
```

- **Sidebar:** fixed 240px, bg `brand-navy`, white text, `brand-light-blue` group labels, **orange active accent**. Wordmark lockup top (AIR in orange / MANIAX in white).
- **Topbar:** 64px sticky, `surface` bg + `shadow-sm` bottom. Search center; **OH = Game-Zone selector chip**, **SM/TL = branch badge**; bell; profile.
- **Content:** `bg` `#F5F7FA`, max content well 1440, 40px top / 32px side padding. Page H1 + primary action top-right.

### 8.2 Mobile shell (SCM / MON / CS / TL-mobile) — navy top app bar + bottom nav

```
┌──────────────────────────────────┐
│ ☰   Today's Checklists    🔔  👤 │ ← Top app bar 56px (navy, white)
├──────────────────────────────────┤
│  Game Zone · Air Maniax Ahd      │ ← scope strip (read-only, light-blue)
│                                  │
│  ┌ Trampoline Daily ─ Overdue ┐  │
│  │ ● ●●● items   📷 needed     │  │
│  └────────────────────────────┘  │
│  ┌ ZipZag Daily ─ In progress ┐  │
│  └────────────────────────────┘  │
│                            ╭───╮ │
│                            │📷 │ │ ← FAB (orange) on fill screens
├──────────────────────────────────┤
│  ⌂      ✓      ⑂      ▤      ⋯   │ ← Bottom nav 56px, 5 slots
│ Home Checkl Status Roster  More  │   active = orange
└──────────────────────────────────┘
```

- **Top app bar:** 56px, bg `brand-navy`, white title + bell + avatar; scope strip below in `brand-light-blue` wash showing the Game Zone.
- **Bottom nav:** 56px, `surface` bg, 1px `gray-200` top edge, 5 slots; inactive `gray-500`, **active `brand-orange`**; min touch target 44×44.
- **FAB:** 56px `brand-orange` circle, navy `camera` icon, on fill screens only.
- **Energetic accents:** orange FAB + orange active nav + orange primary buttons against the calm navy/white frame.

---

## 9. States

| State | Web | Mobile (floor) |
|---|---|---|
| **Loading** | Skeleton rows/cards (`gray-100` shimmer); never spinner-only for tables | Skeleton list; top-bar progress bar |
| **Empty** | `EmptyState` (no-data / no-results / all-clear positive / nothing-assigned) | Same; "No checklists assigned for your shift yet" |
| **Error (generic)** | Inline error card + retry; toast for transient | Full-screen error with retry button |
| **Offline / network failure** | Toast "No connection — retry" | **Clean "No connection — retry" screen, NOT an offline queue** (mobile is online-only); preserves entered draft in memory and re-submits on retry |
| **Form validation** | Field-level `danger` borders + helper; submit blocked | Same; **photo-required block on submit** (completion + per-A-item) |
| **401 (session expired)** | Redirect to `AUTH-LOGIN` with "Session expired, sign in again" | Same |
| **403 (no permission)** | "You don't have access to this Game Zone / action" page; no data leak across Game Zones | Same; respects `[GZ: own]` scope |
| **Lockout (rate-limited login)** | `AUTH-LOGIN` locked state — countdown + "Too many attempts" | Same |
| **Sent-back** | Banner on filler's `*-CHK-STATUS`: red `Sent back` + reason + "Re-fill" CTA → reopens fill | Prominent red banner + re-fill CTA |
| **Overdue** | Solid-red `Overdue` badge + escalation indicator on overview/dashboard | Red badge on `*-CHK-TODAY` |

---

## 10. Accessibility & Mobile-First Notes

- **One-handed floor use:** primary mobile actions sit in the bottom 60% of the screen (thumb zone). **Mobile primary buttons 52px, FAB 56px, G/A toggle 44px tall, bottom-nav slots 44×44 min.** No critical control in the top corners on fill screens.
- **G/A toggle is unmistakable:** large segmented control, color + icon + label (not color alone) — `check-circle-2` green for G, `alert-triangle` red-orange for A — so it is legible for color-vision-deficient users and under glare.
- **Camera is one tap:** `PhotoUpload` opens the camera directly via FAB / inline tile; gallery is the secondary path in the BottomSheet.
- **Contrast locked (§2.5):** white-on-navy AAA; **navy-on-orange for small button labels (AA)**; white-on-orange only for large/bold/icons; solid-red destructive labels ≥14px bold.
- **Status never by color alone:** every StatusBadge carries a text label + (where space allows) an icon; G/A and overdue add a shape/fill cue.
- **Hit feedback & focus:** visible 3px focus rings (orange on actions, blue on inputs); 150ms tap ripple on mobile.
- **Text scaling:** layouts tolerate up to 130% system font scaling without clipping; tables switch to stacked cards on `mobile`.
- **Labels & semantics:** every input has a visible label + programmatic association; icons that act as buttons carry `aria-label`; tables use proper header semantics.
- **English-only, single-tenant:** no language/tenant/theme switcher anywhere (locked).

---

*Generated for Air Maniax Staff Management CRM — Phase D / Step 2 (Design). Consistent with `docs/PROJECT_PLAN.md` (LOCKED 2026-06-18) and `docs/design/SCREEN_LIST.md`. **Amended 2026-06-19 per `docs/CHANGE_LIST_v1.1.md` (CL-1…CL-5): 9 roles / 95 screens, Held + Work-Order state tokens, Maintenance TL + Technician navs, ChecklistBuilder / WorkOrderCard / AllocationBoard / HeldBanner components.** Save location: `docs/design/DESIGN_SYSTEM.md`. Next: Step 3 — per-screen dev-mode packs.*
</content>
</invoke>
