Skip to content

Phase 3: User Story 3 — Onboarding & Identity Setup - #39

Merged
grunch merged 1 commit into
mainfrom
phase-3
Mar 26, 2026
Merged

Phase 3: User Story 3 — Onboarding & Identity Setup#39
grunch merged 1 commit into
mainfrom
phase-3

Conversation

@grunch

@grunch grunch commented Mar 26, 2026

Copy link
Copy Markdown
Member
  • T029 [US3] Implement identity API in rust/src/api/identity.rs: create_identity(), import_from_mnemonic(words, recover), import_from_nsec(nsec), get_identity(), export_encrypted_backup(passphrase), delete_identity(), set_pin(pin), enable_biometric(), unlock(pin), derive_trade_key(), get_trade_key(index), get_nym_identity(pubkey) per contracts/identity.md
  • T030 [P] [US3] Implement nym identity generation in rust/src/crypto/nym.rs: deterministic_nym(pubkey) → (pseudonym: String adjective-noun, icon_index: u8 0–36, color_hue: u16 0–359); same pubkey always yields same output; called by get_nym_identity() per FR-052/FR-053
  • T031 [US3] Implement identity provider in lib/providers/identity_provider.dart: wrap identity Rust API, expose IdentityInfo? stream via on_identity_changed(), handle create/import/delete actions
  • T032 [P] [US3] Implement welcome screen in lib/screens/onboarding/welcome_screen.dart: app logo, "Create New Identity" and "Import Existing" buttons per US3 scenario 1
  • T033 [P] [US3] Implement create identity screen in lib/screens/onboarding/create_identity_screen.dart: call create_identity(), display 12-word mnemonic grid, backup-confirmation checkbox, "I've saved my phrase" CTA per US3 scenario 2
  • T034 [P] [US3] Implement import identity screen in lib/screens/onboarding/import_identity_screen.dart: 12/24-word mnemonic word-grid input and nsec bech32 field, real-time BIP-39 validation, import CTA per US3 scenario 3
  • T035 [P] [US3] Implement PIN setup screen in lib/screens/onboarding/pin_setup_screen.dart: 4–8 digit entry with confirm field, optional biometric enable toggle, skip button per US3 scenario 4
  • T036 [US3] Implement nostr init in rust/src/api/nostr.rs (bootstrap subset): initialize(relays: None) connecting to preconfigured default relays; relay provider in lib/providers/relay_provider.dart exposes ConnectionState stream
  • T037 [US3] Wire onboarding navigation in lib/router.dart: redirect guard sends users without identity to /onboarding; after successful identity creation/import navigate to /home per US3 scenario 5
  • T038 [US3] Implement home screen shell in lib/screens/home/home_screen.dart: scaffold with AppBar (hamburger menu), empty order list placeholder, relay status chip in AppBar — functional shell before order loading is implemented

Summary by CodeRabbit

  • New Features
    • Complete onboarding flow: create new identity or import existing one via recovery phrase or nsec key
    • Recovery phrase generation and secure display with user confirmation
    • Optional PIN-based device unlock protection
    • Home screen with relay connection status indicator
    • Deterministic user avatar and pseudonym generation

- [x] T029 [US3] Implement identity API in rust/src/api/identity.rs: create_identity(), import_from_mnemonic(words, recover), import_from_nsec(nsec), get_identity(), export_encrypted_backup(passphrase), delete_identity(), set_pin(pin), enable_biometric(), unlock(pin), derive_trade_key(), get_trade_key(index), get_nym_identity(pubkey) per contracts/identity.md
- [x] T030 [P] [US3] Implement nym identity generation in rust/src/crypto/nym.rs: deterministic_nym(pubkey) → (pseudonym: String adjective-noun, icon_index: u8 0–36, color_hue: u16 0–359); same pubkey always yields same output; called by get_nym_identity() per FR-052/FR-053
- [x] T031 [US3] Implement identity provider in lib/providers/identity_provider.dart: wrap identity Rust API, expose IdentityInfo? stream via on_identity_changed(), handle create/import/delete actions
- [x] T032 [P] [US3] Implement welcome screen in lib/screens/onboarding/welcome_screen.dart: app logo, "Create New Identity" and "Import Existing" buttons per US3 scenario 1
- [x] T033 [P] [US3] Implement create identity screen in lib/screens/onboarding/create_identity_screen.dart: call create_identity(), display 12-word mnemonic grid, backup-confirmation checkbox, "I've saved my phrase" CTA per US3 scenario 2
- [x] T034 [P] [US3] Implement import identity screen in lib/screens/onboarding/import_identity_screen.dart: 12/24-word mnemonic word-grid input and nsec bech32 field, real-time BIP-39 validation, import CTA per US3 scenario 3
- [x] T035 [P] [US3] Implement PIN setup screen in lib/screens/onboarding/pin_setup_screen.dart: 4–8 digit entry with confirm field, optional biometric enable toggle, skip button per US3 scenario 4
- [x] T036 [US3] Implement nostr init in rust/src/api/nostr.rs (bootstrap subset): initialize(relays: None) connecting to preconfigured default relays; relay provider in lib/providers/relay_provider.dart exposes ConnectionState stream
- [x] T037 [US3] Wire onboarding navigation in lib/router.dart: redirect guard sends users without identity to /onboarding; after successful identity creation/import navigate to /home per US3 scenario 5
- [x] T038 [US3] Implement home screen shell in lib/screens/home/home_screen.dart: scaffold with AppBar (hamburger menu), empty order list placeholder, relay status chip in AppBar — functional shell before order loading is implemented
@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

Pull request was closed or merged during review

Walkthrough

This pull request implements a complete identity and onboarding system by adding Rust-backed identity lifecycle APIs (create, import, unlock, PIN management, trade key derivation), Nostr relay pool initialization, Flutter state providers for identity and relay status, routing with identity-aware redirects, and five new onboarding screens (welcome, create identity, import identity, PIN setup) plus a home screen displaying identity and connection status.

Changes

Cohort / File(s) Summary
Flutter App Initialization
lib/main.dart, pubspec.yaml
Converted main() to async, added platform-gated Rust initialization with SQLite database path, secure storage for master key, and conditional Nostr initialization. Added path_provider dependency.
State Management Providers
lib/providers/identity_provider.dart, lib/providers/relay_provider.dart
Replaced stubs with functional Riverpod notifiers backed by Rust APIs; identity provider manages creation/import/unlock/PIN/deletion lifecycle; relay provider polls Rust relay pool every 5 seconds and reexports types from Rust layer.
Routing & App Shell
lib/router.dart, lib/app.dart
Added RouterNotifier for identity-driven redirect logic, updated redirect guards to defer while loading and redirect anonymous users to /onboarding; added Riverpod listener to re-evaluate redirects on identity changes. Changed initial location to /onboarding, removed recovery route stub.
Onboarding Screens
lib/screens/onboarding/welcome_screen.dart, lib/screens/onboarding/create_identity_screen.dart, lib/screens/onboarding/import_identity_screen.dart, lib/screens/onboarding/pin_setup_screen.dart
Added four new screens: welcome (navigation hub), create identity (mnemonic generation with confirmation), import identity (BIP-39 or nsec input), and PIN setup (optional 4–8 digit unlock). All coordinate with identity provider and navigate to next steps on success.
Home Screen
lib/screens/home/home_screen.dart
Added shell screen displaying identity avatar/pseudonym derived from Nym identity lookup, relay connection status indicator, and placeholder orders area. Includes navigation bar and watches identity/relay state providers.
Rust Identity API
rust/src/api/identity.rs, rust/src/crypto/keys.rs, rust/src/crypto/nym.rs
Implemented identity lifecycle (create, import, unlock, PIN management, deletion), trade key derivation with persistence, deterministic Nym pseudonym/icon/hue generation from public key. Added BIP-32 seed-based key derivation functions.
Rust Nostr Bootstrap API
rust/src/api/nostr.rs, rust/src/api/runtime.rs, rust/src/api/mod.rs
Added relay pool initialization with identity private key decryption and derivation, connection state/relay info queries, and graceful disconnection. Introduced process-wide runtime singleton for storage, master key, identity pubkey, and relay pool.
Rust Infrastructure & Dependencies
rust/src/lib.rs, rust/src/crypto/mod.rs, rust/Cargo.toml, rust/src/storage/sqlite.rs
Added sha2 and argon2 crypto dependencies; introduced unexpected_cfgs lint suppression; improved SQLite migration comment handling; reexported nym module.
Rust FFI Code Generation
rust/src/frb_generated.rs
Regenerated Flutter-Rust-Bridge dispatch layer with new wire functions for identity and nostr APIs, added codec implementations for domain types (IdentityInfo, NymIdentity, RelayInfo, TradeKeyInfo, etc.) and error handling.
Build Configuration & Spec Updates
linux/CMakeLists.txt, specs/001-mostro-p2p-client/tasks.md
Added CMake compiler fallback for g++/gcc; marked onboarding and identity tasks T029–T038 as completed.

