> Part of the PWA DevGuide (split from pwa_dev_style.md on 2026-07-02 — see that file for the index; ADR-099).

## 5. Screen Architecture (7 views)

Views shown/hidden via `showView(n)` — toggles `.active` class, no page reloads.

### View 1 — Home (`#view-home`)

**Layout:** Hero (dark navy) → Stats row (3 cards) → New Recce button → Resume Draft banner → Submissions list → Network status strip

**Hero:** "Branding Recce" title, today's date (en-IN), top-right: 🏡 Hub pill + ⚙ gear

**Stats (field agent):** Synced (green) = local synced + server-only; Pending (amber) = local non-synced; Total = all
**Stats (admin):** Showing = filtered count; Agents = unique agent count; Total = all server rows

**Resume draft banner:** Dark navy, shows "Resume draft · X ago · StoreName · Brand · X photos →". Only shown when `draft.current` has `storeName`.

**Field agent submissions list:** Last 50 local + server-only rows, newest first. Card buttons: 👁 View · ✏ Edit · 📊 Slides. Status badge (synced/pending/failed).

**Admin submissions:** Fetches 500 rows from PostgREST. Filter bar: Agent / Brand / Period / Group-by. Free-text search on store_name + city.

### View 2 — Settings (`#view-settings`)

Key sections (admin-locked panels use SHA-256 password, 10-min session):
- Brand List: pill tags with ×, add/fetch from server URL
- Store List: Excel/CSV upload (SheetJS), brand-assign dialog, preview dialog, server URL
- Sync Endpoint: two admin-locked URL inputs → `gas-url` (Sheet sync) + `slides-gas-url` (Slides generator)
- Geolocation Stamp: toggle switch
- Image Quality: Compressed [default, green "Recommended"] / HD
- Danger Zone: Clear all local data

### View 3 — Form Step 1: Store Details (20%)

Three modes: Store picker (`.povl` overlay, 100-result cap, grouped by brand) · Store selected card (all fields, missing fields editable inline) · Manual entry (Brand + Name + Address + GPS capture + Mobile)

**Validation:** Date · Store name · Brand · Address all required

### View 4 — Form Step 2: Store Photos (40%)

Front: min 1, max 3. Inside: optional, max 3. Both use same photo-src-dialog → Camera/Gallery → compress → GPS stamp (camera only) → saveDraft.

**Validation:** At least 1 front photo required

### View 5 — Form Step 3: Video (60%)

Yes/No radio. Yes → upload slot + comment. Video stored as `videoBlob`, not uploaded (WhatsApp note generated). **Validation:** Yes or No required.

### View 6 — Form Step 4: Branding (80%)

Yes/No radio. Each branding item (`.bitem`): Type chip-grid (13 BTYPE options) · Width/Height/Qty inputs · sqft auto-calc · Photo slot → `capBr(i)` → compress → stamp → `openAnnot(i)` (annotation modal).

"Add Another" button only shown when last item fully complete (type + w + h + qty + photo). Max 10 items. Dup ↑ copies dimensions from previous item.

**Validation:** Every item must have type + w + h + qty + photo

### View 7 — Form Step 5: Additional + Submit (100%)

Mandatory document (image/* or .pdf), Additional photos (optional, max 8), Review card (all data summary), Submit button.

**Submit flow:** Save to IndexedDB (status: pending) → clear draft → if online: syncNow() → navigate home

---

## 6. Overlay / Modal System (z-index layers)

| Z | Element | Trigger |
|---|---|---|
| 200 | `.povl` Store Picker | `openPicker()` |
| 300 | `.annot-modal` Annotation | `openAnnot(i)` |
| 400 | `.bdialog-wrap` Bottom Dialogs | `.classList.add('open')` |
| 500 | `#admin-login-modal` | `showAdminLogin()` |
| 550 | `.upd-banner` App update banner | `checkAppUpdate()` on init |
| 600 | `.ardl-overlay` Admin Detail Sheet | `openAdminRecce(subId)` |
| 1000 | `#toast` | `toast(msg, type)` |

> ⚠️ **DOM ORDER RULE**: When multiple modals share the same `z-index`, the one **later in the DOM** renders on top. Always place picker/source-chooser modals AFTER the base modal they need to appear over. If a sub-picker (e.g. camera/gallery chooser) is placed before a parent modal in the DOM, it will be hidden behind the parent when both are open simultaneously.
>
> Also: when closing a parent modal (e.g. photo-edit), always call `cancelPicker()` first to clear any pending callback — otherwise the callback fires on a now-closed modal.

### Annotation Modal (branding photos only)

Draw toolbar: Rect · Ellipse · Line · Arrow · Free · Move. Color swatches: Orange[default] / Red / White / Yellow / Green. Width: Thin(2px) / Med(4px)[default] / Thick(7px). Undo / Clear.

Canvas scale: `sc = Math.min(1, innerWidth/img.w, (innerHeight-185)/img.h)`. Shapes: strokeRect with `color+'28'` fill; ellipse; moveTo→lineTo; arrow with `atan2` barbs. Move mode: 32px touch hit-test, corner resize handles.

Footer: "Skip markup" (keeps original blob) · "Done ✓" (`canvas.toBlob(jpeg, 0.88)` → replaces `brandings[i].blob`).

### Bottom Dialogs

| ID | Purpose |
|---|---|
| `#photo-src-dialog` | Camera vs Gallery picker |
| `#exit-dialog` | Exit with Save Draft / Discard / Continue |
| `#store-preview-dialog` | Excel preview before store merge |
| `#bdialog` | "Add reference photos?" after branding photo |

---

