# Solution Design Document

## Validation Checklist

- [x] All required sections are complete
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Architecture pattern is clearly stated with rationale
- [x] Every component has directory mapping
- [x] Every interface has specification
- [x] Data models follow Freezed 3.x patterns
- [x] Domain entities follow Equatable patterns
- [x] Error handling covers all error types
- [x] Quality requirements are specific and measurable
- [x] A developer could implement from this design

---

## 1. Technical Summary

### Overview
The Store Navigation Redesign transforms the store detail screen from a flat "Quick Actions" chip-based navigation into a bottom tab bar with category-specific sheets. This provides users with intuitive access to 11+ features through 5 logical category groupings, reducing cognitive load and improving navigation efficiency.

### Key Technical Decisions

| Decision | Choice | Rationale |
|----------|--------|-----------|
| Navigation Widget | Custom `StoreNavigationScaffold` with `BottomNavigationBar` | Flutter's built-in widget optimized for tab navigation; no package dependencies |
| Sheet Implementation | `showModalBottomSheet` | Native Flutter, consistent with existing patterns (TodayTasksScreen), smooth animations |
| State Management | Riverpod 3.x with `Notifier` | Consistent with app; no async needed for UI-only navigation state |
| Tab State Persistence | `StateProvider` (session-scoped) | PRD specifies session-only persistence; simpler than secure storage |
| Store Access History | `flutter_secure_storage` | PRD requires persistence across restarts for store ordering |
| Permission Filtering | Existing `permissionState.canAccess(AppPage)` | Reuse existing permission infrastructure |
| Real-time Badges | Existing `storeStatsProvider` + `schedulingAuthProvider` | No new data sources needed |

### Architecture Pattern
**Presentation-Only Changes with Shared State**

This feature is primarily a UI/navigation restructure. Key architectural decisions:
- **No new data layer components** - All data comes from existing providers
- **New presentation components** - Tab bar, sheets, store switcher widgets
- **Modified routing** - Tab state preservation on back navigation
- **Shared navigation state** - Single source of truth for current tab and store

---

## 2. Component Architecture

### 2.1 High-Level Component Diagram

```
┌─────────────────────────────────────────────────────────────────┐
│                     StoreNavigationScaffold                      │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │                      AppBar (Header)                        ││
│  │  ┌─────────────────────────────────────────────────────────┐││
│  │  │              StoreSwitcherHeader                        │││
│  │  │  [Logo] [Store Name + ID] [▼ Dropdown]                 │││
│  │  └─────────────────────────────────────────────────────────┘││
│  └─────────────────────────────────────────────────────────────┘│
│  ┌─────────────────────────────────────────────────────────────┐│
│  │                      Body (Content)                         ││
│  │  ┌─────────────────────────────────────────────────────────┐││
│  │  │              HomeTabContent                             │││
│  │  │  (Existing store_detail_screen metrics content)         │││
│  │  └─────────────────────────────────────────────────────────┘││
│  └─────────────────────────────────────────────────────────────┘│
│  ┌─────────────────────────────────────────────────────────────┐│
│  │                   BottomNavigationBar                       ││
│  │  [Home] [Buys●7] [Sales] [Ops] [Schedule●3]                ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼ (on category tap)
┌─────────────────────────────────────────────────────────────────┐
│                    CategoryBottomSheet                          │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  [Handle] [Category Icon] [Title] [Subtitle]                ││
│  │  ┌─────────────────────────────────────────────────────────┐││
│  │  │ NavigationSheetItem                                     │││
│  │  │ [Icon] [Title] [Description] [Badge?] [Chevron]         │││
│  │  └─────────────────────────────────────────────────────────┘││
│  │  ┌─────────────────────────────────────────────────────────┐││
│  │  │ NavigationSheetItem (repeated for each feature)         │││
│  │  └─────────────────────────────────────────────────────────┘││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
```

### 2.2 State Management Architecture

```
┌─────────────────────────────────────────────────────────────────┐
│                    Navigation State Providers                    │
├─────────────────────────────────────────────────────────────────┤
│  selectedStoreProvider (String)                                  │
│  └── Current typeNum being viewed                               │
│                                                                  │
│  selectedTabProvider (NavigationCategory)                        │
│  └── Current active tab (home, buys, sales, ops, schedule)      │
│                                                                  │
│  storeAccessHistoryProvider (Map<String, DateTime>)             │
│  └── Persisted to secure storage for sorting                    │
│                                                                  │
│  visibleTabsProvider (List<NavigationCategory>)                 │
│  └── Computed from permissionState                              │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼ (reads from)
┌─────────────────────────────────────────────────────────────────┐
│                    Existing Data Providers                       │
├─────────────────────────────────────────────────────────────────┤
│  dashboardProvider                                               │
│  └── Store list with queue counts                               │
│                                                                  │
│  storeStatsProvider(typeNum)                                    │
│  └── Real-time stats for badge data                             │
│                                                                  │
│  schedulingAuthProvider                                          │
│  └── Pending request count for Schedule badge                   │
│                                                                  │
│  permissionProvider                                              │
│  └── canAccess(AppPage) for visibility filtering                │
└─────────────────────────────────────────────────────────────────┘
```

---

## 3. Data Models

### 3.1 NavigationCategory Enum

File: `lib/core/constants/navigation_constants.dart`

```dart
/// Navigation categories for the bottom tab bar
enum NavigationCategory {
  home,
  buys,
  sales,
  ops,
  schedule,
}

/// Extension providing display properties for each category
extension NavigationCategoryX on NavigationCategory {
  String get label => switch (this) {
    NavigationCategory.home => 'Home',
    NavigationCategory.buys => 'Buys',
    NavigationCategory.sales => 'Sales',
    NavigationCategory.ops => 'Ops',
    NavigationCategory.schedule => 'Schedule',
  };

  IconData get icon => switch (this) {
    NavigationCategory.home => Icons.home_rounded,
    NavigationCategory.buys => Icons.monetization_on_rounded,
    NavigationCategory.sales => Icons.trending_up_rounded,
    NavigationCategory.ops => Icons.settings_rounded,
    NavigationCategory.schedule => Icons.calendar_today_rounded,
  };

  Color getColor(BuildContext context) => switch (this) {
    NavigationCategory.home => AppColors.primary,
    NavigationCategory.buys => AppColors.info,
    NavigationCategory.sales => AppColors.chart1,
    NavigationCategory.ops => AppColors.warning,
    NavigationCategory.schedule => AppColors.secondary,
  };

  /// Returns the features available in this category
  List<NavigationFeature> get features => switch (this) {
    NavigationCategory.home => [], // Home displays inline content
    NavigationCategory.buys => [
      NavigationFeature.buyQueue,
      NavigationFeature.completed,
      NavigationFeature.buyerStats,
    ],
    NavigationCategory.sales => [
      NavigationFeature.storeMetrics,
      NavigationFeature.closeReports,
    ],
    NavigationCategory.ops => [
      NavigationFeature.todaysTasks,
      NavigationFeature.shiftNotes,
      NavigationFeature.backstock,
    ],
    NavigationCategory.schedule => [
      NavigationFeature.timeOffRequests,
      NavigationFeature.scheduleView,
      NavigationFeature.laborCosts,
    ],
  };
}
```