Sequence Diagram(s)

sequenceDiagram
    participant Flutter as Flutter App
    participant SecureStorage as Secure Storage
    participant RustIdentity as Rust Identity API
    participant SQLite as SQLite DB
    participant RustNostr as Rust Nostr API
    participant RelayPool as Relay Pool

    rect rgba(100, 150, 200, 0.5)
    Note over Flutter,RelayPool: App Initialization & Unlock Flow
    Flutter->>RustIdentity: initialize(db_path)
    RustIdentity->>SQLite: open_connection()
    SQLite-->>RustIdentity: connection
    RustIdentity-->>Flutter: ok

    Flutter->>SecureStorage: read_master_key()
    SecureStorage-->>Flutter: master_key_hex
    Flutter->>RustIdentity: unlock_with_master_key(master_key_hex)
    RustIdentity->>SQLite: fetch_encrypted_identity()
    SQLite-->>RustIdentity: encrypted_blob
    RustIdentity->>RustIdentity: decrypt_with_master_key()
    RustIdentity-->>Flutter: unlocked

    Flutter->>RustNostr: initialize_nostr(relay_urls)
    RustNostr->>RustIdentity: get_identity()
    RustIdentity->>SQLite: fetch_identity_pubkey()
    SQLite-->>RustIdentity: pubkey
    RustIdentity-->>RustNostr: identity_info
    RustNostr->>RustNostr: derive_identity_key()
    RustNostr->>RelayPool: create_and_start()
    RelayPool-->>RustNostr: pool_ready
    RustNostr-->>Flutter: initialized
    end
Loading
sequenceDiagram
    participant User as User
    participant CreateScreen as CreateIdentityScreen
    participant IdentityNotifier as Identity Provider
    participant RustIdentity as Rust Identity API
    participant SecureStorage as Secure Storage
    participant Router as App Router

    rect rgba(150, 200, 100, 0.5)
    Note over User,Router: Identity Creation Flow
    User->>CreateScreen: tap "Generate recovery phrase"
    CreateScreen->>IdentityNotifier: createIdentity()
    IdentityNotifier->>RustIdentity: create_identity()
    RustIdentity->>RustIdentity: generate_bip39_mnemonic()
    RustIdentity->>RustIdentity: generate_master_key()
    RustIdentity-->>IdentityNotifier: {mnemonic, master_key_hex}
    IdentityNotifier->>SecureStorage: write_master_key(master_key_hex)
    SecureStorage-->>IdentityNotifier: stored
    IdentityNotifier-->>CreateScreen: mnemonic
    CreateScreen->>CreateScreen: render_12_word_grid()

    User->>CreateScreen: confirm & tap "Continue"
    CreateScreen->>Router: goNamed(onboardingPin)
    Router-->>User: navigate to PIN setup
    end
Loading
sequenceDiagram
    participant RelayProvider as RelayProvider (Polling)
    participant Timer as Timer (5s interval)
    participant RustNostr as Rust Nostr API
    participant RelayPool as Relay Pool
    participant Dart as Dart/Flutter State

    rect rgba(200, 150, 100, 0.5)
    Note over RelayProvider,Dart: Relay Status Polling Loop
    Note over Timer: Every 5 seconds
    loop Poll
        Timer-->>RelayProvider: tick
        RelayProvider->>RustNostr: get_relay_infos()
        RustNostr->>RelayPool: relay_infos()
        RelayPool-->>RustNostr: [RelayInfo]
        RustNostr-->>RelayProvider: result
        RelayProvider->>Dart: update_state([RelayInfo])
        Dart-->>RelayProvider: state_updated
    end
    </mermaid>

## Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

## Possibly related PRs

- MostroP2P/app#17: Implements `get_nym_identity` and the `NymIdentity` type (pseudonym/icon/hue generation) that are directly consumed by the home screen and Nym header display in this PR.

## Poem

> A rabbit digs deep through identity's maze,
> With Rust-forged keys and screens in a blaze,
> From seed to PIN, the onboarding gleams,
> While relay pools swim through encrypted dreams,
> Hopping forward to home—secure and serene! 🐰✨

<!-- walkthrough_end -->


<!-- pre_merge_checks_walkthrough_start -->

<details>
<summary>🚥 Pre-merge checks | ✅ 2 | ❌ 1</summary>

### ❌ Failed checks (1 warning)

|     Check name     | Status     | Explanation                                                                           | Resolution                                                                         |
| :----------------: | :--------- | :------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------- |
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 58.73% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |

<details>
<summary>✅ Passed checks (2 passed)</summary>

|     Check name    | Status   | Explanation                                                                                                                                                                                                                                                 |
| :---------------: | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                                                                                                                 |
|    Title check    | ✅ Passed | The title clearly and concisely summarizes the primary objective of the pull request: implementing Phase 3, User Story 3 for onboarding and identity setup. It directly reflects the main changes across both Rust backend and Flutter frontend components. |

</details>

<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>

</details>

<!-- pre_merge_checks_walkthrough_end -->

<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

<details>
<summary>📝 Generate docstrings</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> Create stacked PR
- [ ] <!-- {"checkboxId": "3e1879ae-f29b-4d0d-8e06-d12b7ba33d98"} --> Commit on current branch

</details>
<details>
<summary>🧪 Generate unit tests (beta)</summary>

- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Create PR with unit tests
- [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Commit unit tests in branch `phase-3`

</details>

</details>

<!-- finishing_touch_checkbox_end -->

<!-- tips_start -->

---

Thanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=MostroP2P/app&utm_content=39)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>

<!-- tips_end -->

