Scaffold no-access-screen-widget structure
epic-no-access-screen-ui-task-001 — Create the initial StatelessWidget/StatefulWidget scaffold for the no-access-screen-widget as a full-screen terminal route widget. Set up the file structure, class definition, and basic build method returning a Scaffold with a safe area wrapper. Register the widget route in the router configuration.
Acceptance Criteria
Technical Requirements
Implementation Notes
Start with `StatelessWidget` and convert to `ConsumerWidget` only when reactive state (BLoC or Riverpod provider) is needed in a later task—do not pre-optimise. Use the project's existing `AppScaffold` or `PageHeader` wrapper if one exists (check `lib/core/widgets/`) to maintain nav-bar suppression and safe-area handling consistently across screens. In the router, set `fullscreenDialog: false` and do not provide a `parentNavigatorKey` that includes the bottom nav shell route—this ensures the bottom navigation bar is not rendered on this terminal screen. Add a `// TODO(epic-no-access-screen-ui): replace placeholder with full content` comment in the build method body so subsequent task assignees know exactly where to add their work.
Follow the role-based access control pattern already in place for the existing no-access screen referenced in the app architecture section.
Testing Requirements
Widget test in `test/features/no_access/presentation/no_access_screen_test.dart`. Pump the widget inside a `MaterialApp` (or `ProviderScope` + `MaterialApp` for Riverpod). Assert: (1) `find.byType(Scaffold)` finds exactly one widget; (2) `find.byType(SafeArea)` finds exactly one widget; (3) no `FlutterError` is thrown during pump. Add a router test asserting that navigating to the `/no-access` path resolves to `NoAccessScreen`.
These tests should remain green throughout the UI epic as subsequent tasks add content to the scaffold.
Flutter's live region (SemanticsProperties.liveRegion) announcement may be delayed or swallowed by the OS accessibility engine if the Semantics tree is not fully built when the screen mounts, causing screen-reader users to miss the denial announcement.
Mitigation & Contingency
Mitigation: Trigger the live region announcement from a post-frame callback (WidgetsBinding.addPostFrameCallback) to ensure the Semantics tree is committed before the announcement fires. Test on both VoiceOver (iOS) and TalkBack (Android) physical devices.
Contingency: If live region timing is unreliable, fall back to using SemanticsService.announce() directly in the initState post-frame callback, which provides more deterministic announcement timing.
The organisation logo may fail to load (network error, missing asset) leaving a broken image in an otherwise functional screen, degrading the professional appearance and potentially confusing users.
Mitigation & Contingency
Mitigation: Wrap the logo widget in an error builder that renders a styled fallback (organisation name text or a generic icon) when the logo asset or network image fails to load.
Contingency: If logo loading is persistently unreliable across organisations, remove the logo from the no-access screen entirely in favour of a text-only header using the organisation's display name from the design token system.