### 3.2 NavigationFeature Enum

File: `lib/core/constants/navigation_constants.dart` (continued)

```dart
/// Individual features within navigation categories
enum NavigationFeature {
  buyQueue,
  completed,
  buyerStats,
  storeMetrics,
  closeReports,
  todaysTasks,
  shiftNotes,
  backstock,
  timeOffRequests,
  scheduleView,
  laborCosts,
}

/// Extension providing display properties and routing for each feature
extension NavigationFeatureX on NavigationFeature {
  String get title => switch (this) {
    NavigationFeature.buyQueue => 'Buy Queue',
    NavigationFeature.completed => 'Completed',
    NavigationFeature.buyerStats => 'Buyer Stats',
    NavigationFeature.storeMetrics => 'Store Metrics',
    NavigationFeature.closeReports => 'Close Reports',
    NavigationFeature.todaysTasks => "Today's Tasks",
    NavigationFeature.shiftNotes => 'Shift Notes',
    NavigationFeature.backstock => 'Backstock',
    NavigationFeature.timeOffRequests => 'Time-Off Requests',
    NavigationFeature.scheduleView => 'Schedule View',
    NavigationFeature.laborCosts => 'Labor Costs',
  };

  String get subtitle => switch (this) {
    NavigationFeature.buyQueue => 'Customers waiting for quotes',
    NavigationFeature.completed => 'Today\'s finished transactions',
    NavigationFeature.buyerStats => 'Performance metrics',
    NavigationFeature.storeMetrics => 'KPI dashboard & analytics',
    NavigationFeature.closeReports => 'Daily end-of-day summaries',
    NavigationFeature.todaysTasks => 'Daily checklist & completion',
    NavigationFeature.shiftNotes => 'Announcements & updates',
    NavigationFeature.backstock => 'Bins & inventory events',
    NavigationFeature.timeOffRequests => 'Pending approval requests',
    NavigationFeature.scheduleView => 'Weekly staff schedule',
    NavigationFeature.laborCosts => 'Weekly labor summary',
  };

  IconData get icon => switch (this) {
    NavigationFeature.buyQueue => Icons.list_alt_rounded,
    NavigationFeature.completed => Icons.check_circle_rounded,
    NavigationFeature.buyerStats => Icons.bar_chart_rounded,
    NavigationFeature.storeMetrics => Icons.insights_rounded,
    NavigationFeature.closeReports => Icons.summarize_rounded,
    NavigationFeature.todaysTasks => Icons.task_alt_rounded,
    NavigationFeature.shiftNotes => Icons.sticky_note_2_rounded,
    NavigationFeature.backstock => Icons.inventory_2_rounded,
    NavigationFeature.timeOffRequests => Icons.beach_access_rounded,
    NavigationFeature.scheduleView => Icons.calendar_month_rounded,
    NavigationFeature.laborCosts => Icons.attach_money_rounded,
  };

  /// Maps feature to AppPage for permission checking
  AppPage get requiredPage => switch (this) {
    NavigationFeature.buyQueue => AppPage.buyQueue,
    NavigationFeature.completed => AppPage.completed,
    NavigationFeature.buyerStats => AppPage.buyerStats,
    NavigationFeature.storeMetrics => AppPage.storeMetrics,
    NavigationFeature.closeReports => AppPage.closeReports,
    NavigationFeature.todaysTasks => AppPage.todayTasks,
    NavigationFeature.shiftNotes => AppPage.shiftNotes,
    NavigationFeature.backstock => AppPage.backstock,
    NavigationFeature.timeOffRequests => AppPage.schedulingRequests,
    NavigationFeature.scheduleView => AppPage.schedulingSchedule,
    NavigationFeature.laborCosts => AppPage.schedulingLabor,
  };

  /// Returns the route path segment for this feature
  String getRoutePath(String typeNum) => switch (this) {
    NavigationFeature.buyQueue => '/store/$typeNum/queue',
    NavigationFeature.completed => '/store/$typeNum/completed',
    NavigationFeature.buyerStats => '/store/$typeNum/stats',
    NavigationFeature.storeMetrics => '/store/$typeNum/metrics',
    NavigationFeature.closeReports => '/store/$typeNum/close-reports',
    NavigationFeature.todaysTasks => '/store/$typeNum/tasks/today',
    NavigationFeature.shiftNotes => '/store/$typeNum/notes',
    NavigationFeature.backstock => '/store/$typeNum/backstock',
    NavigationFeature.timeOffRequests => '/scheduling/requests',
    NavigationFeature.scheduleView => '/scheduling/schedule',
    NavigationFeature.laborCosts => '/scheduling/labor',
  };
}
```

### 3.3 Store Access History (Persisted)

No new Freezed model needed - stored as JSON string in secure storage:

```dart
// Key: 'store_access_history'
// Value: JSON map of typeNum -> ISO8601 timestamp
// Example: {"bk01": "2026-02-05T10:30:00Z", "bk02": "2026-02-04T14:22:00Z"}
```

---

## 4. Navigation State Providers

File: `lib/presentation/providers/navigation/store_navigation_provider.dart`

### 4.1 Selected Tab Provider

```dart
/// Currently selected navigation tab (session-scoped)
final selectedTabProvider = StateProvider<NavigationCategory>((ref) {
  return NavigationCategory.home;
});
```

### 4.2 Visible Tabs Provider

```dart
/// Computes which tabs should be visible based on user permissions
final visibleTabsProvider = Provider<List<NavigationCategory>>((ref) {
  final permissionState = ref.watch(permissionProvider);

  return NavigationCategory.values.where((category) {
    if (category == NavigationCategory.home) return true;

    // Show tab if user can access at least one feature in the category
    return category.features.any(
      (feature) => permissionState.canAccess(feature.requiredPage),
    );
  }).toList();
});
```