<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKPR1AGxJcrsNIhJIAGYuAFUAvgBlAhdgyEAUAkgAeQwBfGdaeAwiSAAySABJJQxxXHlIklxsbkgACltIMyCATgBKA0j8bAoGQIEqDAZYLm4/ALAgyEAkwkhmbQwDAEE8WHwKLiIKbEHYDtxqbEQufG4yOoYKEmo6SAAmAAZbgDYwe6CwZ+gARgBWDi+nv9bgAtdqhGwAGS4sFwuG4RwA9AiiOpYNgBBomMwEQBZfCIXAUfBWW5WBFobjcBHcbAeDwIlpuBDIBrwZjcLxsErIfCpdIUTLZdAYejwYqleQBKo1NAXfHIGyHXCQASygDWZHoiysBWQmHouFggQAYh48DQ+AAzIklTUaSAAYT82WkKEGZqUHCMUEVBJQ4vU8m1BTqWwJCMQPXJ3HgCLF7EDGgoiFaXAuVxoAH14yVA7VWgAaFDsta4TPWliZ5jkZi8+AMWoAdzWtEQRcuTCkFELxe4pfLROYmYwAQbI9EPdIZZzEvzRZIAA8+xQy2QLrJuDRaJnVQw1dVatx/IhRlQAj2lF4szO8z2pZmYxhD1ke2QVF4d/AWJUKPW55Btg8fA92fDAL0oSQSEzQk0CUTMNVkf8p2gqg4IQ2osiUBdJ0qYdZCHG8ykPdEENaHhKAUEoqDERA4wDMoNGYWgNAMH0lUgDB8MgUhyCocReVDJUIyjddN3wBFOOYJMUy4JQLWYLIkHEBg8OYYiBFIngiVobAGCyHI0B4AJsFoXkuNqWCRDESCwAwLowKLeteWzEVFzqbAAA5IHuQBkAiCJ4e31BQgIoTM0UCWpsABby/J+NoAG5IDkygFIwJT63IvgaQ0khZESw4bjkbjcMk7N6MQ1oWKgU1zQowj5F4fAJHjPhag8eABGpIlmqUZM6ITMoH26lqNFoZxcFTSBGyoSkKKaijfWVerIGDSBFz7RB9MKcqCgwS18AAfnsQkrmYSBmsM5z6szIZMFIWh/2dWh2qFdNrjjEsVwRS9KkCWV+JHKrkj5DIttCENEHTMgjgMSBdEgAB1EgPCxQJIcuM42o6iMoZHBFeTSUHsgRRtkdRzN0ZIMhRvGsjG1RdBKUgICiHwYV6AAIgdS5rkgAA5EhG22gbZA5hEOYKT7lQAUQXdLsg5lVzV5RAWLhqBuYzQJlspzH2s63W8YJ/lBSIBE3uvcqKdxmmV0mhg0DpZALag+r/0QVZG2QL5bjAZsBVmGs6wYbjf1oItd33bgwCYPb4AoOYAcYI09zSBciwlgByKR7DQKR6GYBrYDPEhFYdaBFjV+HJeXJbyvsXG6n1nGMaNkGBX0j7a7KkXrdb22Jq4H2EVuAAWP2W0DkhazSkPNjFN0aWVILxxDgRRFgIJbkgS14GR8PIB5jwwHENhIAAIQKKwJmac7HbFagvwwRypcdCuq6gHU+fsSpqgb1um7Y0NrRY2RMzaPgpr/bgfcqYYAHpNUePkvKZBRMqdgsR6aGkorvBOj9eRFhOADR2KovxsEJBlAgRAiBeCLIgNU8AagCGVnA1i/N8SEjdOoeA98ABeeCsC1DDLgYSDBoyxjsgSCg0lJqKXELwkggjkZoFkEcNh5AyKx3INZIUBAkokEtGgWkypLgeGUYgRKJjlFaSai1QBnVGo9UoLRSxsgho2N6vAtaS58SugdLyLRANoi80kadIGNgugWmTndV0WNOpEjwJQeBXBLiZA7MqIg2AMg/xFFtAqyYpqogif6EWkBdH43bqbdmHE87wCII/HRbMESrDPmgS0kSHAMF6IgRAlpaTFNzGURgPMAZd1LEDAAEt+f+sD7BGjpHYlusDaLNJIE078MDqZjTtlwSGrT9oeHoJg2AK1KTn2cHUPwzAmEUFIHwLk2BXzskGS2Ci7U/QcllCQVYBzKBFiCi446BxnYIBqFkE53AzlSKMIseZQxRBqjeUtGgzBkBgsNIEBoSh0YMKTs4QIcwKAanoFiDkv17RGG9GAQwBgTBQE1PwS0OACDEDIMoLcChWDsBGHwQQVlxBSBkPIJgvUVBqE0NoXQVKaXgCgHAVAqBMBMsIDxNlNwsRck4IfNAQsHBOFiEVYVyhVDqC0DofQFLaVGGbhSbgA8vQcwdQYCwK0Cgst4tceguqCXyHwIy26LpEAGAAAbWspAPINHF8BCzZLXPUWBloONsUFaQ+wBBvKNHqSANhIIUD7PQRFrK+BgqDXiSR+BFhhqYfAA5EbDTUB3msRsGRkBBsWIgWQgwABqjtsAkGAEUEWu19oHT0BG6oY0aDIArGdIN9UrDDV6hGrIuig3xItHzfA4hd6JPHdcWoBJritCDfOEc3Qtqs0zGuiiKT46iHSZkgUepLiHxIGAEgEge0ev4FgFmWQEQswifaRYtBMhELpLIIs6KOUaoRP0fUW1nD2XoBeq9fB6a/iFKgXd7LdGXEtF4MQwp+kSjAKqAI9Ab1pO4g+g+/zp7zWJSwDVdAwBFPRS0tKicn78C7GHQIhqqmKsXDBew1Ra4saAg7Lw0H2DMyyCQFiFLnUwotPw5AuioNKAYKYviT8eSMvWqWG4aweDonaiHAae9A1QA3eQOodkvHidoNSMzGUSmbSIBgA4lxgXRNFFgEN2MbXhsqkYCE8nfMuloFwAA1M0BEYAfhGGlgSNkX6BOXGaoLNalp9ori4DiOg8BHAGAdRzSlYArXYzmFkO1pXHXKddSqviNwvXOB9X650pBrOOl5F2dlQaasYAjdO9A9gO1DBtF0ZANVYQUXQRufAy7Sls0VUG/wE2l1pTke1HhFE63Kikx4FtCMxRTkQLNi059ML6Q0NDboJBdpcPkQ9I9gmm3qBbYtcLGJZH5gjevPLgRLiuQwzkKDwXWHAaUPQD5uA8vMGIF+g9K4/6/pDrUWdjLaggDVLqJGAgj1kV0bI7hu3AhQcWsRwMzNlEUSOWN04yYlI3EiAARXCzQJK1AVD+ECEeLBvVIL0FG0GqcFaOT1n4QAEWAo4dgiBpe3rEGsCqQaga7UJIoXSNxLsUVHA946aw0CkHQJ06Q6m2Y83oIZJnm0CQ3DmA7vgCE6hBqrP4C0ABpXK0RjekB97IINPZ0VYFjqBp+jsPBCqjy2oRPcBmyA0IBYCapTuGhxJ7yggfagaDz0exKAkOldJ6X0lPe5IO/ioU4yAq6lTDnYVI0n8iN2SMUaY2QoQKDHa4BgWkHg3tgsMpHTYSGix2QwKRyT9ChRNvkBdWvGhal2UuP9oG0ObjkGjVLKdJm9dWiQ/w2hohDcEn9yQIsAvjmJt6n8kUWb2L9DO/i7XXhkCY/j/VY9tf48SMJN/oNb7DqINBEINa0AQTMZrD1YPRTcwSwFTNlPTFbUpI0PRbTZwNTBlRzIzegEzbKczNaAZKzVhQbeYINNMLrG4UXCQJbAueYf7aYWvY0PAB7YAGgsUPQWYego9OAyATPNKS0FNSAY0atQIRYLzaPPbCgMLCLKJKLWLIIH4BLJ4ZLVLRONVRQYHd9PeIWfRPLTVcZWpXYMrCrKrexBdJxfqRPNxRxKRLZTgercrRrN1VVT1Rwb1LA/1brAwGwEgD5XoG3Y6dEdeUUeuG/enBmQyXpQYMDXsTkdgfhFUdUQqeQKnfDWqUKePJ/WgU3YMVWLNejfOFAwISTYhfwr5fAH5PgINAdRPIdfACNdA7pKpLIB3WCF9N9bxFcG4GonaPaBoqpINPmfCWoiUEbQcYoh/P0INMoU4VWew9XFaEDVrE/Z9M/KgU3ZdEgTYRIoqZdbSXSBDHeeABcFYhgB7MAdYk3QIV3PLSYgIbpJ+BEJ3SJV3IKQyD2PFegINffCoc4y4P3DYkgLbA9QYBTSAWWGgVyb40YwMDdLdPeCgcYlgb9aPMbINKtA5BggkdEZArIbTEyP6dtQYWYSoVYVsBtIteudqQQhgWQbTbwWvF2WEoiN7JsX8SdLg53SABCRybbMneAPbZAVvQkY/XAZAcIvgA9GgdsX+CgEcKeGeesHsWdKWY0QcHEIOWeBgoKVU2udUlgPmUcBg9k9QfFLPF3XKPk57cnNhSRMUwFGgFU8vNUE0y4KoeUhUdiF0l9BwDwCaIsINKUKwLIE0n6OpTk3RRaQKe/INH6GgFktXOocM64S3KY3AIseM10A3S4S4mIa4wgwkCDX0yoZAaUinNmINPvOkGAyAUIbgCdDQqiSoneICIWZNCgIkPgJ6F6cHNmKUWvNtCbbtM0BTICWCfSAAocrtHtBTCdNAb/EzVtYkhgEc3tO7TstYCNIKFdd04uKNCNASAxatB7VWClJ1eA/0xAlWZAzTUQHTTA31bA7o3ArKVzCzIg6QEg5uSU2ia6SU8NWGeGWWWudAZY6LWvQzFcSATOPPERBEIRMRBEWY6QAeTOWZKNYWOo/oosYY5gRM+KCNRRco5AUo8DMALTHTHoxM+o4Pb87GX8qwiUGwkaBYoCjWUxZo1aYTTUOgLgXokWeE+AbdJEuofAjKMhckvUcC9oOGdWJglgy4YAaIMHTg5k8qf7IC+Sn4xSvtdg2gTgmNUsA05gTU+jbUlSraf2VsIsAAbzSBbI7Hmj4AAF4d5HYAgABfHguS+GHSj0vS2gwytUwcI00QWoSyoUVeHyuSqAfy1ghyjwTgl0iK8hIUR8GK7S5ggKtgoKn+XAEMp8SKnIDKoNLSvy7K1g/SzgrMxMzSi8vgzAYSoQkQ6TcQx2WQKQmQ8gSLe6WLDyW4BLAEVQ0+dLTQl9LLXQ3LUsLgQwogYwx1ViUwAwH8iwvqFxFijxew+1BreAprQtZHdw9rTwygwNXw8og0VA3gbQ6bF9DvaxWwoIgQOoaeTceQRFKpMingUxXob5XqUpDcaQOmSIrADbEk7NLsPNSNBE/bPwZUPsJ2SYqnAFPsFsu4gFLIBHRI99SgeQH4H+cPfIjffzC0CQzKL8TIEOBG3s86bhdE9wNgUShnRVZy0xGoDJLJTHB8So3s4PKpTIE8fEYhB2MEukI/KpbDV0KDOyGGigQATAIW1yyI0GdrRpBjkMb+jv1RNzdmimwEBpNEAm06Qo0tpKAuyUx7RfDawiioMvqg0/EMAAkn4gkaAI0yBHBBjfCO9aLGBOLkAlBd5+SVY79/M2iKNX0oKBsHanbeQXbgTAyvblFaKQ7a9E7ZAXbDhkSZ04LIxRFEKKRYwUL5jxpFj6zGzLrAghciig1NE71nb9gaB513FKAI10dkCSUEkeNlB5lpTDgkjyMtarghg7rlEFbU6lF06G7M675Ry2wuDKQtpdEt8EK3w2AbcqFLgIy0YG6Ykg1eQXp46f9RB/E67sgADfV8N5M+bWiaB2iny4U9wtpeAvxqja6xA6BzYT7tEzYza1hvokA36twRs94DkzylNLzVMAY0y7z0DdMbynzI7jM3y003NPyetAtzDm71qJ7NrEkFiuAraGNnytx/1gJiFKKMCoGGUvRtL3azpo667Y6d7IBbKkoAGv6twixAH9IuGOG6B5xNy+BPKyrtKmiFQJ76iWHKI/Q076jahbLLgABHbAW9egYqgCbvWU5R1R3rR2xhjAOOx0kgbyxKQO4hdR7oDwUxrIYhBh6yJh4JHexKYR+izB2w5xHBgC/BpYmHDoyO9/cSkOVaOyIWPLJtAUG4FdHOqMfOmMZCwG4ulcYPGhvyyOmC6JvOoSAu+JuYtCjCoWOxwJHeosWRnCgojvDO8xMquK1arBjxjvXBuwkurgDdewZfbzfjSg5AwJh6lqRAOW4qXiesWiWOSRXSGIGGbSsxjwEe2QJux6ty6chgISkS+ZlqYANOlZxEoscLAkDZiR/ovQPQWoTZzdYSxEjQLfVoQi9i2vaZyiGOgxnetZ/6tyl5ygYAQp+u64Y5vPDQHghq/g5qv0VqsQiQzqygbq10LwviyAGLUee4BLIIL4EatLdlDLbQ7LPQmayAOaha8rJayrFa7GVDOrEw5wqA9lNrWIe+061hMur9Ndc9GjHeCYj4qoVQVrCbfcuyPu93MYOqt7B+tUZAxVeqCi38KQLASjO9VszCyOFIsbLfVOiJSgLZlut3PDHzWAXZqE98Rk1DdVpEwKY2+mIUMXfAS9VV0S3DCOj9M0XmGVmiKaI0LACGygKGnWZhmFs8jWSg8O1JWV0+TudHZJIrNJJ9Su/RM2m4RsfW7WeuVAccypTHJACEdIU2YPO/MyWsPuvJSNl9QN9+g0Ss8pQmDuM+lOuNs4ZaZN1FQ7XkCPXkKPeQJ18U1llEkA0BCtogWtUtlZdXelhsxl61wFVHbgfi5vDwdNh2AGRo/1jtmdNZNgPt2vMtk2ScsJQoyJ1AwA1V1Wbt02XwzsXGiNK9OoIKT7BQTs6QPsHJc19dsBFek9lwM91VvRQOrhXkULH0Pwn64zCpBDe/FZb6z5P6/XRuTE3qd/d3Kwf98DigSIXGXPfPPmhnWOdMLnYBfipGFGb8JD1uAAzWa4RMgj2BAAmuUsUj3GAAwqioaUMjsgb/XUyZNgRj4bdfcCjiLFrokhz4lJQ+CJLaTGs8QkcZw3PY5snSPSc18JBJCgI17c+/QyWsHSLwMALwKQGZzabIK8ASVda1xTlO2CXxoylcXfKkkpSUqpKDDD90wIQ9zuED4BWAhqhAuBhUjTVAihjz/TYhpB0zFBj80oYgqAYm3248FV+TxTt0WvZuUltisLrjggaOTT5GO+X8fVn/Qzs5kSoilCqL9dXLxE6+gLeL61wC3962m4Hp89rzM+UZ/YEoQr1Cxz7IY95yoPRduLklirtiwFpqwQkF0QlacFrqgwcLHquQvquFp4J4BLH4FQgwFLUa9F8azLHQnLfQ2aowxw0w4lg2XGZZb8ZdqCQ2Ml3al1FwlrNwvVDrabr8pYLjwyZV/fBEaXcaWvFZDZOBBYuI6eBMIUQyINVjkgdjiNcGWZZGDwe0OANGRuBVWvB23VSgU7HIyoWtOGqaagOFdTVAm1Me5aVN8qd541qpAFbhovKemDynp564Unvm210HIjVtU5ZwZWyI/K8QbIGQZhQTYKNYJsx5sAG7WdkzUyZULmr53kWXXAWsgoRlZaBVD9atfVxyZUD2KNTNd3PC8ZK4RdApLBQyZKBORSVLEOPOHnPgEfeJe/KuqgiYqDPC6nMoMemuyotYcZXtJT+gAWj5FRSY53+EEgEyHNuoEzITOkBhe3F1kb7YS31XtNEgMiDkPuj41Ybo8gWHd8nk3KWH1AobFURQIVboEHXANE2OfYVoxneDyo/6wAHAJWmTgzgXlkxABcAnQB5iA1rz5hqQjKfkhQ56wUNAxlp3Xi8F95TRsaodqCSAieTCLGgFQgv1xaUlVzInlXdEJMOU593lOO+ICAIy3F2iwkPMZXuH57slYxqCxW58SMP/0cgB7MSRgFQOd9rfwHwCjhQGQDXApD9K/RD5GAsoI0N8UqqXBSeGgAxApGjxEV3ckkRMgz2JxY8jsceevPVA0BTg8KdVbKAhBGAkRcob2O4oZBRBStAuBBBCK52UxXlfOt5bzveUoZIEEGvHALj00syPckuvjZVj031hUBYgu8aTBg0WTQxTup3H7pVx8Y1cc+9MdHodj9r8VQe7HLxFCQpLI95cFANHlOCH7HJGuWwFXK/RVjKhFByHWyg4CZwaAEI3lH3l3U7JihigteTQZUCVjVoHo58FRgcgdo0AFwDbW0N4KLCODcAvhRlHhiPSKZnUQLIbsqFBajcOq43SbtC39axYvgHkIIAlnuCot1CjGJQBNU27Yt8sfBIrCVhMKEszCwgtuOW1NjmxhkrsK2Od22p7cKWB1KlkdRpadZokgaJ7r4xe7ZZ980yM4PbWqHUdCOdQaAG8CCBkR/A6JNQYzTjq9IPAAQzHvWloCStXQhkXAM2DzJ+Fv0FQraPhijT2gnsO2aPOr3yZxo3QWuWmtlmZqQB6+AAcSaFaEX2RcEuB3yYSwheQiUa6o8Uwwa9SyY2ZNltHLJcNY8teOdGtUubFdEk6lEWP9loSS522UGd0t0Cz6Klg45w1bFNEniIoU6IOaDpMR8xGIn6xcPnHWQKAHDGUtnKPO5WrRz11AwAzcKeUmIj8o0WAX+lKTSoGQ7eSAf3vW1j4G0Uuj4cHKgQBGYYckYvKRMDF6CTFqw5lDKMr20CmIk+kGVAobHsCYJceorG4QAE0ugfAJyl2GeF84O+DuGoAdhfSg48egQH2JiMfSxdDI7wJgGaGrChwF4DOTCIuBuCKjkYc9L4QrjGzCs04lJACBEGwTxwuM+CKpG+CT6Zp6+ngrIL2leF89968gVpJEljg4JwxcCawD5h045BYxvIbngmOqTNQt60DVAvVxuDns92CSA9oB2yCFUI0C+d3r4M0Csxe+a9RYnD3BSQo2GiAfVnqDwAsBH4a8dUMWNqSJFtg4gGZlBhlFKkQ4YwFUDMkpZ0AqBEDa8p5zZgwMHyVDZgU5n4DIMCC7AjoZwJuCIN6AH3aCrwPaz8UrxsQF2AnmYqGwbmJ4+gGeIKSyCIu3SfimI0dCDDyoSgniiKGQDTDKAsw2kAsMN46DDBegmIEyUMG/itYQw2BPIzMGJJLBALcIYNxaojd2qkhSFhN1kIwsIKMWW4F8ERavAMhY1bIRtyxbTV8hhWTIEUMWoQAiWzcYBI+x7ajIVwD4wMOIPqHks9q13Q6ndxOrtDOhNwboULF6GqiaiUsJCUxxGFvBR4yA+tFHi169hSwQHF3vIEOCm1UQFEQyJfGvgtBURs8HgMSICD7iWeq8AHBvC3haRJAvMXkj/CP5ZcF8wPCoEQGYy0A3B7wjjq/3h4AIhsVfBUl9Uh7lkgxUGB/sWw0nQVVOK/KDF4KWgYAl4M9XtEWBFE5Be6c9d4qyMEakluk1xe0CkFEwCAFIjxCMXSPIQooqk9rB+JyQnxvonk8gLIEvDFKkUM2gI4psUTDwgj3WuaRQKCJJ7giZa5zDVrohID6TqiZnXACZTMpziMIKUvALKRfZcAxOyfQ8tNJCqGljSrUvAKEMKAUjUCM07Wr0DoBxoK674qcD/2OjVoZmubW0AfDpFeYSxqZZAtWMnQaA2uRABsYlGEpEZFwvQTcNxmAj/FaRh2CkAFRbQKYCAxVBgsukrKZg2R3+Bks4A6kTlsgOI3ivkigxsjYuUGINNAGEwiF94jYumkGjZFEzvBnY1ApXyJB0hXkzVOkgyRulzAvM90FUPID974hF6u7KmbgGlgR5sgng+mV4FErFpuZAQNfAFMXh4Bji+8UMRkg85NwVA3oosAgG5BFhUozxNAAuDkw9UiwCECoQDVOBkRJaMgPnLgSwBRTkYd6R3JoRTpeEto8U32lcBxnHSlpy8e/D5j+G4zBGq4laDQMwJeco2sDR8gZhYGvlyBqDELl+RfHcddCkcuTHBhcA3iOofAlqVLB4mDQnxW2Hrod1bggI6xZsGadnNcR1CS6UOLjtwOkHP45Bx4b8X7UKByT/xjcQCaoJVjqCwJ8wuudoOkZid9BcEkcMqEo4rh5JT4UwdUDQm5QrBvBCIdhLapjd8J8Q3qrCxiy/B4sFE5bmoSolaFJqW3HFgxOKzMA9uJQg7mUKLk7DiYECKUNUD4njQdqThQScuNu4eFaWYk8LpJMgAXjhE0kxuEGjo5QJweikxQhv3ZDxFHp6AbXiBMQ4705hCw/PoEEh6Oij+ZwwhJHhmZ95GaGUH0l/DqCIJkEtST7GRAXzrC2YggSGA9m+L8ySZNad/HgqCjpiwx/CMiD5nIQxTdEKCT7KxgwBokgo5slnkjMEYRoIpAkPaYdlOqIK+hWAcIsgHr6RB6E3ADvv9CQJmiXp44rnCujk6fSB2kEopHbnlgGRrABQPmMVKwDyKnBX8NvicLqmNkWQJi5mGQCIBYJagqUNkB7WUlcMU4aoPHvWgYX34mFuCJOInCGDKizgUobXpmEykjZTEnI4lCCNnTDSsGEI2WhoGDKhlSq+UO9qdItwoBlQ6i0se9O0WoUB2KdI8gqMNx0i/eHeS0SgSJCNg8pXZY6GDli7u5kZUimQddLZnXFnYBY0WYzNpL0lpMTYyWQojez8KHiteB6UA2Tjwo0yHNAOBFIEUrw++/Ce0JAHPLUDIGqizcfQLDk7iI5e4vAjnyPFVyuB2WW8bIDTkpyGoWQSBNKB+6EVWlbEo7hxMqE3yoE98pJmcs3w8c9xnSpwWI0bmRdAFDHNud4M1DATO5MwuBeBN7mQT+5MEpco12sBZB6O1QdjihKnlSJ0JZVAbgIQXlgtYhy8wiYkLhYAh5uW8lbmiw0LUTMWU1bbgUMYknzihLE0oexO+kkwyY6yCuflgaHPyHhr846u/IDTehJB9AZVj/OOKCCXlhct5Z3FJh4c2AXyuXh+0UhA8HB3KtjjR1rxx038iABBSQVw6oxgFuImvB5IdjTUa0CKjya0hICLAeY1g/rFLnAyMB2AlAT0coiKSV95gWkpyM/EZimjPAEUevqWi1xt8YybhAQKUGkxJSgY7zdTM2AckeECliRFRSrBSZQBYx1QhOdpI74FQW0lfYTJgPwDti6AJzfdl9OLnEcnSg7eSvX1HloI5YqWTDOVHzWTLmxcatsWgDXoVqaxVaq+UQEbWhD6WHauHmwC+mWhagRa7wW9l0QEhZA0mf1QLz4A7lhMdQfEh6C2hpADkvMyutyPuoxq/oynSkNHi2h70DG3QAxL0G7SZcSgjRD3gfj4BRT0QR6n9hCSogNQlszXRHrmMEFQcI0klQaQzgcxbF5SxCCKSvDZjrRzMdIvGT2W3W5RG2whM0HNjQx1yOUG0L9iwnAaBztlN5EOWgW3FMDDlOBKyWwLQZirwuNcoLldMBXyDNVSqsHuCpUHIA9VFuCCcWllVLJ5VxMRVeTF5Vy9bmyPAeTEG/HwTjV+HEwahJxUzyYqUAQrIaEGnOU+M/FAAAIqb4wiMDDVB1qBuCXBng9dTOomh4rMJBK4bovOJXSECJU3IibFgADsHkNIZRLW50r95eQzVEfKYkEs2VxLPvAuARAOhM8GoXVqrFwDeDH5bnfau6maEiSRV3haHJmiC1oANQis7oIkQMR0hI46AZUIlJOBYFAs/mwLcFpIChaNA4WuXlwGrYBYgtiwL3NLEzAOgAAGk1sa1JAcQOoCENLBsBLpkAMtNVVnxOEBBnAGou4mLhiwxYyZwPQOtuEag7FmAzHe/P9P2jbAD4eWXoJcT+H20cQdWhrc1ta0Oh2tnW7rau3G2TbBitW+rY1ra0daCgXWnre9KICdI85UGZLalodhwp8+m/LXNJ2haNtsNwtPpWjTbKLigckxINI1D5SCJtgvESABCEWB8xbhoQRYLcOliRBHQLWoVlHj+S5ZT6OQONvWGOQEBKit0MFCSlEL5I3tgQemPMgKgBz3Owc3ZaHOI3wNSNL5cjScso02ZeQEUBzGeJcy0aqdpSZwOdiLDRF7GI4AhHwA/SZdoxRGr4vjOZBsNcsi42QMhrA1k0PtqBdBFZlCw2aEhfmJIeRPSHbzVutKvebkLokGFdurK5atlBPCiANAsgHtR4Ei2NCYtrWFofdx9Y/LAiyrJQKcFciDBiyEO6gLAEaZcAAAercA0D3BY93+QjXGT8K8VBgVmERY/yfIQ70QDuhgE7pd3f4oxNNCHEzF0SygS8oHeHGsERw57zmIcAQa+nbQO4zoV+d/DLXfDyAvM/KQINLnPg8Aw9FtOGKwib7oo+AAelPXSXOhOI9MJ6ZqDaA1SIr5g4pKXShoyJpblZo4KhqTGfRET6dQcqhoRp87hz/OUcijbHJ6y2YIoyY2vPbtOC57ndzADwBGjH1B6J9h+zfR6tmCKAxptAPXSvIe7ETUhVKnea5ot20TGV6bRsKfN82IUHQIu/AOVpYCu7+VV3F+aJji1tDRV4XUSJQhmgIB5x/gDWTkGf3FA9IroSTj9sOLmtPi28NyhzDj1kSOY1g9bDcl5A0HIAdBjQD8EYPoif8QkWAywYQMP7FiQZPwLcF61gVfGhGwvkGgADaxBtcFZgAC66eiXUWAJ2CC4DGAMQzdJM6RMmd1SLvTwrRKyH9gNySoBoEzgMBLQRAWoDLVqCmHkII245LQabSIBmAW8DmK0FaCwV5Dqe6QMoYJoAxZSkiesNzxyB0jRsCMRYJEBxDODQG9oYfUaFH3J6X98+KfcHR3gZhTyy+hw4Ct5C7wlZiRA+sgC32dM/Mu+/DRuMmJv6SNx+jnbRtOXc7j9AugglTiPHoBcdxbQvLwvkD8HWYeiQPSQasxTQP9uh5iFC1XnESlCQBs3VkNAMMqcWEBqA8tUQq50kKGA5MG7oFUe6hVrQh7h0M/m5qqcqnWkIEDrzhg1j2TDY4gCXTgKAeuYHRKgX3ydENoNwRMitB1BSKTjS6qTjrglI58CR/pZpeMxg41rHs5UXwn6Tl6BlG1iZSE0YgAJL9YIJAQPLRQmHKcDDNkK/WDRDhTtBS2NT9X2GWyzp+S8iWoLQAgJX451WPZvgqXZyc5t65+KpBEoAgyhQap6qXEnC2C5gz4Cy5iIUGXggZUU1JJmUMsCDi799bMF2Eq2yzLRRsURbVmiVQNGSb4pkjKIog9JpRHj+KLUhlHeIJylTgq4IL7DkBc5SCzueCLlDCiLhtybSebIMBcDAztTeiFYZ6ipjOZn6H6LnOoUy4zM7iPeo3ECR7AnTCIwx+U2qZDgR8AsNkhyV6ZuK5QL2x2NmJSyA7h1NT56l4pQEtOuIjQC4LNpkdwDD1bOpfdgGiXWI3AlemtTHMhC/w9gfSNsiqVgCKhaZHTYR+4jEFPEOmNw7KZaGmkEDBiji4Rb4pmdCgIRrTeZt3C6UzBHIPcFphCPmaChZlJi/xMvtpJaKT42AtYFwBGAtzcYIpmOLMmXJgJMgX8anP6EKb0TNQpRuChxd0tIAap+K94R8KBDey2K3phkAhWw1QQcR1BGUL+OrIIOug6RukoUA6tZhaGF4CMsbFYHGQOhmlPDZkx2d5GTCPilQMI4lApleYk+n4b8OwoYIGKCQmaRKlcCwBSg2zdGTUy2nWlBoMLKVUqjPXqlS1UCDCupDfXbOC96AMFuC2MGOQmYkRnpWvNRZdZnAHMeC1AFKEUzGgTMMEbIbcW2AS774ZQE4SZz62PwpARhrmb9FN5pRzepSZfrn00uSs019+Ai1CV6D8V7eKEZE9mYYItnuztS8s6+K7ObgViNwEC0KO1gbn6MsQEcwZczIQQBUkxcgN4MgBfBSMlsvS8iYMtugsIF7QcSLwdP3GAymUe3AiNQKS1/MWELGZReqMBXIm+lnppQIFMqhp86mfS1XQy0mZQzroAlESgRVJ6jLAMB8GHsgAuVaDq8RgwcOXhJmJDLaZCDJagjoR3R2EVdu6V/A40iMjsa3IZeFyRXZLCZ4gdxEghYAwrZGCs65D1kankR56i/SNiqsYBbFCIbYGYL3HDXVhsZGMxVYBh66RCEhY4XkrdAEksUteZCKVDdi4DCBe15I/JDN7KQjIIfUyJJDjCxwmkvaQZuuJs5JqVyK+tDWAEWhgBz4YcU3P434pvmsws44ONOZbCIAmw2N8ZbGWXHDhBYVYXUw2BHW4aGdkp3K/stqNvjjlDRyjb7tzX17eDFxqMFcfKjSQ3avHfSJmuv24kxmhGME3CekBGIpG2UN0PtC4A0UymEtjG7PC4DFVL8uJEc9mfHOK2ORkAFxtpQluC2R5LckWPCaBOsMJbmNfANLb6L7Rlbz1VW2OdzMa2Wl2tvyrrdE0wBl+qJzWibdxLDWuA2ALeNbejkqQ8BkAdRk7bioS2cTO8eNKSfJzknKTYeh2/pDIhUoCiUJ4APmFHT2Y1Lr6ZMbJWdu4lI7loMPNULLn5hdAnBI27gE+Z/jDbot/0pndsPZ2NLed8OwXehtF3YpZYadCTdlENhrKqiJWy+lWlF9Kiydiu3XaruwmITE9hu53qxM9GW7/N56oXfjRZzu7q8Ww6OETvZAx7qdoxP2gNuJ5K7s9pu7nduZZ7l77drADWY0q73K7wAJIMDN5D9pLb+AI5kRTnvqWz7OttuxNijsAReFqeac6iFnMWgbLttq0/bZDsci77E94AIlRPuGHv7+dy+3/Y7uHm3YsDtOxnY/un2F759iO1ffyoPgMlWQbe0QCwf72cHWdpB/g5/uoOSSHdwvVBDUC4XfwZN8u3vf9LwPP+SVXB7Q+jyL2L7UCtB1gDotkPoHYOSh9w4Qf8P57gjgh7/cYdYBLLA1my9I6rtImlAHt/aIg/keyAhHhD0R8VDLBqOhrG1321vA0cP2n7GAYAFo5RO5R6i79mh/o8MdKO6919kqPhFLsfWrlkjpO5w/vvYDyoo6c+2YBkPpE0Nl6evNkVIAcAOA4BfdBNlaCKHAu/9tG1BHlv1gsbj6XG4+nIe73EqIjPyhE6idgOsiSN7wIk4oACBkngwVJ+k47uE2t8PdhadY+KphP8VwLKIThKXnWa/9dmuFot3s3ObTdNKuYzkLAM4s8WyxolqsfZtxNVO0kLYygaNPUtvddLLsd8c8uxWQmgdtEv4wkMA6Zmq0BwGVNfzSBJ25UJg3/iRJdX0APVuYBqFqV14eTwJUqTs/QAq9FR0mI5GilpktYG4zfVEvIExzZRp1LWInJzPl0tK6RKFz/WeYSPkKR9zMfACiEjN8A6ZwOzCq4cRff7FdqATILlgqPriyxzOxgazrqP03DxXO8VYi9OPEN+KCz0RNk2WfJgI0MwYR6p20nPj6XXzyOsy6EiXGlnigLm4wW5eDS7nfLrAy1kuJMBTgBcS50K7ZusvRXzEDlxK4hcXBD0iLw+JOLZAkAbm+uyYwoTGfUrMhCgNzZbvANRo5nRgFl0hTucrPkDiwaLa4TQNvyMD3hcSX7uyyf2c7PRu0hwnr4OVxSMEGoMGA75fOP8wrxZ+Ikbxc2VJ8NCwnqBXJFhnj1602p+p4Dfr226NCemAFRozMaSogMU1UmUYQRUKMskkzaUFJQQ7n7eZRJmEsYpgRs8l3FAwG0Y+Ygr2d/V+87RcV41ogjVFEdOBwGuz4WGLU3W72wHwr+JwUd2NhRq8PHn01rxEpDnpsjF3UGJXh1L3D8M0XsEByx2dCIlJRsVxUgP5ewO1Ksgm2xs4QWwM1dGr8ZnSbmOlEWk/LLpyCLUqYA3tCMW8UjLIC5wJpn30Vta1bNrxXWn4zVw0HAI5gdWx0kywyFQCFgAfTTL7wvCPvpiWSC1Y2J4OPHQ8/wJJdvPKze/rikReGB1ygO2zpFQXsD6L3AxlAQhz1dBIJ9EmnSsC8OiK6C5tq6vcu9NfGAKcEBCBTC0JTDaVrQvdVGm7xZ23GGkOyBTrlnalxb2LtydPjb1rgsPJNZW/4EdukCpRiQ7CzFy4RaeFMHegwX4sKl7aHDb5jQASdJBcsB9JdMEIncOc9RE9XNy2VQDlTcxBCLD0gC0I5WoMxbsepHdp5GMMLyEDambZxtvZNoMA5wGiUs9nDXqgyD6v9J88IXN0SR7D4F+REtpi3SYHBjF/zBaAPscvbvnGXYZ6MxADeNvEQL08CQ8XYx5AjsV6BzC0Syw/LYiI88qeio+wGfOEeVCjY1PhroxlUiOznrCvAtQBqV+1TaBVV1WrSNIHYCkvaBB+hgbQN3FkaaXMcrhBwPpc0aCChdxrxgEnYx363dXwkE29cStuuAj9gGMAE7SiBlKHIo5tY+ofFpHX2TZ15q8bsCP585MiJ1YZsN2HcjZYJw61fYOuH3DtwTw6k7orxyjvep9u6d/4rIRTP5ZMuynfvvS86eNATO197jdquE3kicV/9/0e01gewP6w7Yc3T2GRduESHy4f8Cw/4fyh9oEj+yw9MTvMRJ+Oj9wjRf+isXoJ3A+e8MB9m3tQ5oT4Czfe4mv3m4646/tBumxNP0H/T/B+Zhmf0P1nx4a8Mc+mbyPi3qj75+8gLL1XgJFd4oDY/x72D1oDL9ZvCIRXpPwkOT4DcaWqfteNX3T9wAM+zDEPnoM4Z19uG9fCP9oN08iHCE+nVmiY//qSG3BN5Jui17vKmcLH6JhQllcxJWPE+kKo3idZsddfuubunr4Vd68e6HHlWxxy547/grZM8/CmTVwHXVVGL+OB7xqCXj9haabU5mRIvX6DEBv7MvIMAFEZiNkQoO+RbZ9X/2La4ukteANwk4lyKh3nzHIZNcCLege9bKBetH9VqX0mzSgZgss/zvz8BbHxCW95udVzGnAP3pj985JZqn+ZmWuis2ERz53/lOWAHj2TWXctlpoNqda4OR6BFiAoG6tEAKU2qFM0ahEEBiEINBSBegGdjVAg0cK33dPUfSD04AsGwFCA+YaAAKAcQaWAfUSgX1XNYHvJ+GABF/Vz0zt14DWUCIZiL/i/AEnFcgSccQBJDzM1DTnirJs7BJ3r8olVPB1IdaUAOfUseZL3RJM4aUnrAz2aNhBwpRKQ2YDiAGjGMwH/ft3U9rSHbEFJhOXLUrJdrLWhwQ/QOnRlk+/VABXNSzIVH+0wMCvhYAYwCflBcgxGYkZ8A/YehAA3KGHy3hB2Cmz30dlamxZ0FSbb3Z1dvYLn29jxQ72ywY3evUmhzjJ33jcEKVzy5tDfbLB1caAdf0cksOUTX4p5/DgDIDl/BFR6Zt0UBj5sgyfMnicQ7RRnagaAQEmuISnOKggd/HYgOfsZDTyESgt4RQzCdtKf8gIEqg2x1e9VKcoKPoGmYt3u92gh1Ql84g6p3IAyFQlE4DPGXhwScOPXh3fsYgoWCGCEguMx3g0fOfw4COALgOnwbfPIBECG6ChFoDzbDgAYCOAJgK8EbHR71SD0g9T1mCI/QlRiE8JAZ1JVDdOFkUJzXYA3N1U/A+XT9mVe1wMBHXejwRBmPF1wEk1nHYxL89jH3V9dSkJNSN8DLCigCsCTIslzdl0XxWVA5AI8GaIVTEyWycqaNGW3UZrZqCFBMQ7eCGAXBAy39plccvnkB5TaOzAAL/WIHw9r/NGHdMLLPKwHBKwAIDoAtfacA2t90d0z5IsIN7Ht5alN4mVBBsBEFHhR4TOARAfYIIHs0pQoIBSEUhKUMRZbKYa2EZIxEdxQBGUEk1is2rbyG/x1DM40stroMc27sOQh6HNDBQ0j0mJloEUKmVxQyUOlDbgWUPlDFQoIGVCEQe4EWJz4bL2WDTfBUljh+sfEQntz1JrXnQJAVdjQ8gPBzjwBUpJsQIAdwaMNi9rBX0QoAc4RcEMVh3JpUagjwfvgEgmxaXHhCAYKwATsandkOu8/mSqGM0UOf5iYMwwtMISdLLbuyvwawqsILFhMVsJcDeCSm3cCtxSly8C2ddlF8DCyfwKo0uOLIJWDhHdB1ZCzQ90y5CXILCF5DYWXIFqCPIRKHw9FDfkMXBLHW4GsdVw+oNuBGgvOTCD4Kf4MBCOXJmwnD/Q/igltpwlYTLk1bWcPLVzQrgBXC6gyAA3C9w98IaCHfE8LWMzw3KFVgLwk1zj84WW4EGoZjCZytd5jT4M80M/H4L+DWzcSHZcYYYELdchJWLS9d9jSEK/kemAV1445/fCFO1EI7s2QixXDly1kUtc9SGCEnejwSdJIbch+csuR2F5AiATaGyFcZZtTbMwCUQiRlnLaE1rxmPRbQP5ViM7g7NANS5zAZuwtwII19DGoypc6bA8T29xAOOXFUbwlWyld8IJ5SJ9VXKoVIjniciMV9FpCg1n8a6FrFoikI+iKIjw/QZzJV15V4NmNoIj4I80uAJY1t15nHP3+DJIIEMu50I1Aw2dRJTA2rlssKvzPMa/f8KQiJIfCHFcY0CBTbMTeNxV0sg+YyEBsuIFMyPI2WJ/kXA30QYE0J6AEUiUiQ4Iq0n8wopvym49+CSVoA+USCCqReWLAGspEUC6Uw0axMbEiBxkRYA+BFuL8yEInyHd09lOZT6WKshQyayqi66HOFGwUEIQnQ9kAXyC+A6gNQCIAcozIEVQooJ4D1cfge4HAgVhTNDqjgbMRQ2tuuSaL9BCPbeFcVBpWUMjUv3QK0VRxkSIE7Qn+MGwmjakKaKTDggHyFHh5o2pCWjuEMR2igeXfyE2ipFN8ST0frHS2UhVIdt39CX0HKzWFqgQQVSowcIsE8gkYgECFYCxQgI8tKSROAGwOYYDGqic4DdG2BuDYPlD5JIMJUjRtgGK0XAU6KDHCgDhLfl8ZtgKqRTRkALsGEpZALaASjfrDKAiRUpO4iike1Ty1SkgoNG15l60YLwOIAiFAFjgqYvWSChwodynmR/nVkSXA7ZCjHaE1vRnQ8D+wvzkUjA7EcJUiescLivCJddSOeppw0GPSgVISSHUg7bbcLyBJEXewRiELZGIAhUY48JIixIKKKkhgIszR6co/SzXuDY/IZxiwPIL4AcioIjFnc0rdArHgj3Ih1xz9wCSAkFUfIp+RBCPXAKPi0DvBlgGxjQCEFCBoAaAG61MwDAMiBoATMHPgbAAoGlxUdNrWlxpYVHT5g2tLAOlgsAzMHai2o/ANBJlQe1keiJiRAI8hHgUeHs1mgL4FHgPIU7QeBkhezXs0fgezTENOOXxjZkO9bLCsBshP3hx5jkevnQwSADvglMkCMbRoiOAAujdoRQIk25AXqTAQ0BAyeqASdbKe8Xqh/LK8BqERYfy3vCzHK0kIJ9WHCzIR2HIsBadibbELqAVyHCGnByof+O8cCIcBJMcrLNCE/jS5bu2xCX4buHXtRwZQIFI9sMUhIcA1F0iRjAHPcGAdDQUByzNnJTJzadMbfu2ASUndUN1I7nO+Jm8OGK3wgSywTH26kovYr2F90E+RCt9hGHsHQw91M6EI1f3HzDvZKkHEwjA/7KnFWhUBfmmPoxEziE74MkDVCnRHeD2SxBz1H+VuBFoQrEKlSASIACBB2KAEhJeKb4gbIoIXLHgBMwDeMLMkjIaDSwXAbMHAVANTeM74apXDHfYTOE5zuZ5LMqEbFZyd/C+A88L4FvhU+PrWyxq45AFuAfIH4CLB7NHyC+AvgIsC+AggeJLaA6BQIGVYd4v0Il1fWCEghVoSa/TggLEqxO5FN4rMxXJHEjkGcSbE1xLTJz2TxNiJobMAh8SxQPxNno3cJ4AAIHgRnn0NlWaGyyT94lWCBgQKfUBuAf5KYCpwtML/SFATrUsBhdPE4mEsglQc9ViiQ7AIELCBMPLEA1CRUlCON2IIuiqRn6BSC71kAAqHoB+vcsWyw8iDdQZjz1TAFkBPYBJ2lhhFQMmKdx8X8xDgDk2dC3g4wfDyOsnNVaKOtnAghFscTZV0HahUtGAPaDOnYSKSghxNFASY5MSFLx846CjlfsACEJxFgACUpn2hsUiek6BugXoDxSKmankRN3bJx36Jv8UJKR4a7I+wnsQCWSVrgRbKE2PMjEpcFGT6ARaCmApVNcGmSspMTDmSioBZLNglkltRyA9SGZk1x8AKVS2TZgHZOkxTIQvkBNctRFNqA/ELTBcppcJgHRMJVbLAlS1kkgGlhco7IVlTYopK0wI1UzQgYAXKfROT5IpFUUFj8qLAkVT5gMFNogjkrhCkB8YWx0/oCA+TGfUEmPkies7kziEeSOAZ5K7JDE+ly/kg0W1KNTNkkzHKcszSp2fwEnPLGTidjBJ3uSw0iNK3J/uDVESIzRXlOes7yJhCylNbBHE1iqbPsK29Bw1gU50z9MVRzimybuJVdwg0RCTiX5aIPodIXXVxRU84guKLibAEuNCAy4iuKria4vbSSB64xuObii4tuI7jxkLgHgBnAyAH4owAQeLHgR4seIniJXKeI8gZ4ueLEM55LCQs0iVIOJAiQ45JNniEsZ0Jc13gmiTT9rdeagQic/fWFTiotDCM910DbCMOMFgtLhmZqAchDeEzjMwBAAZDNSUbBIoYK1v0twG6GsMUwAI2ZoVYj5E71L/dVFkx51MTBzEkadiBehctKvFuRFWMAlQ0KnWJyqcg0QnltBwNGZgKgCtEH1qAk49aH1ADpRIwogdnTd14456EzGxc/TEHQM9VOb/SrTewvZU8DdYpOWHDGjNRAUxg4uyMAMk/N4MmdH02CNci7XeON+Cc/C91WREAIoLNIP093QzivdQKJ9dpcM9CFA9/b01qRlZD0XOIk4YtEiBdMkoPyDqnWkwT0VRHeg1RLieETbMYwU4APp/9briZw8ya4Hn1qpVKHx1OeeL12S8g8aEQBCE2AFqAOYCAE8NgGGgU1EhgbYDVBAPGODRAMAEVicph5GCX58gxIemOQgyeEVqBM4eKEzghWXLLVAThfZ0uBquPUHmRijF1h8Q9LNkDXpKIKElRDtiavgZxEAsAG/xLgIQG/VkAJrIxj9ZV0BA1BYYo0rw2QWpWVStoek0U9FFH9zqytQh63XhUYX/makgY04lszuMI7Gx5Tk4dhww2YdEMzQ8MD1TBI3cHTI8AFwBJx09EIXIAJBmAEzQ35h5fXiwIJYm6j7oWRHonezVVXuJkzXAyo3JdYXI/T1jT9UcKH0edMIUsB55M9LuCIWB4Ns07IseOUJ70pTPpUVMplWPkfgnPVoh7ge4DCtawMtCLdbgaOG0w94EoGQp/AHxUYhosQvy/TdjTZzEkDAXaFrxicz0LJzaQxvEIBuAanJjh2odgAZy6EVWCYgE9RnOQAAAKjlzRhBPx8hRhBUIVyRjZ9Gq4RcCYnktvFG4ExwZDSAA5926cwOfj6AA3IXAOfL7WQBarCUiJBNgXJTBRfAEkUmBagcIAohASeQBCBgYQdTyAsKCUBDsoEMfxakxRepCFFxY4URZxOU9iHqgEQZ3iCgCoke3DcZoD411ByYqVRA8sGKpG+lpFd/H40TuF2C4lhEDKnJic81NSTh+EzGRs5UCZzkbgPYaHjqAGcAFGylk4BhAmFn0fZ1GYWoCszuNvJRHLw0yXdJKhyDlal0KiDY4ggMA+YJID5hpYWTKeD15ciTIkccpyOUyXIuHTUys/alFpRCCXAkZRDEZlFQMMMkoGSRtUMEPkADUTQioBjUcVDNQpUbfPVR1AMqDizH0zkJRxlQc1GlQIAUKxIAvgNAH8gfgMaA8ggC24AEBLQcCIlCgge4BHiPIZoGRMfgKmB+AfgMiWaBHNL4GeoP8+/KYxH8sUGfz6VTkPpRzUbfOuoqwSgFIAbobxTiy38yVGpRbKICg5gkAWwHPhp8OgD8ROUEoE48HcWgA5guATLQCACwOgqQAkgXjDsEyAbgsVi+CugtMgGAEI2FlnKa4k1xKACQjjoxC2gt8oOYOX1jB6/aSBULyqOGA5gCAfYA8BmCf0MQAxCxJN0L2DQZJHB08WAFlxpCjkVMKuAdIV8pPKfgrULPYggABDAI7Qq4BVC3ynYMDCx2GMKcksQtHg3C/wo5grCg1VRA7CmQrYixC+zXKpXC8qnUKc/H70TdkwHQv8KAizdCCLTvRwsgAwiiwsiL8imwtiKHCsQoeAki8Ir0KPC8SG8jMi3wuKLAiowvyKxCoIBqK5KEopMKyi4CDiKCixIpcLOi1It0iObEWB8KWGZotyLWikwsqLzC7Iu6Kck3ovsKwcAooRZqilIsdctMiMEcyG/Aor8K1ClouCKoGMws6K9CqIuWL+isQucK5KZIvcLE42pwzTXCCYoOKuio4raKuAZoFuAziywtKKYivooqKnCpIqAo7i9g0NQbAUVHUAEYIkEboKAdwHL5S4Hgo8oL8Ogs15aQHyWnxbAMQt4KUSvQsyBaAJfzsLiqRACdB4UMQvWlwijmHxKl/eEq8BSSvcHJKtgXEvYNqS7YELDsUWx3pK1QbEuRLKSl6CJRdQBwGkAVKMQodQ+Sz3C5LK7AopkNyqV4r0LhWMtVFL2Si4BxRuMLko5gfijmGylGS9KWKKYNTAH4RRSrkvsBFFRV3hh1UkgAhLr8segQB5qDThxptOIzNQBmHZiA1Lii+KVFLwmLUyIA3ShYrWAiFCQi5LFSrgCpLpAFUtsdysIYtlLiihUsFjRS2ks6YySzUu1K1pJks1L9Sue15B4y1AiPVXZRLxTEAlXkD0hD+SUBaF63ZT1/A35YQDGiHOMd1Mx5kJRl7QCQZdN7zAeHIGdzLJDorrIQxT3LiA7iHPMvZ64W+VtQhoikKS99EVBXfcydU6jNwiQZojSAsEKnAVZ3/egF6EKwW0ELKSwcgG5ANAX0oiKPSkMq9L9IPcvcLj6OOCVlES8QuZK1C/0pRBAy7xWDKAi4NUjLbizorlKwSh8rjKQy8opaU/ELsGuITyropTKry9MqXBTETMowAlSgEpaUX2AslQAfgDyA0B7NIIAABSWPmHpN+BwAsTSDEoCkVGynRhH41aOvhulB42PXuAUK3cs1KfMSojwAn4UUuhL9/KQv6L7U8U3yLZgJADfd2MZAj7FxAHpHkBbOOQtNxCKzXgORKK90s0JPS5wG9LAKvQtvKbGDwCDKvylkugr9IUwuBK4YTcLoLOKAqhsBlS38AjKQyhgGaB/IW4DQAPIBgCCAEWDaK+BaAUnMtB7gS0CeAfgIIFoBFuWCCeBOkBUI8haABgBSEBAX4HuA0AJ4FaQfYNAGaBLQFIRIB7gJ4AEBaAZ0IEB7gL4r3KOYbStsAEy0UoEAggAIjCr/INABIB540AtuAHYUeFFQfgWCCCBLK2gA8hR4YJNoBkKsnIT9khZ4H0QPIeApKrLQYyvHingRzSeAD0hgC+BysTymMAZUZbxIKzDcgrmU+I+gEIKRq/fMtYjwAqDM9rgJaugoP82yhSrPcEsLOTFgQITwLaAVguOS/EScW4L7gIas/yoAOauatFq8simrqCoAA=== -->

<!-- internal state end -->
Loading

@grunch
grunch merged commit b11a82f into main Mar 26, 2026
1 check was pending
@grunch
grunch deleted the phase-3 branch March 26, 2026 15:25
@grunch
grunch restored the phase-3 branch March 26, 2026 15:25
@grunch
grunch deleted the phase-3 branch March 26, 2026 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant