# 08 — Mobile Navigation Structure

The mobile app uses **Expo Router** with file-based routing.

## Top-level groups

```
app/
  _layout.tsx              # Root, locale + auth boot
  (auth)/                  # Pre-login flows
    splash.tsx
    onboarding/
      [step].tsx           # 1, 2, 3 (with Skip / Back / Next)
    login.tsx
    signup.tsx
    forgot-password.tsx
    otp/
      [purpose].tsx        # signup | login | reset
  (tabs)/
    _layout.tsx            # Bottom tab bar
    home.tsx
    discover.tsx
    bookings.tsx
    saved.tsx
    profile.tsx
  services/
    [type].tsx             # stays | activities | taxi | food | pharmacy | grocery | sim | emergency
  listing/
    [id].tsx
  room-select/
    [listingId].tsx
  checkout/
    [bookingId].tsx
  trip-planner/
    [step].tsx             # 1..4
    result/
      [tripId].tsx
  translator.tsx
  emergency.tsx
  settings/
    index.tsx
    language.tsx
    notifications.tsx
    payment-methods.tsx
    security.tsx
```

## Bottom tab bar (consistent across home and module screens)

| Tab | Icon | Default route |
|---|---|---|
| Home | home-outline | `/home` |
| Discover | compass-outline | `/discover` |
| Bookings | calendar-outline | `/bookings` |
| Saved | heart-outline | `/saved` |
| Profile | person-outline | `/profile` |

The PDF showed multiple inconsistent tab variants — we standardize on these five for all top-level screens. Trip-planner, listing detail, and translator render with a back-button header and **no** bottom tab.

## State

- **Auth state** — Zustand store with `accessToken`, `refreshToken`, `user`. Persisted via SecureStore.
- **Locale state** — Zustand store with `locale` and `isRtl`. On change, `I18nManager.forceRTL` and reload.
- **Server state** — React Query everywhere; query keys are scoped (`['bookings', userId]`).

## Deep linking

Scheme: `navi://`. Routes:

- `navi://listing/:id`
- `navi://trip/:id`
- `navi://booking/:id`
- `navi://promo/:code`

## Required per-screen states

`loading`, `empty`, `error`, `offline`, `denied`. Each has a shared component in `packages/ui` and a screen wrapper.

## Documented assumptions

1. Default locale on first launch comes from device. If unsupported, fall back to `en`.
2. Onboarding shows once per install. After Skip or Get Started, we mark `hasOnboarded` in storage.
3. OTP screen is shared across signup, login (2FA), and reset flows; behavior differs by `purpose` query.