### 4.3 Visible Features Provider

```dart
/// Computes which features should be visible within a category
final visibleFeaturesProvider = Provider.family<List<NavigationFeature>, NavigationCategory>(
  (ref, category) {
    final permissionState = ref.watch(permissionProvider);

    return category.features.where(
      (feature) => permissionState.canAccess(feature.requiredPage),
    ).toList();
  },
);
```

### 4.4 Tab Badge Provider

```dart
/// Badge counts for each tab (returns null when 0 or unavailable)
final tabBadgeProvider = Provider.family<int?, NavigationCategory>((ref, category) {
  switch (category) {
    case NavigationCategory.buys:
      final stats = ref.watch(storeStatsProvider(ref.watch(selectedStoreProvider)));
      final count = stats.valueOrNull?.today.buysInQueue;
      // Return null for 0 or unavailable - PRD specifies badges only show when > 0
      return (count != null && count > 0) ? count : null;
    case NavigationCategory.schedule:
      final authState = ref.watch(schedulingAuthProvider);
      final count = authState.valueOrNull?.pendingRequestCount;
      return (count != null && count > 0) ? count : null;
    default:
      return null;
  }
});
```

### 4.5 Store Access History Provider

```dart
/// Store access history for sorting (persisted)
class StoreAccessHistoryNotifier extends Notifier<Map<String, DateTime>> {
  static const _storageKey = 'store_access_history';

  @override
  Map<String, DateTime> build() {
    // Load from storage on init
    _loadFromStorage();
    return {};
  }

  Future<void> _loadFromStorage() async {
    final storage = ref.read(secureStorageDataSourceProvider);
    final json = await storage.getString(_storageKey);
    if (json != null) {
      final map = jsonDecode(json) as Map<String, dynamic>;
      state = map.map((k, v) => MapEntry(k, DateTime.parse(v as String)));
    }
  }

  Future<void> recordAccess(String typeNum) async {
    final now = DateTime.now();
    state = {...state, typeNum: now};

    // Persist to storage
    final storage = ref.read(secureStorageDataSourceProvider);
    final json = jsonEncode(state.map((k, v) => MapEntry(k, v.toIso8601String())));
    await storage.setString(_storageKey, json);
  }

  DateTime? getLastAccess(String typeNum) => state[typeNum];
}

final storeAccessHistoryProvider = NotifierProvider<StoreAccessHistoryNotifier, Map<String, DateTime>>(
  StoreAccessHistoryNotifier.new,
);
```

### 4.6 Sorted Stores Provider

```dart
/// Stores sorted by last access time
final sortedStoresProvider = Provider<List<Store>>((ref) {
  final stores = ref.watch(dashboardProvider).valueOrNull ?? [];
  final accessHistory = ref.watch(storeAccessHistoryProvider);

  return [...stores]..sort((a, b) {
    final aAccess = accessHistory[a.typeNum];
    final bAccess = accessHistory[b.typeNum];

    // Both have access history - sort by most recent
    if (aAccess != null && bAccess != null) {
      return bAccess.compareTo(aAccess);
    }
    // Only one has history - prioritize the one with history
    if (aAccess != null) return -1;
    if (bAccess != null) return 1;
    // Neither has history - sort alphabetically
    return a.name.compareTo(b.name);
  });
});
```

### 4.7 Barrel Export

File: `lib/presentation/providers/navigation/navigation_providers.dart`

```dart
export 'store_navigation_provider.dart';
```

Update `lib/presentation/providers/providers.dart`:

```dart
// Navigation providers
export 'navigation/navigation_providers.dart';
```

---

## 5. Widget Specifications

### 5.1 StoreNavigationScaffold

File: `lib/presentation/widgets/navigation/store_navigation_scaffold.dart`

**Purpose**: Main scaffold wrapper for store detail screen with bottom tab bar

**Props**:
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| typeNum | String | Yes | Store identifier |
| storeName | String? | No | Store display name |
| child | Widget | Yes | Body content (HomeTabContent) |

**Behavior**:
- Renders `StoreSwitcherHeader` in app bar
- Renders `StoreNavigationTabBar` at bottom
- Handles tab selection state
- Opens category sheets on non-Home tab tap
- Preserves tab state on return from sub-screens

**Implementation Notes**:
```dart
class StoreNavigationScaffold extends ConsumerStatefulWidget {
  final String typeNum;
  final String? storeName;
  final Widget child;

  // ... constructor
}

class _StoreNavigationScaffoldState extends ConsumerState<StoreNavigationScaffold> {
  /// Tracks the currently open sheet category (null if no sheet open)
  NavigationCategory? _openSheetCategory;

  /// Timer for debounce-last-tap behavior (PRD: process last tap within 100ms window)
  Timer? _debounceTimer;
  NavigationCategory? _pendingTab;

  @override
  void dispose() {
    _debounceTimer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final selectedTab = ref.watch(selectedTabProvider);
    final visibleTabs = ref.watch(visibleTabsProvider);

    return Scaffold(
      appBar: AppBar(
        title: StoreSwitcherHeader(typeNum: widget.typeNum),
        // ... existing header actions
      ),
      body: child,
      bottomNavigationBar: StoreNavigationTabBar(
        selectedTab: selectedTab,
        visibleTabs: visibleTabs,
        onTabSelected: (tab) => _handleTabSelection(context, tab),
      ),
    );
  }

  /// Handles tab selection with debounce-last-tap behavior
  /// PRD: "Rapid tab tapping (< 100ms between taps) → Debounce, only process last tap"
  void _handleTabSelection(BuildContext context, NavigationCategory tab) {
    _pendingTab = tab;
    _debounceTimer?.cancel();
    _debounceTimer = Timer(const Duration(milliseconds: 100), () {
      _executeTabSelection(context, _pendingTab!);
    });
  }

  void _executeTabSelection(BuildContext context, NavigationCategory tab) {
    final currentTab = ref.read(selectedTabProvider);

    // PRD Rule 3: "Tapping Home tab always closes any open sheet and shows dashboard"
    if (tab == NavigationCategory.home) {
      _closeSheet();
      ref.read(selectedTabProvider.notifier).state = tab;
      return;
    }

    // PRD Rule 2: "Tapping the active category tab closes its sheet"
    if (tab == currentTab && _openSheetCategory == tab) {
      _closeSheet();
      return;
    }

    // PRD Rule 1: "Only one sheet can be open at a time"
    // Close any existing sheet before opening new one
    if (_openSheetCategory != null) {
      Navigator.of(context).pop(); // Close existing sheet
    }

    ref.read(selectedTabProvider.notifier).state = tab;
    _showCategorySheet(context, tab);
  }

  void _closeSheet() {
    if (_openSheetCategory != null) {
      Navigator.of(context).pop();
      _openSheetCategory = null;
    }
  }

  void _showCategorySheet(BuildContext context, NavigationCategory category) {
    _openSheetCategory = category;
    final sheetOpenTime = DateTime.now();

    showModalBottomSheet(
      context: context,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
      ),
      // Custom animation for PRD: 350ms with easeOutCubic
      transitionAnimationController: AnimationController(
        vsync: Navigator.of(context),
        duration: const Duration(milliseconds: 350),
      ),
      builder: (context) => CategoryBottomSheet(
        category: category,
        typeNum: widget.typeNum,
        storeName: widget.storeName,
      ),
    ).then((_) {
      // Track dismissal for analytics
      final timeOpenMs = DateTime.now().difference(sheetOpenTime).inMilliseconds;
      NavigationAnalytics.logSheetDismissed(
        category: category,
        dismissMethod: 'unknown', // Updated by sheet if known
        timeOpenMs: timeOpenMs,
      );
      _openSheetCategory = null;
    });
  }
}
```

### 5.2 StoreNavigationTabBar

File: `lib/presentation/widgets/navigation/store_navigation_tab_bar.dart`

**Purpose**: Custom bottom navigation bar with badges

**Props**:
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| selectedTab | NavigationCategory | Yes | Currently selected tab |
| visibleTabs | List<NavigationCategory> | Yes | Tabs to display |
| onTabSelected | Function(NavigationCategory) | Yes | Tab selection callback |

**Styling Specs** (per PRD):
- Tab bar height: 56dp + safe area inset
- Active tab: Category color with 12% opacity background + semibold (w600) label
- Badge: `AppColors.error` (#f43f5e) background, white text, 10sp bold, min-width 18dp, border-radius 9dp
- Touch targets: minimum 48x48dp

```dart
class StoreNavigationTabBar extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return SafeArea(
      child: Container(
        height: 56,
        decoration: BoxDecoration(
          color: AppColors.surface,
          border: Border(top: BorderSide(color: AppColors.border)),
          boxShadow: AppTheme.shadowSm,
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: visibleTabs.map((tab) {
            final isSelected = tab == selectedTab;
            final badge = ref.watch(tabBadgeProvider(tab));

            return _TabItem(
              category: tab,
              isSelected: isSelected,
              badgeCount: badge,
              onTap: () => onTabSelected(tab),
            );
          }).toList(),
        ),
      ),
    );
  }
}

class _TabItem extends StatelessWidget {
  // ... renders icon + label + optional badge
}
```

### 5.3 StoreSwitcherHeader

File: `lib/presentation/widgets/navigation/store_switcher_header.dart`

**Purpose**: Tappable header showing current store with dropdown to switch

**Props**:
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| typeNum | String | Yes | Current store identifier |

**Styling Specs** (per PRD):
- Store logo: 44x44dp, border-radius 12px
- Store name: 16sp semibold
- ID badge: Small chip with store typeNum
- Tappable area opens store selection sheet

```dart
class StoreSwitcherHeader extends ConsumerWidget {
  final String typeNum;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final stores = ref.watch(sortedStoresProvider);
    final currentStore = stores.firstWhereOrNull((s) => s.typeNum == typeNum);

    return GestureDetector(
      onTap: () => _showStoreSwitcher(context, ref),
      child: Row(
        children: [
          StoreLogo(typeNum: typeNum, size: 44),
          const SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(currentStore?.name ?? 'Unknown Store', style: ...),
                Row(
                  children: [
                    _TypeNumBadge(typeNum),
                    const Icon(Icons.keyboard_arrow_down),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  void _showStoreSwitcher(BuildContext context, WidgetRef ref) {
    showModalBottomSheet(
      context: context,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
      ),
      builder: (context) => StoreSwitcherSheet(currentTypeNum: typeNum),
    );
  }
}
```

### 5.4 StoreSwitcherSheet

File: `lib/presentation/widgets/navigation/store_switcher_sheet.dart`

**Purpose**: Bottom sheet for selecting a different store

**Props**:
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| currentTypeNum | String | Yes | Currently selected store |

**Behavior**:
- Shows all accessible stores sorted by last access
- Current store has primary color border + checkmark
- Each store shows logo, name, ID, queue count
- Selection updates `selectedStoreProvider`, records access, refreshes stats

```dart
class StoreSwitcherSheet extends ConsumerStatefulWidget {
  final String currentTypeNum;

  // ... constructor

  @override
  ConsumerState<StoreSwitcherSheet> createState() => _StoreSwitcherSheetState();
}

class _StoreSwitcherSheetState extends ConsumerState<StoreSwitcherSheet> {
  bool _isLoading = false;

  @override
  Widget build(BuildContext context) {
    final stores = ref.watch(sortedStoresProvider);

    return DraggableScrollableSheet(
      initialChildSize: 0.5,
      minChildSize: 0.3,
      maxChildSize: 0.8,
      expand: false,
      builder: (context, scrollController) => Column(
        children: [
          const _SheetHandle(),
          const _SheetHeader(title: 'Switch Store'),
          if (_isLoading)
            const LinearProgressIndicator(),
          Expanded(
            child: ListView.builder(
              controller: scrollController,
              itemCount: stores.length,
              itemBuilder: (context, index) {
                final store = stores[index];
                final isSelected = store.typeNum == widget.currentTypeNum;

                return _StoreOption(
                  store: store,
                  isSelected: isSelected,
                  onTap: _isLoading ? null : () => _selectStore(context, store),
                );
              },
            ),
          ),
        ],
      ),
    );
  }

  /// PRD-compliant store selection:
  /// "Network error during store switch → Show snackbar error, keep current store selected, do not navigate"
  Future<void> _selectStore(BuildContext context, Store store) async {
    if (store.typeNum == widget.currentTypeNum) {
      Navigator.pop(context); // Just close if selecting current store
      return;
    }

    setState(() => _isLoading = true);

    try {
      // Attempt to load new store data BEFORE navigating (PRD requirement)
      await ref.read(storeStatsProvider(store.typeNum).notifier).refresh();

      // Success - now safe to navigate
      // Record access
      await ref.read(storeAccessHistoryProvider.notifier).recordAccess(store.typeNum);

      // Close sheet and navigate
      if (mounted) {
        Navigator.pop(context);
        context.goToStoreDetail(store.typeNum, storeName: store.name);
      }
    } catch (e) {
      // PRD: "Show snackbar error, keep current store selected, do not navigate"
      if (mounted) {
        setState(() => _isLoading = false);
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('Failed to load store. Please try again.'),
            behavior: SnackBarBehavior.floating,
          ),
        );
        // Sheet remains open, current store remains selected
      }
    }
  }
}
```

### 5.5 CategoryBottomSheet

File: `lib/presentation/widgets/navigation/category_bottom_sheet.dart`

**Purpose**: Sheet showing features within a category

**Props**:
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| category | NavigationCategory | Yes | Category to display |
| typeNum | String | Yes | Current store identifier |
| storeName | String? | No | Store name for navigation |

**Styling Specs** (per PRD):
- Sheet border-radius: `BorderRadius.vertical(top: Radius.circular(24))`
- Animation: 350ms with `Curves.easeOutCubic` - requires custom `AnimationController` (see below)
- Feature item: icon (40x40dp), title (16sp semibold), description (14sp regular), chevron
- Badges: Render when count > 0

**Dismissal**:
- Tap outside (default Flutter behavior)
- Swipe down > 100dp (requires custom `DraggableScrollableSheet` threshold - see implementation)
- Tap Home tab (handled by scaffold)
- Feature selection (navigates away, closes automatically)

**Animation Implementation** (PRD requires 350ms easeOutCubic):
```dart
// In _showCategorySheet():
showModalBottomSheet(
  context: context,
  transitionAnimationController: AnimationController(
    vsync: Navigator.of(context),
    duration: const Duration(milliseconds: 350),
  ),
  // ... rest of config
);
```

**Custom Swipe Threshold** (PRD: > 100dp to dismiss):
```dart
// Wrap content in NotificationListener to detect drag
NotificationListener<DraggableScrollableNotification>(
  onNotification: (notification) {
    // Track drag distance; close if > 100dp down
    if (notification.extent < initialExtent - (100 / MediaQuery.of(context).size.height)) {
      Navigator.pop(context);
    }
    return true;
  },
  child: // ... sheet content
)
```

```dart
class CategoryBottomSheet extends ConsumerWidget {
  final NavigationCategory category;
  final String typeNum;
  final String? storeName;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final visibleFeatures = ref.watch(visibleFeaturesProvider(category));

    return SafeArea(
      child: Padding(
        padding: const EdgeInsets.fromLTRB(20, 8, 20, 24),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const _SheetHandle(),
            const SizedBox(height: 20),
            _CategoryHeader(category: category),
            const SizedBox(height: 20),
            ...visibleFeatures.map((feature) => NavigationSheetItem(
              feature: feature,
              typeNum: typeNum,
              storeName: storeName,
              badgeCount: _getBadgeCount(ref, feature),
            )),
          ],
        ),
      ),
    );
  }

  int? _getBadgeCount(WidgetRef ref, NavigationFeature feature) {
    final stats = ref.watch(storeStatsProvider(typeNum));

    return switch (feature) {
      NavigationFeature.buyQueue => stats.valueOrNull?.today.buysInQueue,
      NavigationFeature.completed => stats.valueOrNull?.today.buysCompleted,
      NavigationFeature.timeOffRequests =>
        ref.watch(schedulingAuthProvider).valueOrNull?.pendingRequestCount,
      _ => null,
    };
  }
}
```

### 5.6 NavigationSheetItem

File: `lib/presentation/widgets/navigation/navigation_sheet_item.dart`

**Purpose**: Individual feature item in a category sheet

**Props**:
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| feature | NavigationFeature | Yes | Feature to display |
| typeNum | String | Yes | Store identifier for routing |
| storeName | String? | No | Store name for query params |
| badgeCount | int? | No | Badge count to display |

**Styling Specs**:
- Icon container: 40x40dp, 12px border-radius, category color at 12% opacity
- Title: 16sp semibold, `AppColors.textPrimary`
- Subtitle: 14sp regular, `AppColors.textTertiary`
- Badge colors:
  - Buy Queue: `AppColors.warning`
  - Completed: `AppColors.success`
  - Time-Off Requests: `AppColors.error`

```dart
class NavigationSheetItem extends StatelessWidget {
  final NavigationFeature feature;
  final String typeNum;
  final String? storeName;
  final int? badgeCount;

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () => _navigate(context),
      borderRadius: BorderRadius.circular(AppTheme.radiusLg),
      child: Container(
        padding: const EdgeInsets.all(14),
        margin: const EdgeInsets.only(bottom: 6),
        decoration: BoxDecoration(
          color: AppColors.background,
          borderRadius: BorderRadius.circular(AppTheme.radiusLg),
        ),
        child: Row(
          children: [
            _FeatureIcon(feature: feature),
            const SizedBox(width: 14),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(feature.title, style: ...),
                  Text(feature.subtitle, style: ...),
                ],
              ),
            ),
            if (badgeCount != null && badgeCount! > 0)
              _Badge(count: badgeCount!, feature: feature),
            const Icon(Icons.chevron_right, color: AppColors.textTertiary),
          ],
        ),
      ),
    );
  }

  void _navigate(BuildContext context) {
    Navigator.pop(context); // Close sheet
    context.go(feature.getRoutePath(typeNum));
  }
}
```

### 5.7 Widget Barrel Export

File: `lib/presentation/widgets/navigation/navigation_widgets.dart`

```dart
export 'store_navigation_scaffold.dart';
export 'store_navigation_tab_bar.dart';
export 'store_switcher_header.dart';
export 'store_switcher_sheet.dart';
export 'category_bottom_sheet.dart';
export 'navigation_sheet_item.dart';
```

Update `lib/presentation/widgets/widgets.dart`:

```dart
// Navigation widgets
export 'navigation/navigation_widgets.dart';
```

---

## 6. Screen Modifications

### 6.1 StoreDetailScreen Refactor

File: `lib/presentation/screens/store_detail/store_detail_screen.dart`

**Changes Required**:

1. **Wrap with StoreNavigationScaffold** - Replace direct `Scaffold` with `StoreNavigationScaffold`
2. **Remove Quick Actions section** - Delete lines ~1141-1265 (the `_buildQuickActionsSection`)
3. **Move metrics content to HomeTabContent** - Extract dashboard metrics into reusable widget
4. **Preserve all existing content** - Sales/Buys cards, Live Stats, Trends, NPS, Labor sections remain

**Before (simplified)**:
```dart
class StoreDetailScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(...),
      body: SingleChildScrollView(
        child: Column(
          children: [
            _buildStatsCards(),
            _buildLiveActivity(),
            _buildTrends(),
            _buildQuickActions(), // REMOVE THIS
            _buildNPS(),
            _buildLabor(),
          ],
        ),
      ),
    );
  }
}
```

**After (simplified)**:
```dart
class StoreDetailScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return StoreNavigationScaffold(
      typeNum: widget.typeNum,
      storeName: widget.storeName,
      child: RefreshIndicator(
        onRefresh: () => ref.read(storeStatsProvider(typeNum).notifier).refresh(),
        child: SingleChildScrollView(
          child: Column(
            children: [
              _buildStatsCards(),
              _buildLiveActivity(),
              _buildTrends(),
              // Quick Actions REMOVED - navigation now via bottom tabs
              _buildNPS(),
              _buildLabor(),
            ],
          ),
        ),
      ),
    );
  }
}
```

### 6.2 Sub-Screen Navigation State Preservation

**Problem**: When returning from a sub-screen (e.g., Buy Queue), the tab bar should show the previously selected tab (Buys) with sheet closed.

**Solution**:
- `selectedTabProvider` persists across navigation
- Sheet is not persistent (closed when navigating away)
- On return, tab remains highlighted but sheet is closed

**No GoRouter changes needed** - This is handled by:
1. `selectedTabProvider` is session-scoped Riverpod state
2. Tab selection triggers sheet opening only on tap
3. Returning from sub-screen doesn't trigger sheet

---

## 7. Router Updates

File: `lib/router/app_router.dart`

### 7.1 Tab State Query Parameter (Optional Enhancement)

If we want deep-linking to specific tabs, add optional query param:

```dart
GoRoute(
  path: '/store/:typeNum',
  name: 'store-detail',
  builder: (context, state) {
    final typeNum = state.pathParameters['typeNum']!;
    final storeName = state.uri.queryParameters['storeName'];
    final tab = state.uri.queryParameters['tab']; // Optional: 'buys', 'sales', etc.

    return StoreDetailScreen(
      typeNum: typeNum,
      storeName: storeName,
      initialTab: tab != null
        ? NavigationCategory.values.byName(tab)
        : null,
    );
  },
  // ... existing child routes remain unchanged
),
```

**Note**: This is optional - existing routes continue to work without changes per PRD backward compatibility requirement.

### 7.2 Navigation Helper Extensions

Add to navigation extension methods:

```dart
extension NavigationExtensions on BuildContext {
  /// Navigate to store detail with specific tab selected
  void goToStoreWithTab(
    String typeNum, {
    String? storeName,
    NavigationCategory? tab,
  }) {
    final params = <String, String>{
      if (storeName != null) 'storeName': storeName,
      if (tab != null) 'tab': tab.name,
    };
    final uri = Uri(
      path: '/store/$typeNum',
      queryParameters: params.isNotEmpty ? params : null,
    );
    go(uri.toString());
  }
}
```

---

## 8. Analytics Events

### 8.1 Event Definitions

| Event | Properties | Trigger |
|-------|------------|---------|
| `store_detail_load` | `store_id`, `user_role`, `visible_tabs[]`, `timestamp` | StoreNavigationScaffold mounted |
| `nav_tab_tap` | `tab_name`, `store_id`, `had_badge`, `badge_count`, `timestamp` | Tab item tapped |
| `nav_sheet_open` | `category`, `feature_count`, `store_id`, `timestamp` | Category sheet opens |
| `nav_sheet_item_tap` | `category`, `feature_name`, `store_id`, `item_position`, `timestamp` | Sheet item tapped |
| `nav_sheet_dismissed` | `category`, `dismiss_method`, `time_open_ms` | Sheet closed without navigation |
| `store_switch` | `from_store_id`, `to_store_id`, `time_on_previous_ms` | Store selection in switcher |
| `nav_back_from_feature` | `feature_name`, `time_in_feature_ms`, `back_method` | Return from sub-screen |

### 8.2 Analytics Integration

```dart
class NavigationAnalytics {
  static void logTabTap({
    required NavigationCategory tab,
    required String storeId,
    int? badgeCount,
  }) {
    analytics.logEvent('nav_tab_tap', {
      'tab_name': tab.name,
      'store_id': storeId,
      'had_badge': badgeCount != null && badgeCount > 0,
      'badge_count': badgeCount ?? 0,
      'timestamp': DateTime.now().toIso8601String(),
    });
  }

  // ... similar methods for other events
}
```

---

## 9. Error Handling

### 9.1 Error Scenarios

| Scenario | PRD Behavior | User Feedback |
|----------|--------------|---------------|
| Store switch fails to load data | **Keep current store selected, do NOT navigate** | Snackbar: "Failed to load store. Please try again." |
| Permission provider error | Show Home tab only | Silent degradation - features hidden |
| Badge data unavailable | Show no badge | Silent - badge simply not displayed |
| Store list fails to load | Show error in switcher | "Unable to load stores. Pull to refresh." |
| Navigation to protected route fails | Redirect to dashboard | Snackbar: "You don't have access to this feature." |
| Store removed from access mid-session | Navigate to dashboard | Snackbar: "Store access has changed" |

### 9.2 Error Recovery Patterns

**Store Switch (PRD-compliant)**:
```dart
// In StoreSwitcherSheet - see Section 5.4 for full implementation
Future<void> _selectStore(BuildContext context, Store store) async {
  setState(() => _isLoading = true);

  try {
    // PRD: Validate new store data BEFORE navigating
    await ref.read(storeStatsProvider(store.typeNum).notifier).refresh();

    // Success - now safe to record access and navigate
    await ref.read(storeAccessHistoryProvider.notifier).recordAccess(store.typeNum);
    Navigator.pop(context);
    context.goToStoreDetail(store.typeNum, storeName: store.name);
  } catch (e) {
    // PRD: "Show snackbar error, keep current store selected, do not navigate"
    setState(() => _isLoading = false);
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Failed to load store. Please try again.')),
    );
    // Sheet remains open, user can retry or cancel
  }
}
```

**Store Removal Detection**:
```dart
// In StoreDetailScreen initState or build
ref.listen(dashboardProvider, (previous, next) {
  final stores = next.valueOrNull ?? [];
  if (!stores.any((s) => s.typeNum == typeNum)) {
    context.go('/');
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Store access has changed')),
    );
  }
});
```

---

## 10. File Structure Summary

```
lib/
├── core/
│   └── constants/
│       └── navigation_constants.dart          # NEW: NavigationCategory, NavigationFeature enums
├── presentation/
│   ├── providers/
│   │   ├── navigation/
│   │   │   ├── store_navigation_provider.dart # NEW: Tab state, store history providers
│   │   │   └── navigation_providers.dart      # NEW: Barrel export
│   │   └── providers.dart                     # MODIFIED: Add navigation export
│   ├── screens/
│   │   └── store_detail/
│   │       └── store_detail_screen.dart       # MODIFIED: Wrap with scaffold, remove Quick Actions
│   └── widgets/
│       ├── navigation/
│       │   ├── store_navigation_scaffold.dart # NEW
│       │   ├── store_navigation_tab_bar.dart  # NEW
│       │   ├── store_switcher_header.dart     # NEW
│       │   ├── store_switcher_sheet.dart      # NEW
│       │   ├── category_bottom_sheet.dart     # NEW
│       │   ├── navigation_sheet_item.dart     # NEW
│       │   └── navigation_widgets.dart        # NEW: Barrel export
│       └── widgets.dart                       # MODIFIED: Add navigation export
└── router/
    └── app_router.dart                        # MODIFIED: Optional tab query param, helper methods

test/
├── presentation/
│   ├── providers/
│   │   └── navigation/
│   │       └── store_navigation_provider_test.dart    # NEW
│   └── widgets/
│       └── navigation/
│           ├── store_navigation_scaffold_test.dart    # NEW
│           ├── store_navigation_tab_bar_test.dart     # NEW
│           ├── store_switcher_sheet_test.dart         # NEW
│           └── category_bottom_sheet_test.dart        # NEW
└── integration/
    └── store_navigation_flow_test.dart               # NEW
```

---

## 11. Testing Strategy

### 11.1 Unit Tests

| Test File | Coverage |
|-----------|----------|
| `navigation_constants_test.dart` | Enum properties, route generation |
| `store_navigation_provider_test.dart` | Tab selection, visibility computation, store history |

### 11.2 Widget Tests

| Test File | Coverage |
|-----------|----------|
| `store_navigation_tab_bar_test.dart` | Tab rendering, badge display, selection callbacks |
| `store_switcher_sheet_test.dart` | Store list rendering, selection, sorting |
| `category_bottom_sheet_test.dart` | Feature list, permission filtering, navigation |
| `navigation_sheet_item_test.dart` | Feature display, badge styling, tap handling |

### 11.3 Integration Tests

| Test File | Coverage |
|-----------|----------|
| `store_navigation_flow_test.dart` | Full navigation flow: tab tap → sheet → feature → back |
| `store_switching_flow_test.dart` | Multi-store: switch → data refresh → tab preservation |
| `permission_visibility_test.dart` | Different roles see correct tabs/features |

### 11.4 Test Scenarios

**Tab Navigation**:
1. Tap Buys tab → Sheet opens with features
2. Tap sheet item → Navigates to screen, sheet closes
3. Press back → Returns to store detail with Buys tab selected
4. Tap Home → Sheet closes (if open), Home tab selected
5. Tap active tab → Sheet toggles (closes if open)

**Permission Filtering**:
1. Employee sees: Home, Buys (Queue, Completed), Ops (Tasks only)
2. Shift Lead sees: All above + Buys (Stats), Sales (both), Ops (Notes)
3. Manager sees: All tabs and features
4. Owner sees: All tabs and features

**Store Switching**:
1. Tap store header → Switcher opens
2. Select different store → Navigates, refreshes, preserves tab
3. Current store shows checkmark, primary border
4. Stores sorted by last access time
5. Switch fails → Shows error, doesn't navigate

**Edge Cases**:
1. Rapid tab tapping (debounce < 100ms)
2. Store removed from access mid-session
3. Empty store list (single-store user)
4. Network error during stats refresh

---

## 12. Quality Requirements

### 12.1 Performance

| Requirement | Target | Measurement |
|-------------|--------|-------------|
| Tab bar render time | < 16ms | Frame budget compliance |
| Sheet animation | 350ms at 60fps | No dropped frames |
| Store switcher open | < 100ms | Time from tap to first frame |
| Tab state preservation | Instant | No perceptible delay on back |

### 12.2 Accessibility

- All tabs have semantic labels (e.g., "Buys tab, 7 items in queue")
- Badge counts announced by screen readers
- Touch targets minimum 48x48dp
- Color is not only indicator (icons accompany colors)
- Focus traversal follows logical order

### 12.3 Platform Compliance

| Platform | Requirement | Implementation |
|----------|-------------|----------------|
| iOS 13+ | Safe area insets | `SafeArea` widget wrapping tab bar |
| Android 10+ | Edge-to-edge | System UI overlay handling |
| Both | Back gesture | Tab state preserved via provider |

---

## 13. Architecture Decision Records

### ADR-1: Session-Only Tab State

**Decision**: Store selected tab in session-scoped `StateProvider`, not persistent storage.

**Rationale**:
- PRD specifies tab selection doesn't persist across restarts
- Simpler implementation with less overhead
- Users expect fresh state on app restart
- Store history (different concern) IS persisted for sorting

**Trade-offs**:
- User must reselect preferred tab after restart
- No persistence of "favorite" category

**Status**: Approved

### ADR-2: Reuse Existing Permission System

**Decision**: Filter tabs/features using existing `permissionState.canAccess(AppPage)` pattern.

**Rationale**:
- Permission system already handles all feature pages
- AppPage enum already exists for all navigable screens
- Consistent with existing permission checks in router
- No new permission infrastructure needed

**Trade-offs**:
- Must add missing AppPage entries if not present
- Coupled to existing permission system

**Status**: Approved

### ADR-3: Debounce-Last-Tap Pattern

**Decision**: Use timer-based debounce that processes the LAST tap within 100ms window.

**Rationale**:
- PRD specifies: "Rapid tab tapping (< 100ms between taps) → Debounce, only process last tap"
- Simple ignore-during-window approach would process FIRST tap (wrong per PRD)
- Timer coalesces rapid taps and executes the final intended destination

**Implementation**:
```dart
Timer? _debounceTimer;
NavigationCategory? _pendingTab;

void _handleTabSelection(BuildContext context, NavigationCategory tab) {
  _pendingTab = tab;  // Always record the latest tap
  _debounceTimer?.cancel();  // Cancel any pending execution
  _debounceTimer = Timer(const Duration(milliseconds: 100), () {
    _executeTabSelection(context, _pendingTab!);  // Execute LAST tap
  });
}
```

**Trade-offs**:
- 100ms delay on all taps (acceptable per UX guidelines)
- Requires timer cleanup in dispose()

**Status**: Approved

### ADR-4: Store Access History in Secure Storage

**Decision**: Persist store access timestamps in `flutter_secure_storage`.

**Rationale**:
- PRD requires persistence across restarts
- Consistent with other app persistence (tokens, user info)
- Simple key-value storage sufficient
- No migration path needed (additive feature)

**Trade-offs**:
- Secure storage is overkill for non-sensitive data
- Could use shared_preferences, but consistency wins

**Status**: Approved

---

## 14. Implementation Order

### Phase 1: Foundation (Day 1)
1. Create `navigation_constants.dart` with enums
2. Create `store_navigation_provider.dart` with all providers
3. Add unit tests for providers
4. Run `dart run build_runner build`

### Phase 2: Core Widgets (Day 2)
1. Create `StoreNavigationTabBar` widget
2. Create `CategoryBottomSheet` and `NavigationSheetItem`
3. Add widget tests for tab bar and sheet

### Phase 3: Store Switcher (Day 3)
1. Create `StoreSwitcherHeader` widget
2. Create `StoreSwitcherSheet` widget
3. Implement store access history persistence
4. Add widget tests for switcher

### Phase 4: Integration (Day 4)
1. Create `StoreNavigationScaffold` wrapper
2. Refactor `StoreDetailScreen` to use scaffold
3. Remove Quick Actions section
4. Add integration tests

### Phase 5: Polish (Day 5)
1. Add analytics events
2. Verify permission filtering
3. Test on iOS and Android devices
4. Fix any edge cases
5. Performance profiling

---

## 15. Out of Scope / Future Work

Per PRD "Could Have" and "Won't Have" sections, the following are explicitly **NOT included** in this implementation:

### Deferred to Future Phases

| Feature | PRD Reference | Rationale |
|---------|---------------|-----------|
| Category Quick Actions (long-press) | Could Have #8 | Adds complexity; core navigation must ship first |
| Customizable Tab Order | Could Have #9 | Requires settings UI; nice-to-have after adoption |
| Feature "last accessed" timestamps | Open Question | UX value unclear; defer to user feedback |
| "What's New" indicator | Open Question | Requires feature flag infrastructure |

### Won't Have (This Phase)

| Feature | PRD Reference |
|---------|---------------|
| Customizable sheets | Won't Have |
| Deep linking from push notifications | Won't Have (existing routes work) |
| Horizontal swipe between categories | Won't Have |
| Animated tab transitions | Won't Have |
| Search within navigation | Won't Have |
| Favorites/pinned features | Won't Have |

---

## 16. Badge Data Source Latency Confirmation

PRD requires specific update latencies for badge data. This section confirms existing providers meet requirements:

### Buys Tab Badge (Queue Count)

| Requirement | Implementation | Status |
|-------------|----------------|--------|
| Source | `storeStatsProvider(typeNum).today.buysInQueue` | ✅ Existing |
| Update mechanism | Ably channel `{typeNum}` event `queue:update` | ✅ Existing (see `ably_provider.dart`) |
| Latency | < 2 seconds | ✅ Confirmed via Ably subscription in StoreDetailScreen |

**Note**: The existing `storeStatsProvider` already subscribes to Ably real-time events. No new implementation needed.

### Schedule Tab Badge (Pending Requests)

| Requirement | Implementation | Status |
|-------------|----------------|--------|
| Source | `schedulingAuthProvider.pendingRequestCount` | ✅ Existing |
| Update mechanism | Polling OR push notification | ⚠️ **Needs verification** |
| Latency | < 60 seconds | ⚠️ **Implementation note below** |

**Implementation Note**: The current `schedulingAuthProvider` loads pending count on auth but doesn't poll. For PRD compliance, add one of:

```dart
// Option A: Add polling to existing provider
Timer.periodic(const Duration(seconds: 60), (_) {
  ref.read(schedulingAuthProvider.notifier).refreshPendingCount();
});

// Option B: Rely on push notification to trigger refresh
// Push handler calls: ref.invalidate(schedulingAuthProvider);
```

**Recommendation**: Option B (push notification) is preferred as it provides real-time updates when requests are submitted, rather than waiting up to 60 seconds.

---

## 17. Additional Edge Cases

Per Codex review, these edge cases are documented for test coverage:

### Device Rotation While Sheet Open

**Behavior**: Sheet closes, tab state preserved
**Implementation**: Flutter's `showModalBottomSheet` handles rotation by closing. Tab remains selected via `selectedTabProvider`.

### Store Removed Mid-Session

**Scenario**: User's access to a store is revoked while viewing it
**Detection**: On next `dashboardProvider` refresh, store disappears from list
**Behavior**:
- If viewing removed store: Navigate back to dashboard with snackbar "Store access has changed"
- If removed store in switcher: It simply won't appear in list

**Implementation**:
```dart
// In StoreDetailScreen or StoreNavigationScaffold:
ref.listen(dashboardProvider, (previous, next) {
  final stores = next.valueOrNull ?? [];
  final stillHasAccess = stores.any((s) => s.typeNum == typeNum);
  if (!stillHasAccess) {
    context.go('/'); // Back to dashboard
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Store access has changed')),
    );
  }
});
```

### Offline Mode

**Scenario**: Network unavailable when viewing store
**Behavior**:
- Show last cached store data (from provider state)
- Add "Offline" chip indicator in header
- Disable store switching (show "No connection" in switcher)
- Tab navigation works locally (sheets still open)

### History Loading Race Condition

**Scenario**: `StoreAccessHistoryNotifier.build()` triggers async storage load; sorting happens before load completes
**Behavior**: Initial render uses empty history (alphabetical sort); re-sorts when history loads
**Mitigation**: Either:
1. Accept brief flicker (acceptable for edge case)
2. Add loading state to `sortedStoresProvider` that waits for history

---

*SDD Version: 1.1.0*
*Created: 2026-02-05*
*Last Updated: 2026-02-05*
*Spec ID: 005-store-navigation-redesign*

## Revision History

| Version | Date | Changes |
|---------|------|---------|
| 1.0.0 | 2026-02-05 | Initial SDD |
| 1.1.0 | 2026-02-05 | Codex review: Fixed store switch error handling (validate before navigate), added sheet toggle logic, fixed debounce-last-tap pattern, added animation specs, documented out-of-scope features, confirmed badge data sources, added edge cases |
